第 18 章:把项目变成可以值守的服务
一份演练从明确边界开始
在独立 kind 集群运行,不连接生产环境。固定 Go、kind、Kubernetes 和 Envoy 版本,并先记录实际服务器版本与镜像 digest。基线不是追逐最新版本,而是让不同读者能够复现同一组 API 行为。升级依赖应产生新的验证记录,不应直接替换旧证据。
运行 bash lab/run-kind.sh 会创建属于本次执行的集群,应用 CRD,构建控制器并执行场景,最后导出现场和删除自身集群。脚本不复用已有同名环境,也不把清理命令指向用户的当前默认集群。独立命名与显式 kubeconfig,是可重复实验的基础。
自动套件适合回归,手动演练则适合理解。先创建实例,读取默认后的对象,再观察 finalizer、登记、Deployment、Service 和状态。接着从客户端 Pod 访问 Service,确认资源关系确实支撑 HTTP 行为,然后再进行破坏操作。
三种日常故障
第一种是子资源误删。删除 Service 后,主资源 spec 不变,周期复查应重新创建入口。记录它的新 UID 与保留的主对象 UID,理解自愈并非恢复原对象生命,而是恢复期望关系。Service ClusterIP 可能改变,因此依赖稳定名称比缓存旧 IP 更可靠。
第二种是配置漂移。将 targetPort 改错,观察请求失败,再恢复控制器运行,确认托管字段被修复。不要只检查 YAML 已回到目标值,还要验证真实请求。这个路径能帮助区分“配置看起来正确”和“数据面已经完成更新”。
第三种是控制器停机。停止进程后更新副本或创建新实例,再启动。旧数据面可能继续服务,但新目标应等待重发现后推进。把控制平面恢复时延和数据面服务中断分开记录,避免用其中一个指标解释另一个。
一次删除阻塞处置
设置实验阻塞注解后删除实例。保存 UID、deletionTimestamp、finalizer、CleanupBlocked 条件和登记内容。确认对象没有消失,也没有在删除路径重新创建缺失 Service。随后解除注解,重新协调,等待登记和主对象消失,再观察垃圾回收完成。
如果现实外部系统不可用,不应直接照搬解除注解的动作。需要先恢复依赖或执行经过审核的补偿,确认不会留下收费资源、DNS 指向或访问权限。实验开关只是故障注入工具,生产处置必须由真实责任和证据驱动。
强制移除 finalizer 应是明确的运维决策,附带残留资源清单和后续补偿负责人。对象不见了只证明 Kubernetes 层的删除完成,不能把它写成业务清理已经成功。
升级前的审查清单应可执行
检查新旧 schema 的差异、默认值、served 和 storage 设置;确认旧控制器是否能理解新对象,状态字段是否兼容,回滚是否会丢失新数据。对同结构版本切换,可以沿随书测试验证双版本访问;对结构变更,则需要新的转换与存量迁移项目。
同时检查运行权限是否增加,镜像是否固定,探针是否仍符合业务语义,以及代码是否开始依赖本地文件。任何变化都可能穿过书稿、实验包和部署三条交付路径,因此不能只跑编译后就宣布升级完成。
本项目没有生产容器打包、leader election 和多集群控制面。要真正作为平台服务值守,需要补齐部署配置、资源预算、指标、告警、凭据轮换和容量验证。这些是清楚的后续工程任务,不影响当前单实例教学契约,但不能靠文档措辞假装已经具备。
如何继续扩展这一个项目
有三个自然方向。第一是加入 Route 对象和上游教学服务,研究多对象依赖、引用授权与配置汇总;第二是为子资源添加 watch,比较事件驱动与周期扫描的成本;第三是实现 conversion webhook 和受控存量迁移,验证双向兼容。
每个方向都应带来新的可证伪场景,而不只是更多 YAML。路由对象要验证删除传播与错误引用,事件驱动要验证丢失事件后的兜底恢复,版本迁移要验证旧对象和旧客户端。沿着恢复问题扩展,项目才会越来越接近真实工程。
最后的阅读练习
用自己的话写出四个不变量:不接管无关对象,不提前确认未处理 generation,不在清理完成前移除自身 finalizer,不把版本切换写成全量迁移。再从测试中分别找到直接证据。若某个结论只有解释却没有可观察证据,就把它保留为设计目标,继续完善实验,而不要将它升级为已经兑现的能力。
把未完成项变成明确任务
准备生产化时,将每项缺口写成可验收目标,例如双进程故障切换、受限凭据轮换、持续请求下的更新错误率,以及完整存量迁移。不要使用“加强稳定性”这类无法判断完成的描述。
沿着现有项目逐项增加证据,可以保留已经理解的资源生命周期。每一步都清楚说明新增承诺及限制,工程知识才能从教学演练自然延伸到团队值守。
交接时保留选择依据
把Recreate、周期复查和同结构版本切换写成经过选择的教学边界,而不是隐藏实现细节。接手者才能判断何时需要替换策略,以及替换后哪些旧测试仍有效。技术交接不只保存最终配置,还应保存为什么选择这条路径、目前证据能支撑到哪里。
故障演练之后将新发现转成可重复断言,再更新运行手册和发布记录。这样每次值守积累都会进入系统,而不会只停留在某位维护者的记忆里。