[{"content":"事情起因 今天May 24是我院计算机通信网络的实验考试，考试要求设计在Cisco仿真软件中配置单臂路由，DHCP，静态IP，NAT，ACL；同组的一个同学做的比我快，但是最后卡在了NAT配置上，导致最后以我的实验成果作为最好的标准完成了考核目标 该同学具有刨根问底的精神，哪怕考试后依然虚心向我请教学习和思考，我被我校同学的探究精神打动，耐心回复与解答，并且打算成文一篇，把我们今天遇到的问题公布于互联网上一起讨论：\nA Quick glance at 网络拓扑 我们的网络拓扑大致分为左、中、右三块：\n左侧（内网/私网）： 由 Router0 作为边界网关，下挂交换机和路由器（Router2, Router3），连接 PC0-PC3。采用单臂路由（Router-on-a-stick）划分了多个 VLAN，IP 网段规划为 192.168.14.0/24 等私网地址。 中间（骨干网/公网）： 由 Router1 充当 ISP（运营商）路由器。它与 Router0 之间是公网网段 200.14.1.0。 右侧（外部网络）： 由 Router4、Router5、Router6 组成的外部目标网络，连接 PC4-PC7。 Router0 路由表： Router1 路由表： 问题聚焦 假设配置好所有VLAN，单臂路由，DHCP等等之后，思考几个问题：\n1. 为什么本网络老师要求用静态路由配置，写很多规则，难道是老师折磨学生的变态心理？ 很多同学的第一反应是为了让左侧的 PC0 能够 Ping 通右侧的 PC7，干脆在 Router0、Router1 和右侧的所有路由器上全部开启 RIP，互相学习路由。全网路由全覆盖，自然就通了。然后做一个NAT限制，外网访问不了PC0等内网设备就可以了，我的组员队友就是踩在了这个坑里 真相是：\n这样做在模拟器里确实能通，但在逻辑上完全违背了真实世界的网络架构，也让 NAT 彻底失去了意义。 在真实情况中，Router1 是运营商的路由器，公网路由器绝对不可能包含你家内网（192.168.x.x）的路由表。如果用了动态路由，左右两边完全不需要做 NAT 转换就能互相访问，公网私网的边界就被打破了。路由器同样学会了外网怎么打入内网设备的路由，那这个NAT还有什么意义？ 2. 公网不知道私网的IP，那么Ping的回包是怎么传回的？ 假设 PC0（192.168.14.1）去 Ping 远端的 PC7，流程如下：\n去程： PC0 发出 ICMP 包（源:192.168.14.1，目的:PC7 IP）。包到达边界 Router0。 NAT 介入： Router0 发现配置了 NAT，于是将包的源 IP 修改为自己的公网 IP（200.14.1.2），目的 IP 不变，丢给下一跳 Router1。 Router1 查路由表，找到去往 PC7 的路，将包顺利送达。 回程： PC7 收到包，准备回包。此时回包的目的 IP 是 200.14.1.2（Router0 的公网口 IP），而不是 PC0 的私网 IP！ NAT 映射开始作用： Router1 知道怎么去 200.14.1.0 网段（直连路由），把包交还给 Router0。Router0 查自己的 NAT 映射表，把包的目的 IP 还原成 192.168.14.1，最终送回 PC0。 而相反的，如果是PC7直接pingPC1的话，数据包到达Router1之后就不知道去哪里了，此时路由器会直接丢包并向源地址PC7地址发送一个 ICMP 错误消息（如“目的不可达”或“超时”）。 ","date":"2026-05-24T22:59:27+08:00","image":"/p/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/%E7%BD%91%E7%BB%9C%E6%8B%93%E6%89%91.png","permalink":"/p/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/","title":"NAT配置与路由"},{"content":"一. 算法 求前面0的个数，我们最先想到的方式就是从第一位开始遍历，C语言代码很好写，也就是从软件上的算法很简单，只需要遍历判断的逻辑就可以统计出前0个数，但是这种遍历的方法很低效因为如果将这种串行的 for 循环直接映射到硬件电路上，会生成一种被称为行波结构的深度级联逻辑。 对于 1024 比特的超宽数据，串行结构意味着最坏情况下的关键路径需要依次穿过上千个逻辑门（如数据选择器 MUX）。这种结构的组合逻辑延迟时间复杂度为 O(N)。在极长的逻辑链路下，门延迟与线延迟的不断累加会导致电路的最高工作频率急剧下降，根本完全无法满足现代高速数字系统的时序要求。\n所以适合的算法应该是：二叉树分治,算法的核心在于将一个庞大的 1024-bit 查找问题，不断对半拆分，直到拆成最基础的 2-bit 单元，然后再逐级向上合并结果\n二. 电路图Schematic 三. 数学与理论分析 四. Verilog代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 `timescale 1ns / 1ps ​ // ============================================================================= // Module: lzc_tree (Leading Zero Counter Tree) // Parameter: WIDTH - 输入数据的位宽 (必须是 2 的幂次方，如 2, 4, 8... 1024) // COUNT_WIDTH - 计数输出的位宽，自动计算为 log2(WIDTH) // ============================================================================= module lzc_tree #( parameter WIDTH = 1024, parameter COUNT_WIDTH = $clog2(WIDTH) )( input wire [WIDTH-1:0] in, output wire [COUNT_WIDTH-1:0] count, output wire all_zero ); ​ generate // --------------------------------------------------------------------- // Base Case: 2-bit 最底层叶子节点 // --------------------------------------------------------------------- if (WIDTH == 2) begin : gen_base // 如果高位和低位都是 0，则全零标志位置 1 assign all_zero = ~in[1] \u0026amp; ~in[0]; ​ // 如果高位 in[1] 是 1，前导零个数为 0； // 如果高位 in[1] 是 0，前导零个数为 1。 // (注意：如果 in 为 00，all_zero 为 1，此时 count 的值在上一级会被忽略，所以这里可以简化逻辑) assign count[0] = ~in[1]; ​ end // --------------------------------------------------------------------- // Recursive Case: 树的中间节点合并逻辑 // --------------------------------------------------------------------- else begin : gen_tree wire [COUNT_WIDTH-2:0] count_l, count_r; wire az_l, az_r; ​ // 递归实例化左半部分（高位段） lzc_tree #( .WIDTH(WIDTH/2), .COUNT_WIDTH(COUNT_WIDTH-1) ) inst_left ( .in(in[WIDTH-1 : WIDTH/2]), .count(count_l), .all_zero(az_l) ); ​ // 递归实例化右半部分（低位段） lzc_tree #( .WIDTH(WIDTH/2), .COUNT_WIDTH(COUNT_WIDTH-1) ) inst_right ( .in(in[WIDTH/2-1 : 0]), .count(count_r), .all_zero(az_r) ); ​ // 【核心合并逻辑】 // 只有当左右两边全为零时，当前层级才全为零 assign all_zero = az_l \u0026amp; az_r; ​ // 巧妙的位拼接逻辑： // 如果左半部分（高位）全是 0，说明前导零数量 = 左半边全长 + 右半边前导零。 // 在二进制中，这刚好等同于在最高位补 \u0026#39;1\u0026#39;，拼接右半边的计数。 // 如果左半部分不全是 0，前导零在左边，直接在最高位补 \u0026#39;0\u0026#39;，拼接左半边的计数。 assign count = az_l ? {1\u0026#39;b1, count_r} : {1\u0026#39;b0, count_l}; ​ end endgenerate ​ endmodule 五. 功能仿真图 六. 功能仿真结果分析 图中 error_count[31:0] 那一行，从头到尾都是一根笔直的低电平绿线（数值恒为 00000000）。这说明在我们写的比较器逻辑中，1024 位的海量随机测试和极端边界测试，没有发生哪怕一次结果不匹配。\n对比一下 count[9:0] 和 expected_count[9:0] 这两行的波形。你会发现无论是方框的长短，还是数据跳变的时间点，两者在视觉上完全一模一样，如同倒影。这就证明硬件电路的结果紧紧咬住了软件算出的正确答案。\n屏幕最左侧（0 ns 到 10 ns 的位置），all_zero 和 expected_all_zero 都有一个小小的脉冲凸起（高电平）。回忆一下我们在 Testbench 里的代码： in = {WIDTH{1\u0026rsquo;b0}}; #10; // 第一项测试：全0 这个小小的凸起完美地证明了：在仿真的最初 10 纳秒内，输入是全零，电路正确地输出了“全零标志位”。10纳秒后，输入变成了全 1，标志位立刻降为 0。\n七. Synthesis Path 1 是整个 1024-bit 树形电路中最长的数据通路。它的总延迟是 10.781 ns。Path 1 到 Path 10 的 Levels，数值全都是 9 或者 10。\n如果使用传统的 for 循环从高位到低位依次判断，对于 1024-bit 的数据，关键路径需要穿过约 1024 个数据选择器MUX，逻辑级数接近 O(N) 即 1024 级，延迟极其巨大，电路无法在时钟下工作。 而本设计采用了二叉树分治架构，将延迟的复杂度从 O(N)降维到了 O(\\log_2 N)。理论上 1024-bit 的二叉树深度恰好为 log_2(1024) = 10 级。\n如 Vivado 时序分析报告所示，经过逻辑综合后，最长关键路径（Path 1）的总延迟为 10.781 ns。其中最核心的逻辑级数实测为 9 级（部分路径为 10 级）。理论上树有 10 层，为什么 Vivado 综合出来有的是 9 级？因为 Xilinx 7系列 FPGA 的底层是由 6输入查找表（LUT6）构成的。综合工具在映射时，把最底层的 2-bit 单元和 4-bit 单元的逻辑合并到了同一个 LUT6 里面，从而将 10 级的组合逻辑进一步压缩成了 9 级。\n八. 结论 二叉树架构成功将 1024 级的庞大延迟压缩至 9~10 级查找表延迟，在没有任何流水线寄存器切分的情况下，纯组合逻辑仅耗时不到 11 ns，极大提升了电路的最高工作频率\n","date":"2026-05-24T22:26:27+08:00","image":"/p/fpga-ic-design/image.png","permalink":"/p/fpga-ic-design/","title":"集成电路设计-前导零检测器LZD(Leading Zero Detector)"},{"content":"helm install之后的log be like： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 (base) savilahao@bogon ~ % helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring --create-namespace NAME: prometheus LAST DEPLOYED: Wed May 20 14:54:39 2026 NAMESPACE: monitoring STATUS: deployed REVISION: 1 DESCRIPTION: Install complete TEST SUITE: None NOTES: kube-prometheus-stack has been installed. Check its status by running: kubectl --namespace monitoring get pods -l \u0026#34;release=prometheus\u0026#34; # ======================Grafana登陆与port转发教程=============== Get Grafana \u0026#39;admin\u0026#39; user password by running: kubectl --namespace monitoring get secrets prometheus-grafana -o jsonpath=\u0026#34;{.data.admin-password}\u0026#34; | base64 -d ; echo Access Grafana local instance: export POD_NAME=$(kubectl --namespace monitoring get pod -l \u0026#34;app.kubernetes.io/name=grafana,app.kubernetes.io/instance=prometheus\u0026#34; -oname) kubectl --namespace monitoring port-forward $POD_NAME 3000 Get your grafana admin user password by running: kubectl get secret --namespace monitoring -l app.kubernetes.io/component=admin-secret -o jsonpath=\u0026#34;{.items[0].data.admin-password}\u0026#34; | base64 --decode ; echo Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create \u0026amp; configure Alertmanager and Prometheus instances using the Operator. 如helm创建后指导所示看密码： kubectl get secret --namespace monitoring -l app.kubernetes.io/component=admin-secret -o jsonpath=\u0026quot;{.items[0].data.admin-password}\u0026quot; | base64 --decode ; echo ^ebfc61\n查看helm起的agons的配置： helm list -A看release名： 1 2 3 4 NAME NAMESPACE REVISION\tUPDATED STATUS CHART APP VERSION agones agones-system 1 2026-05-19 13:07:59.439179 +0800 CST\tdeployed\tagones-1.57.0 1.57.0 metallb metallb-system\t1 2026-05-19 13:01:18.107293 +0800 CST\tdeployed\tmetallb-0.15.3 v0.15.3 prometheus\tmonitoring 1 2026-05-20 14:54:39.049677 +0800 CST\tdeployed\tkube-prometheus-stack-85.2.0\tv0.90.1 看具体配置（也就是value [!注意默认不加“a参数” 的话只能看到你手动set过的value] helm get values agones -n agones-system -a\n1 2 3 (base) savilahao@bogon ~ % helm get values agones -n agones-system -a | grep prome prometheusEnabled: true prometheusServiceDiscovery: true Q1:prometheusEnabled: true说明什么？ 在 Agones 中开启这个配置，它的实际意思是：Agones Controller 在代码层面启动了一个内置的 HTTP Server（通常是 8080 端口的 /metrics 路径），并且开始以 Prometheus 能看懂的纯文本格式往外“吐”数据（比如当前有多少个游戏服）。 Q2:prometheusServiceDiscovery: true 是干嘛的？ 简答：做服务发现—— 原生 Kubernetes 早期是用 Annotations（注解）来做服务发现的。开启这个，Agones 会给自己的 Pod 打上类似 prometheus.io/scrape: \u0026quot;true\u0026quot; 的标签。意思是：“嗨，如果有谁在收集监控数据，请来这里抓我。”\n","date":"2026-05-21T20:34:27+08:00","image":"/p/helm-notes/helm.png","permalink":"/p/helm-notes/","title":"知识点：helm"},{"content":"观测逻辑链条 1 2 3 4 5 6 7 Prometheus ↓ select ServiceMonitor ↓ select Service ↓ endpoint Pod /metrics 1.首先helm add: 1 2 3 4 helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update # 在 monitoring 命名空间下安装全家桶 helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring --create-namespace 2.查你自己生产集群对象字段再做连接prometheus kubectl get prometheus -n monitoring -o yaml | grep -A 5 serviceMonitorSelector\n1 2 3 4 5 6 7 (base) savilahao@bogon ~ % kubectl get prometheus -n monitoring -o yaml | grep -A 5 serviceMonitorSelector serviceMonitorSelector: matchLabels: release: prometheus #你通过helm拉的一般都叫这个名字 shards: 1 tsdb: outOfOrderTimeWindow: 0s Prometheus Operator只看有特定 Label 的资源，matchLabels这个key下是你需要告诉它的内容，后续写到k apply YAML 里 metadata.labels对象中 查Agones 暴露指标的靶子（定位 Service 和 Port Name） 1 2 3 4 5 6 7 8 9 10 11 12 # 先看srv对象列表 (base) savilahao@bogon ~ % kubectl get svc -n agones-system | grep metrics agones-allocator-metrics-service ClusterIP 10.101.11.151 \u0026lt;none\u0026gt; 8080/TCP 26h agones-controller-metrics-service ClusterIP 10.109.209.46 \u0026lt;none\u0026gt; 8080/TCP 26h agones-extensions-metrics-service ClusterIP 10.105.232.133 \u0026lt;none\u0026gt; 8080/TCP 26h # 看对应srv对象的Port Name (base) savilahao@bogon ~ % kubectl get svc agones-controller-metrics-service -n agones-system -o jsonpath=\u0026#39;{.spec.ports[*].name}{\u0026#34;\\n\u0026#34;}\u0026#39; metrics #同样的命令抓allocator和extensions的 # 看Agones 的 Service 自己带了什么标签 (base) savilahao@bogon ~ % kubectl get svc agones-controller-metrics-service -n agones-system --show-labels NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS agones-controller-metrics-service ClusterIP 10.109.209.46 \u0026lt;none\u0026gt; 8080/TCP 26h agones.dev/role=controller,app.kubernetes.io/managed-by=Helm,app=agones,chart=agones-1.57.0,heritage=Helm,release=agones kubectl get svc agones-allocator-metrics-service -n agones-system \u0026ndash;show-labels\n根据信息组成yaml： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: agones-metrics namespace: agones-system #注意这里最好写业务的ns而不是我们刚才创建的monitoring ns labels: # 替换为你从第一步查到的 Prometheus Selector release: prometheus spec: endpoints: - port: metrics # 替换为你从第二步查到的 Port Name (可能是 http web) - port: http #我抓到allocator的是这个 namespaceSelector: matchNames: - agones-system selector: matchLabels: # 替换为你从第三步查到的 Service Label app: agones 做check：\n1 2 3 (base) savilahao@bogon ~ % (base) savilahao@bogon ~ % kubectl get servicemonitor -n agones-system NAME AGE agones-all-metrics 4m12s 进入 Grafana 监控室 kubectl port-forward svc/prometheus-grafana -n monitoring 3000:80\n输入user和pw 补充，转发进入服务器指令：\nkubectl port-forward svc/prometheus-grafana -n monitoring 3000:80是Grafana 可视化界面的server ^f070bd kubectl port-forward -n monitoring svc/prometheus-operated 9090用于Prometheus 服务器本身 import json 到dashboards后： 当然这里有个小插曲，开始的时候都是红色三角no data, fix: add Data Source-Prometheus mannually 看板信号分析：\nGameServers count per type (饼图) \u0026amp; count overview (折线图): 这两个图表显示，当前集群里只有 1 个状态为 Ready 的游戏服。结合下方的折线图，这条绿线一直平稳维持在 1，说明这个游戏服已经存活了一段时间。 GameServers per node \u0026amp; Node availability: 这两个底部的图表监控的是底层的物理资源。它告诉你，当前有一个 Node（也就是你的 Minikube 虚拟机）正在被“使用（used）”，并且上面跑着这个孤零零的游戏服。 Q:为什么Fleet RollOut Percentage, Fleet Replicas Count是No Data？ 当前集群里只有一个散养的单体 GameServer,并没有部署任何 Fleet资源 证据： 1 2 (base) savilahao@bogon ~ % kubectl get fleet No resources found in default namespace. Grafana监测实验：部署gameServer舰队！(Fleet对象) kubectl apply -f :\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 cat \u0026lt;\u0026lt;EOF | kubectl apply -f - apiVersion: \u0026#34;agones.dev/v1\u0026#34; kind: Fleet metadata: name: simple-game-fleet spec: # 我们要求集群里永远保持 5 个 Ready 状态的游戏服 replicas: 5 template: spec: ports: - name: default containerPort: 7654 template: spec: containers: - name: simple-game-server image: us-docker.pkg.dev/agones-images/examples/simple-game-server:0.35 EOF 我们的舰队all set!!!\n1 2 3 (base) savilahao@bogon ~ % kubectl get fleet NAME SCHEDULING DESIRED CURRENT ALLOCATED READY AGE simple-game-fleet Packed 5 5 0 5 5m23s 爽看Grafana： ^b3ca9b\nQ：“General，鄙人突然想把这支新组建的舰队都干掉怎么办？”\n1 kubectl delete fleet simple-game-fleet ","date":"2026-05-21T20:34:27+08:00","image":"/p/observability-practice/grafana.png","permalink":"/p/observability-practice/","title":"知识点：生产环境observability做法"},{"content":"书接上回，现在我们部署好了gameserver和监控系统，现在还要研究agones的两个核心功能：模拟玩家进场和动态扩缩容 依赖的组件：\nFleetAutoscaler：基于缓冲区的自动扩缩容 GameServerAllocation：模拟Matchmaker分配房间服务器![[参考文档#^6dde12]] ","date":"2026-05-20T00:00:00+08:00","permalink":"/p/agones-may20-notes/","title":"k8s生产化项目-Agones导学（May20）"},{"content":"安装与配置 1 2 3 4 5 6 7 8 9 10 11 12 # 安装 brew install kubectl brew install helm brew install minikube # 创建minukube k8s cluster minikube start \\ --kubernetes-version v1.34.6 \\ -p agones \\ --driver docker \\ --cpus 4 \\ --memory 8192 配置MetalLB\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # 安装 MetalLB helm repo add metallb https://metallb.github.io/metallb helm repo update helm install metallb metallb/metallb \\ --namespace metallb-system \\ --create-namespace # 安装好都就绪后先minikube ip -p agones看IP然后配置IP地址池 cat \u0026lt;\u0026lt;EOF | kubectl apply -f - apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: default-pool namespace: metallb-system spec: addresses: - 192.168.49.50-192.168.49.250 # 从 minikube ip 的⽹段分配 --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: default namespace: metallb-system spec: ipAddressPools: - default-pool EOF 安装Agones helm repo add ; helm repo update ; helm install agones xx\n部署服务 kubectl apply -f https://raw.githubusercontent.com/agones-dev/agones/release-1.57.0/examples/simple-game-server/gameserver.yaml\nminikube tunnel -p agones 修改macos路由表，让所有发往整个 LoadBalancer 网段的包都走特定网关IP，从而可以转发到k8s集群内部\n外部可以ping通但是无法udp连接(我用 minikube tunnel -p agones之后也不行)，进入minikube ssh -p agones后发现可以ACK返回测试正常\n停止集群运行：保留磁盘但是释放内存和cpu minikube stop -p agones\n看minikube整体情况 minikube profile list\n看某个profile的局域网IP minikube ip -p agones 看Agones GameServer 的分配端口： kubectl get gs -n \u0026lt;namespace\u0026gt;\n安装成功后测试 1 2 3 4 # 看gameserver的IP和端口后 docker@agones:~$ nc -u 192.168.58.2 7621 kk ACK: kk 这里证明 UDP 包已经成功打通了 Minikube 的网络，并被游戏服接收并回包。\n体会agones项目的工程魅力 kubectl describe gs simple-game-server-pwdww\n可以看到events： 1 2 3 4 5 6 7 8 Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal PortAllocation 7m38s gameserver-controller Port allocated Normal Creating 7m38s gameserver-controller Pod simple-game-server-pwdww created Normal Scheduled 7m37s gameserver-controller Address and port populated Normal RequestReady 7m36s gameserver-sidecar SDK state change Normal Ready 7m36s gameserver-controller SDK.Ready() complete Status部分，看看 Agones 是如何把端口映射和宿主机 IP 同步到资源状态里的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Status: Address: 192.168.58.2 Addresses: Address: 192.168.58.2 Type: InternalIP Address: agones Type: Hostname Address: 10.244.0.25 Type: PodIP Eviction: Safe: Never Immutable Replicas: 1 Node Name: agones Players: \u0026lt;nil\u0026gt; Ports: Name: default Port: 7621 Reserved Until: \u0026lt;nil\u0026gt; State: Ready 一个实验，体会pod优雅退出（gameserver业务逻辑：自己决定自己的生死，不由k8s调度机制杀掉）： 实验：在nc -u中发送EXIT信号，观察容器状态编程退出 观察这个gm完整的生命周期： 给pod发送EXIT信号这个pod就自动把自己删除了，k get pod -A 不会看到 0/1 ，这是gameserve的业务逻辑，一个游戏结束后该服务器就完全释放掉了\n理解agones重要工程机制 kubectl logs simple-game-server-rcsxb -c agones-gameserver-sidecar 密密麻麻的输出中， \u0026quot;grpcEndpoint\u0026quot;:\u0026quot;localhost:9357\u0026quot;\n\u0026quot;httpEndpoint\u0026quot;:\u0026quot;localhost:9358\u0026quot; 这说明 Sidecar 启动后，在 Pod 的内部网络空间（localhost）死死地守住了这两个端口。监听信号：\n游戏进程启动时，它其实是向 localhost:9357（Sidecar 的端口）发送了一个 /Ready 请求。 当你敲下 EXIT 时，你的程序也是向 localhost 发送了 /Shutdown 请求。 业务代码完全不需要懂怎么调用复杂的 Kubernetes API。它只需要和同 Pod 内的本地 Sidecar 通信。Sidecar 收到指令后，替你去和 K8s 的 API Server 交涉，修改 CRD 的状态。这就是典型的解耦设计。\nkubectl get pod simple-game-server-rcsxb -o yaml | grep hostPort 1 2 # 输出 hostPort: 7078 Agones 暴躁地跳过了 K8s 标准的 Service 负载均衡，直接将宿主机的网卡端口和容器内的 UDP 端口硬绑死。刚才用 nc 连的 192.168.58.2 根本不是一个虚拟 IP，那是 Minikube 虚拟机的真实物理 IP。Agones Controller 在后台帮你维护了一个庞大的端口池，自动避开端口冲突，实现了网络直通（Direct Routing）。\n在原生 K8s 中，是 Kubelet 定期去Probe你的 Pod 活没活着。但在 Agones 中，这个逻辑被反转了。日志会看到游戏程序在疯狂地、周期性地向 Sidecar 发送心跳包。 机制揭秘： 游戏服一旦卡死（比如死锁），Kubelet 的外部探测可能无法察觉业务逻辑层的假死。Agones 要求业务进程主动调用 SDK 上报 Health()。一旦 Sidecar 一定时间内没收到心跳，Sidecar 就会直接向 K8s 宣告这个游戏服处于 Unhealthy 状态，触发后续的回收逻辑。 该心跳机制可以看日志：\n1 2 3 4 5 6 7 8 9 (base) savilahao@bogon ~ % k logs simple-game-server-rcsxb Defaulted container \u0026#34;simple-game-server\u0026#34; out of: simple-game-server, agones-gameserver-sidecar (init) 2026/05/20 06:15:09 Creating SDK instance 2026/05/20 06:15:09 Starting Health Ping 2026/05/20 06:15:09 Marking this server as ready 2026/05/20 06:15:09 Starting UDP server, listening on port 7654 2026/05/20 06:15:09 Health Ping 2026/05/20 06:15:11 Health Ping 2026/05/20 06:15:13 Health Ping 做下observability(Prometheus + Grafana) [[知识点：生产环境observability做法]] agone项目暴露了很多观测借口，\n1 2 3 4 helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update # 在 monitoring 命名空间下安装全家桶 helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring --create-namespace 创建一个 ServiceMonitor 资源，把 Agones Controller 的指标端点暴露给它:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 cat \u0026lt;\u0026lt;EOF | kubectl apply -f - apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: agones-all-metrics namespace: agones-system labels: release: prometheus spec: namespaceSelector: matchNames: - agones-system selector: matchLabels: # 靠这个标一网打尽三个 Service app: agones endpoints: # 匹配 Controller 和 Extensions - port: metrics # 匹配 Allocator - port: http EOF 可以查到这个serviceMonitor对象创建成功：\n1 2 3 (base) savilahao@bogon ~ % kubectl get servicemonitor -n agones-system NAME AGE agones-all-metrics 4m12s 端口转发+登陆Grafana： kubectl port-forward svc/prometheus-grafana -n monitoring 3000:80 看密码![[知识点：helm#^ebfc61]]\n进入grafana后import json, json哪里来？ 从官方项目复制 curl -LO https://raw.githubusercontent.com/agones-dev/agones/main/build/grafana/dashboard-gameservers.yaml\n最终看板效果![[知识点：生产环境observability做法#^b3ca9b]]\n写在实验结束： minikube资源生命管理层级：\nminikube pause -p agones -\u0026gt;不占CPU了，但是内存依然在吃 minikube stop -p agones -\u0026gt;Graceful Shutdown，给Kubelet 和所有的 Pod 发送 SIGTERM 信号，CPU和占用内存都释放掉 minikube delete -p agones -\u0026gt; 毁灭整个集群，释放CPU和内存，同时连带之前写入的磁盘资源一并释放 Q：下次如何恢复之前的状态？ Kubernetes 是Declarative的架构，依赖etcd 的分布式键值数据库，所有数据都做了持久化，理论上不需要我做什么minikube start -p agones就可以恢复，不过监测这里我还要输port-forward命令连到grafana server，另外就是Grafana 面板可能需要在dashboards中重导个那个json文件![[知识点：生产环境observability做法#^f070bd]]\n总结 我的暴露出来的一些弱点：\n对网络层级的理解一般，本次ICMP可通但是TCP不通其实可以用cn的知识很好理解但是i cant 对Kubernetes的网络入口机制不清楚，不知道NodePort Ingress 这些都是干嘛的，概念都没有，not to mention结合这些设计原理进行网络排查 ","date":"2026-05-19T00:00:00+08:00","permalink":"/p/agones-may19-notes/","title":"k8s生产化项目-Agones导学（May19）"},{"content":"This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.\nHeadings The following HTML \u0026lt;h1\u0026gt;—\u0026lt;h6\u0026gt; elements represent six levels of section headings. \u0026lt;h1\u0026gt; is the highest section level while \u0026lt;h6\u0026gt; is the lowest.\nH1 H2 H3 H4 H5 H6 Paragraph Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.\nItatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.\nBlockquotes The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a footer or cite element, and optionally with in-line changes such as annotations and abbreviations.\nBlockquote without attribution Tiam, ad mint andaepu dandae nostion secatur sequo quae. Note that you can use Markdown syntax within a blockquote.\nBlockquote with attribution Don\u0026rsquo;t communicate by sharing memory, share memory by communicating.\n— Rob Pike1\nTables Tables aren\u0026rsquo;t part of the core Markdown spec, but Hugo supports supports them out-of-the-box.\nName Age Bob 27 Alice 23 Inline Markdown within tables Italics Bold Code italics bold code A B C D E F Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ultricies, sapien non euismod aliquam, dui ligula tincidunt odio, at accumsan nulla sapien eget ex. Proin eleifend dictum ipsum, non euismod ipsum pulvinar et. Vivamus sollicitudin, quam in pulvinar aliquam, metus elit pretium purus Proin sit amet velit nec enim imperdiet vehicula. Ut bibendum vestibulum quam, eu egestas turpis gravida nec Sed scelerisque nec turpis vel viverra. Vivamus vitae pretium sapien Code Blocks Code block with backticks 1 2 3 4 5 6 7 8 9 10 \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Code block indented with four spaces \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026quot;en\u0026quot;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026quot;utf-8\u0026quot;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Diff code block 1 2 3 4 5 [dependencies.bevy] git = \u0026#34;https://github.com/bevyengine/bevy\u0026#34; rev = \u0026#34;11f52b8c72fc3a568e8bb4a4cd1f3eb025ac2e13\u0026#34; - features = [\u0026#34;dynamic\u0026#34;] + features = [\u0026#34;jpeg\u0026#34;, \u0026#34;dynamic\u0026#34;] One line code block 1 \u0026lt;p\u0026gt;A paragraph\u0026lt;/p\u0026gt; List Types Ordered List First item Second item Third item Unordered List List item Another item And another item Nested list Fruit Apple Orange Banana Dairy Milk Cheese Other Elements — abbr, sub, sup, kbd, mark GIF is a bitmap image format.\nH2O\nXn + Yn = Zn\nPress CTRL + ALT + Delete to end the session.\nMost salamanders are nocturnal, and hunt for insects, worms, and other small creatures.\nThe above quote is excerpted from Rob Pike\u0026rsquo;s talk during Gopherfest, November 18, 2015.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023-09-07T00:00:00Z","permalink":"/p/markdown-syntax-guide/","title":"Markdown Syntax Guide"},{"content":"Hugo theme Stack supports the creation of interactive image galleries using Markdown. It\u0026rsquo;s powered by PhotoSwipe and its syntax was inspired by Typlog.\nTo use this feature, the image must be in the same directory as the Markdown file, as it uses Hugo\u0026rsquo;s page bundle feature to read the dimensions of the image. External images are not supported.\nSyntax 1 ![Image 1](1.jpg) ![Image 2](2.jpg) Result Photo by mymind and Luke Chesser on Unsplash\n","date":"2023-08-26T00:00:00Z","image":"/p/image-gallery/2.jpg","permalink":"/p/image-gallery/","title":"Image gallery"},{"content":"For more details, check out the documentation.\nBilibili video Tencent video YouTube video Generic video file Your browser doesn't support HTML5 video. Here is a link to the video instead. GitLab Quote Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n― A famous person, The book they wrote Photo by Codioful on Unsplash\n","date":"2023-08-25T00:00:00Z","image":"/p/shortcodes/cover.jpg","permalink":"/p/shortcodes/","title":"Shortcodes"},{"content":"Stack has built-in support for math typesetting using KaTeX.\nIt\u0026rsquo;s not enabled by default side-wide, but you can enable it for individual posts by adding math: true to the front matter. Or you can enable it side-wide by adding math = true to the params.article section in config.toml.\nInline math This is an inline mathematical expression: $\\varphi = \\dfrac{1+\\sqrt5}{2}= 1.6180339887…$\n1 $\\varphi = \\dfrac{1+\\sqrt5}{2}= 1.6180339887…$ Block math $$ \\varphi = 1+\\frac{1} {1+\\frac{1} {1+\\frac{1} {1+\\cdots} } } $$ 1 2 3 $$ \\varphi = 1+\\frac{1} {1+\\frac{1} {1+\\frac{1} {1+\\cdots} } } $$ $$ f(x) = \\int_{-\\infty}^\\infty\\hat f(\\xi)\\,e^{2 \\pi i \\xi x}\\,d\\xi $$ 1 2 3 $$ f(x) = \\int_{-\\infty}^\\infty\\hat f(\\xi)\\,e^{2 \\pi i \\xi x}\\,d\\xi $$ ","date":"2023-08-24T00:00:00Z","permalink":"/p/math-typesetting/","title":"Math Typesetting"},{"content":"Welcome to Hugo theme Stack. This is your first post. Edit or delete it, then start writing!\nFor more information about this theme, check the documentation: https://stack.jimmycai.com/\nWant a site like this? Check out hugo-theme-stack-stater\nPhoto by Pawel Czerwinski on Unsplash\n","date":"2022-03-06T00:00:00Z","image":"/p/hello-world/cover.jpg","permalink":"/p/hello-world/","title":"Hello World"}]