本页目录

云 II · 编排、K8s 与可观测性

对标:Kubernetes 文档 / Kubernetes Up & Running / Google SRE 书 | 前置:cloud-01(容器)、dist 线(分布式)、web-02(可观测性初步) 几个容器用 docker-compose 够了;但当你有成百上千个容器跨几十台机器、要自愈、要按负载扩缩容——就需要编排(orchestration)。这一页讲 Kubernetes 的核心思想(声明式 + 控制循环,一个优雅的分布式系统)、以及可观测性(怎么看见一个庞大系统的健康——监控、日志、追踪、告警)。这条线的落点是"让大系统可靠运行"的运维工程,也回收 Medusa"静默失败"的教训。

学习层:控制器看到偏差后,下一步应该做什么?

1. 具体谜题:期望 3 个副本,为什么 Service 只应该送流量给 2 个?

Deployment 声明 replicas: 3。当前有一个 Pod 崩溃、一个 Pod 尚未通过 readiness probe,节点仍有容量;另一时刻 CPU 负载升高,HPA 的目标上限为 5。先预测:

  1. 第一次 reconcile 应创建、重启还是删除 Pod?
  2. 未 ready 的 Pod 是否应进入 Service endpoint?
  3. 负载升高时,控制循环应改变 desired replicas,还是只把流量强行发给现有 Pod?

实验台固定期望状态、观测状态、容量和健康信号;提交预测后再观察一个控制周期的动作、endpoint 集合和可观测指标。重点是把“控制器纠偏”和“Service 路由资格”分开。

2. 最小模型:期望、观测、动作、下一次观测

用状态向量表示

\[ S_t=(D_t,O_t,H_t,C_t), \]

其中 D 是期望副本/版本,O 是实际对象,H 是 readiness/health,C 是节点容量。控制器计算偏差 D_t - O_t,产生动作 A_t,系统在下一次观测得到 S_{t+1}。Service 只把 H_t=ready 且符合 selector 的端点加入流量集合。

这不是瞬时函数:创建 Pod、拉镜像、探针通过、节点调度和网络传播都需要时间。一个健康的循环应在容量、权限和依赖可用时使偏差逐步缩小,但不承诺单次循环立即收敛。

3. 正式机制与不变量:自愈是反馈,不是魔法

  • 副本不变量:在资源足够且控制面可用时,期望副本数是长期目标;短暂的 observed < desired 是收敛过程中的状态,不必伪装成即时一致。
  • 路由不变量:未 ready、错误版本或 selector 不匹配的 Pod 不应接收 Service 流量;否则控制面“修好副本”仍可能把错误暴露给用户。
  • 滚动更新不变量:新版本要先达到 readiness,再按策略替换旧版本;错误率和可用性信号触发暂停或回滚。
  • 观测边界:metrics、logs、traces 是对内部状态的投影;没有 trace 不能把跨服务延迟归因给某个 Pod,没有告警也不能把“暂无事件”当作健康。

控制循环、etcd 状态、scheduler 放置、Service 发现和 HPA 指标彼此耦合;把某一个 YAML 字段当成整个系统的保证,是把机制范围说过头。

4. 失败边界与迁移任务

网络分区、控制面不可达、镜像仓库故障、资源不足、错误 readiness、指标延迟和有状态存储都会让自愈停在边界;扩容也可能把下游数据库压垮。实验固定单一控制器、即时观测和简化容量,不替代生产的 rollout、PDB、HPA 和 SLO 设计。

迁移任务:为 Medusa 的 webapp、worker 和 Postgres 分别写出期望状态、ready 条件、可观测信号、扩缩容边界和回滚动作;再说明为什么单机、稳定流量的小系统不应仅凭“能自愈”就引入 K8s。

JavaScript 失效时的静态读法:先比较 desired 与 observed,再检查 readiness;控制器补副本不等于新 Pod 已能接流量。负载变化影响 desired,健康变化影响 endpoint。

情景 desired/observed ready endpoints 下一动作
Pod 崩溃 3 / 2 2 创建替代 Pod
Pod 未 ready 3 / 3 2 不路由给未 ready,等待探针
CPU 高负载 3 / 3,目标上限 5 3 HPA 增加 desired
新版本金丝雀异常 3 / 3 3,但错误率升高 暂停 rollout 或回滚

1. 为什么需要编排

单机几个容器你手动管(cloud-01)。但规模上去后,一堆事没法手动:机器挂了容器要迁走、流量涨了要加实例、更新要滚动不能停服、几百个容器要相互发现和通信。编排系统自动化这些——你声明"我要 3 个这个服务的副本",它负责让现实始终符合这个声明。Kubernetes(K8s)是事实标准。

2. K8s 的核心思想:声明式 + 控制循环

K8s 控制循环:声明期望状态→观察实际→对比→调整(自愈),配 Pod/Deployment/Service/etcd 架构。

图 cloud-02.2K8s 控制循环:声明期望状态→观察实际→对比→调整(自愈),配 Pod/Deployment/Service/etcd 架构。

K8s 最优雅的地方是它的工作原理——声明式期望状态 + 控制循环(reconciliation):

这就是 K8s 的灵魂——"你说要什么状态,我持续保证它成立"(🔗 与 dist-02 Raft 的状态机复制、se-02 CD 的期望状态、甚至 React"UI=f(state)" web-03 同一种声明式哲学)。你不写"怎么做"(命令式脚本),只写"要什么"(期望状态),系统自己 reconcile。自愈能力就是控制循环的自然结果——不是特意编程"挂了就重启",而是"实际偏离期望就纠正"的通用机制顺带实现了自愈。

核心抽象:

3. K8s 提供的能力

代价与判断:K8s 强大但复杂——运维一个 K8s 集群本身是重活。不是所有系统都需要它:小项目、单机、流量稳定的,docker-compose 或干脆一台机器 + 进程管理就够(你的 Medusa 现在就是这样,完全合理)。"何时该上 K8s"是工程判断:多机、要自愈、要弹性扩缩、团队够大时才值。过早上 K8s 是一种技术债(se-02 的过度工程)。

4. 可观测性:看见庞大系统的健康

可观测性三支柱:指标/日志/追踪 + 告警(回收 Medusa 静默失败教训)。

图 cloud-02.1可观测性三支柱:指标/日志/追踪 + 告警(回收 Medusa 静默失败教训)。

系统越大越分布,"出了什么问题"越难查(🔗 dist 线的调试地狱)。可观测性是"从外部观察就能理解系统内部状态"的能力,三支柱(web-02 提过,这里系统化):

回收 Medusa 的教训(memory 血泪):"webapp 挂一周""RSS 断供 3 天静默失败""无主动告警""MedusaPredict 静默失败 4 天"——这些全是可观测性缺失的经典案例:系统坏了没人知道,因为没有监控 + 告警。本页给出的解药:哪怕最简单的健康检查(定时 curl 关键端点,挂了发个通知)+ 关键指标监控(articles 表还在涨吗?预测 state 更新了吗?),就能把"3 天后偶然发现"变成"3 分钟内收到告警"。可观测性不是大厂专利,是任何 24/7 系统的基本生存装备——这是本页对你最实用的一课。

5. SRE:可靠性的工程化

Google 的 SRE(站点可靠性工程) 把"运维"变成"工程":

方法论:可靠性是设计出来的,不是救火救出来的——冗余、自愈、可观测、快速回滚(se-02)、渐进发布,这些前置的工程投入,换来的是不用半夜起来救火。对你 24/7 的 Medusa,往这个方向走一小步(哪怕就加个健康检查告警),生活质量就大不一样。

6. 练习与要点

例 1(声明式的威力) 描述"手动保证 3 个副本一直在"(要不断检查 + 手动重启)vs K8s Deployment(声明 replicas: 3)——理解"声明期望 + 控制循环自愈"比命令式脚本强在哪。

例 2(该上 K8s 吗) 判断 Medusa 现状(单机、流量稳定、你 + GPT 小团队)该不该上 K8s——答案是不该(过度工程),练"何时该用重武器"的判断。

例 3(给 Medusa 加可观测) 设计一个最小监控:健康检查哪几个端点/指标(webapp 活着吗、articles 在涨吗、预测 state 新鲜吗)、超时怎么告警——把本页直接用到根治你系统的"静默失败"顽疾。\(\blacksquare\)


工程与全栈线的主体完成。下一页进入安全线——从攻击者视角理解系统的脆弱面,才能构建防御。(教育/防御视角:懂攻击是为了会防守。)