缘起
sentinel-go 最早只是一个很小的 Go HTTP 服务,后来顺着「怎么看清系统状态、怎么安全接管维护」一步步往下长:先接进 Linux 内存指标与 Prometheus,再装进容器与 Kubernetes,最后落成一个受限的节点维护协调器。
因此仓库里既有 /healthz 和内存表这种最基础的模块,也有 cordon 所有权注解、Eviction API、PDB 429 冷却和整轮 deadline 这类一不小心就会写成「表面能跑、遇到边界就坏」的控制逻辑。
问题
最开始去读 /proc 的时候,我发现自己以前对「系统在跑」的理解其实全靠现成命令的输出。真要自己把 MemAvailable 从内核文本变成 HTTP 指标,中间得挨个跨过字段缺失、单位换算、uint64 溢出保护、定时抓取和 PromQL 查询;哪一环偷了懒,监控面板上的数字就只是摆设。
等到往 Kubernetes 里走,节点维护也远不止敲一条 drain 命令那么简单。请求发出、API 受理、节点状态回读、Pod 真正退出、再到 PDB 预算恢复,这五个时刻在时间线上是彻底分开的。如果把它们混成一件事,写出来的代码就会在「刚发出请求」的那一刻草率宣布成功。
结构
整个项目由三条链路共用同一个 Go 二进制:一条负责把内核字段暴露成监控指标,一条负责在维护前只读地排查风险,最后一条才去集群里执行受限的节点维护。每条链路只管自己的输入、输出和失败退路,彼此之间不留隐式的黑盒。
观测
预检
维护
预检
真正动手封锁节点之前,先用只读客户端把现场摸清楚:节点上的 Pod 归属于哪个 ReplicaSet 和 Deployment、有没有挂载本地或非共享 PVC、同一批 Pod 会不会撞上多个重叠的 PDB。预检工具最后会给出一份三档风险报告和对应的退出码,既方便人直接看,也能塞进脚本里做门禁。
- 01
Reader
只读客户端,最小权限。
- 02
Snapshot
把 Pod、RS、Deployment、PDB 读成一份现场。
- 03
Analyze
归属链、PVC、重叠预算,逐项判断。
- 04
Report
三档风险与退出码,不写集群。
回归测试里专门放了两个反例:一个是挂载了 PVC 的 Pod,另一个是同一节点上互相重叠的 PDB。这两组用例必须稳定拦住预检——毕竟测试全绿本身不说明问题,能准确拦住坏情况才算有效。
控制器
下面的状态机可以直接点击推进。它把一次维护拆成了十一个状态:从发出 cordon 请求、等待回读确认,到撞上 PDB 429 进入冷却,再到预算恢复与最终解封。留意第一步的状态叫「已提交,等待观测」——因为请求发出去的那一瞬间,集群里的真实状态还没变呢。
一次维护的十一个状态
STEP 01 / 11
点击「开始维护」可单步推进状态机。任意步骤均可中断复位;当 PDB 预算耗尽时,Eviction API 会返回 429 并触发持久冷却。状态机不设自动播放,方便逐帧核对期望状态与观测回读的差异。
- pod-a Running node-01
- pod-b Running node-01
- pod-c Running node-01
指标均针对 node-01 与该 PDB;差异出现时,观测格会被标出。
节点可调度,3 个 Pod 就绪。PDB minAvailable = 2,当前允许 1 次中断。
示意基于真实控制器行为,但为便于阅读压缩了时间与次数;它不是线上集群的实时数据。
证据
- 01
并发安全:互斥锁保护的 MemoryStore、依赖注入装配,以及 go test -race 下的隔离测试。
- 02
输入校验:同时断言错误响应与「状态零副作用」,确保非法请求不会污染原有数据。
- 03
/proc/meminfo:覆盖字段缺失、非法数字、单位换算,以及 MaxUint64 / 1024 的溢出边界。
- 04
真实集群:在 kind 多节点集群中完成 readinessProbe 与 targetPort 错配排障,验证修复后流量恢复。
- 05
控制器重启:从 Status 中的 nextActionAt 与 deadline 重读现场,决定继续协调或安全停止。
边界
它离生产可用的通用 Operator 还有很大距离,边界很明确:
- 目前只覆盖单节点维护,没有经过多租户、大规模并发与长周期运行的验证。
- 实验主要在 kind 与自有测试环境完成,没有在真实生产业务集群上跑过完整维护窗口。
- 429 冷却与 deadline 仍有未覆盖的异常分支;「测试过重启恢复」不等于「所有中断场景都已穷尽」。
- 项目在教学目标、骨架与审查支持下完成;核心切片由本人亲手实现,但并非从零独立设计整套系统。