Kubernetes 基础体系 · 第 75/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。

Kubernetes 生产就绪检查:架构、安全、容量、观测、发布和运行手册

生产就绪不是“Pod 能启动”的同义词,而是系统在正常负载、依赖异常、节点故障、版本升级和人员误操作下,仍具备可验证的服务目标、可控的故障半径和可恢复的运行路径。

可以把一个工作负载是否生产就绪表示为:

Ready=ASCORPReady = A \land S \land C \land O \land R \land P

其中:

  • AA:Architecture,架构能够承受预期故障;
  • SS:Security,身份、权限、网络和供应链边界明确;
  • CC:Capacity,容量、碎片和故障余量满足目标;
  • OO:Observability,能够发现、定位并量化故障;
  • RR:Release,发布、回滚和数据库变更可控;
  • PP:Procedures,值班人员拥有经过验证的运行手册。

其中任一项缺失,系统都可能在演练或真实事故中表现为“看起来正常,但无法安全运行”。

本文示例使用当前稳定 Kubernetes API,例如 apps/v1batch/v1autoscaling/v2policy/v1networking.k8s.io/v1。具体 Kubernetes 版本、发行版和云厂商可能改变默认行为;生产环境应先执行 kubectl api-resourceskubectl explain 和发行版兼容性检查,不能仅凭博客中的旧 API 版本判断可用性。


一、先定义生产就绪的边界

“高可用”至少有三个不同层次:

  1. 组件高可用:控制面、节点和关键依赖存在冗余。
  2. 服务高可用:业务 Pod 分布在不同故障域,单个实例或节点故障不会让服务失去全部端点。
  3. 恢复能力:发生数据损坏、区域故障或错误发布后,能够在目标时间内恢复到可接受状态。

这三者不能互相替代。例如,三个控制平面节点可以避免单个控制面节点故障,但不会自动保证数据库数据安全;三个副本的 Deployment 如果全部位于同一个节点,也不能抵御节点故障。

生产检查应先把目标写成可验证的条件:

  • 可用性目标:例如月度可用性不低于某个 SLO;
  • 恢复时间目标(RTO):故障发生后多久恢复服务;
  • 恢复点目标(RPO):最多允许丢失多长时间的数据;
  • 最大允许发布影响:例如滚动发布期间可用副本不得低于某个数量;
  • 最大故障域:例如单节点、单可用区或单机房故障是否必须继续提供服务。

没有这些条件时,“生产就绪”只能变成主观印象。


二、架构检查:控制面、数据面和故障域

2.1 请求经过哪些组件

一个典型的 Kubernetes 请求路径如下:

flowchart LR
    U[用户或发布系统] --> API[kube-apiserver]
    API --> AUTH[认证]
    AUTH --> AUTHZ[授权]
    AUTHZ --> ADM[准入控制]
    ADM --> ETCD[(etcd)]
    API --> SCH[kube-scheduler]
    API --> CM[kube-controller-manager]
    SCH --> APIS[API Server]
    CM --> APIS
    APIS --> K1[kubelet]
    K1 --> CRI[容器运行时]
    CRI --> CNI[CNI 网络]
    CNI --> P[Pod]
    P --> S[Service/Ingress]
    S --> CL[客户端]

关键状态变化是:

  1. 用户向 API Server 提交 Deployment。
  2. API Server 完成认证、授权、准入检查,并将期望状态持久化到 etcd
  3. Deployment Controller 创建或更新 ReplicaSet。
  4. ReplicaSet Controller 创建 Pod 对象。
  5. Scheduler 为未绑定节点的 Pod 选择节点,并写入绑定结果。
  6. 目标节点的 kubelet 观察到 Pod,调用容器运行时创建容器。
  7. CNI 配置网络,容器启动并执行探针。
  8. Service EndpointSlice 根据 Pod 的就绪状态更新后端端点。
  9. 流量组件根据 Service、Ingress 或 Gateway 配置把请求转发到可用后端。

因此,“Deployment 已创建”只说明期望状态被接受;“Pod 已运行”只说明容器进程存在;只有就绪探针成功后,Pod 才通常会成为 Service 的流量端点。

2.2 控制面高可用的必要条件

etcd 是 Kubernetes 控制面状态的关键存储,使用 Raft 类共识机制维护成员间的一致日志。集群需要多数成员确认才能提交写入,因此法定人数(quorum)为:

Q=N2+1Q = \left\lfloor \frac{N}{2} \right\rfloor + 1

其中 NN 是 etcd 成员数。允许同时失效的成员数为:

F=N12F = \left\lfloor \frac{N-1}{2} \right\rfloor

因此:

  • 1 个成员:没有容错能力;
  • 3 个成员:可容忍 1 个成员失效;
  • 5 个成员:可容忍 2 个成员失效。

3 个成员并不比 2 个成员“少一个副本”,而是因为 2 个成员失效 1 个后无法形成多数;3 个成员失效 1 个后仍有 2 个成员,可以继续提交写入。

但 etcd 成员不能只在逻辑上有多个,还应分布到独立故障域。把三个成员放在同一台物理机上的三个容器,并不能抵抗该主机故障。跨可用区部署还要权衡网络延迟、分区风险和云厂商控制面实现。

控制面检查至少应验证:

kubectl get nodes -o wide
kubectl get --raw='/readyz?verbose'
kubectl get componentstatuses

/readyz?verbose 可以帮助检查 API Server 的就绪子检查;componentstatuses 在新版本中不应作为完整健康判断依据,因为它是历史接口且不能代表整个控制面状态。生产环境还应从控制面主机或管理网络直接检查 etcd 成员健康、磁盘延迟、数据库大小和备份恢复结果。具体命令取决于自建集群还是云托管集群。

失败路径:如果 etcd 无法形成 quorum,API Server 可能无法持久化新的资源变更;已有节点上的容器未必立即停止,但扩缩容、调度、发布和大多数控制操作会逐渐失效。此时反复执行 kubectl apply 通常不会修复问题,应该保留控制面日志和 etcd 证据,按控制面恢复手册处理。

2.3 数据面和故障域

工作负载至少要明确以下故障域:

  • 节点;
  • 机架或宿主机;
  • 可用区;
  • 区域;
  • 集群;
  • 外部依赖所在的故障域。

topology.kubernetes.io/zone 等拓扑标签常用于调度约束,但标签的真实性依赖云厂商或集群管理员。不要仅因为对象上存在标签,就假设两个值一定代表独立电源或网络故障域。

一个跨可用区的 Deployment 示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: production
spec:
  replicas: 6
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: api
      containers:
        - name: api
          image: registry.example.com/api@sha256:REPLACE_WITH_DIGEST
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: "500m"
              memory: "512Mi"
            limits:
              cpu: "1"
              memory: "1Gi"
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            periodSeconds: 5
            failureThreshold: 2
          livenessProbe:
            httpGet:
              path: /live
              port: 8080
            periodSeconds: 10
            failureThreshold: 3

这里 DoNotSchedule 的含义是:如果不能满足跨区域均衡,Pod 不应被调度,而不是把它挤到已有区域。它可能提高故障时的可用性,也可能导致容量不足时 Pod 长期 Pending。因此必须结合区域容量和故障演练,而不是盲目复制配置。

PodDisruptionBudget 只约束自愿中断,如节点排空或集群维护;它不能阻止节点突然断电、内核崩溃或云区域故障,也不能替代副本数:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api
  namespace: production
spec:
  minAvailable: 4
  selector:
    matchLabels:
      app: api

对于 6 个副本,minAvailable: 4 表示受 PDB 约束的自愿中断最多同时减少 2 个可用 Pod。若使用 maxUnavailable,其计算同样受副本数和控制器行为影响。错误设置为 minAvailable: 100% 可能阻塞节点维护;设置过低则无法达到维护期间的服务目标。


三、安全检查:从“谁能访问”到“能运行什么”

Kubernetes 安全不是单一开关,而是请求、工作负载、网络、镜像、密钥和节点多个边界叠加的结果。

3.1 认证、授权和准入

请求处理顺序通常可以抽象为:

  1. 认证(Authentication):请求者是谁;
  2. 授权(Authorization):该身份能否执行该动作;
  3. 准入控制(Admission):即使有权限,资源是否符合集群策略;
  4. 持久化和执行:对象写入状态存储,并由控制器和节点执行。

RBAC 权限应遵循最小权限。下面的 Role 只允许应用读取同一命名空间中的 ConfigMap:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: api-config-reader
  namespace: production
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["api-config"]
    verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: api-config-reader
  namespace: production
subjects:
  - kind: ServiceAccount
    name: api
    namespace: production
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: api-config-reader

resourceNames 能进一步限制对象名称,但不能解决所有动态资源场景。生产检查应特别关注:

kubectl auth can-i \
  --as=system:serviceaccount:production:api \
  get configmaps/api-config -n production

kubectl auth can-i \
  --as=system:serviceaccount:production:api \
  list secrets -n production

第二条命令的预期结果应通常是 no。不要把 cluster-admin 绑定给应用 ServiceAccount,也不要误以为“只读权限”天然安全:读取 Secret、Pod 环境变量或某些自定义资源可能暴露凭据。

准入控制可以阻止不符合策略的对象,例如禁止特权容器、强制镜像来源、要求资源请求和限制。Pod Security Admission 是 Kubernetes 内置能力,通常通过命名空间标签选择 privilegedbaselinerestricted 等级。启用前应先用 warnaudit 观察现有工作负载,否则可能在发布时突然拒绝大量合法但不合规的 Pod。具体能力和例外以目标 Kubernetes 版本文档为准。

3.2 Pod 安全边界的实际含义

以下字段常常决定容器是否能够突破预期边界:

spec:
  automountServiceAccountToken: false
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: api
      securityContext:
        allowPrivilegeEscalation: false
        capabilities:
          drop: ["ALL"]
        readOnlyRootFilesystem: true

这些配置的含义分别是:

  • automountServiceAccountToken: false:应用不需要访问 Kubernetes API 时,不自动挂载令牌;
  • runAsNonRoot:拒绝以 root 用户运行,但镜像本身必须能以非 root 用户工作;
  • RuntimeDefault:使用运行时默认 seccomp 配置;
  • allowPrivilegeEscalation: false:限制通过 setuid 等机制提升权限;
  • 丢弃 Linux capabilities:减少进程可用的内核权限;
  • 只读根文件系统:应用必须把临时文件写到显式挂载的可写卷。

边界:Pod 安全上下文不是虚拟机隔离。内核漏洞、特权容器、主机路径挂载、设备映射和不安全的运行时配置都可能扩大影响范围。需要配合节点加固、补丁、运行时检测和网络隔离。

3.3 网络策略不是默认防火墙

NetworkPolicy 只有在网络插件支持并正确实现时才生效。它通常是命名空间和 Pod 级别的允许规则;没有策略的命名空间在许多插件中默认保持开放。

一个默认拒绝入口和出口的策略:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

应用随后需要显式允许 DNS、入口网关和数据库等依赖。例如,只允许同命名空间中带有 role: gateway 的 Pod 访问 API 的 8080 端口:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-ingress
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes: ["Ingress"]
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: gateway
      ports:
        - protocol: TCP
          port: 8080

常见失败表现是:Pod 本身健康,但无法解析 DNS、访问数据库或接收网关流量。诊断必须从 Pod 内部同时检查 DNS、路由、端口和策略,而不能只看 kubectl get pod

3.4 镜像和 Secret

镜像应尽量使用不可变摘要:

image: registry.example.com/api@sha256:...

标签如 latest 或可变的 v1.2.3 不能证明每次部署使用相同字节。供应链检查还应覆盖:

  • 镜像构建来源和构建权限;
  • 漏洞扫描及其豁免记录;
  • 签名验证;
  • 基础镜像更新;
  • Registry 凭据轮换;
  • 运行时禁止从非批准仓库拉取镜像。

Kubernetes Secret 默认是 Base64 编码,不等于加密。生产环境应确认 API Server 到 etcd 的静态加密配置、访问 RBAC、备份保护和外部密钥系统的轮换流程。任何已经出现在日志、命令行、事件或 Git 仓库中的凭据,都应按“已泄露”处理,而不是只删除对象。


四、容量规划:Request、利用率、碎片和故障余量

4.1 Request、Limit 和实际利用率

  • Request 是调度器为 Pod 选择节点时使用的资源需求,也是 CPU、内存等资源超卖和 QoS 判断的重要输入。
  • Limit 是容器运行时或内核侧的使用上限。CPU limit 通常表现为节流,内存 limit 超过后可能触发 OOMKill。
  • 利用率 是运行时实际消耗,不能替代 request。

调度器通常先检查:

requestsscheduled+requestnewallocatable\sum requests_{scheduled} + request_{new} \le allocatable

其中 allocatable 是节点可分配给 Pod 的资源,不是物理机总资源。系统守护进程、kubelet、容器运行时和操作系统会消耗一部分资源。

查看节点容量:

kubectl describe node NODE_NAME
kubectl get node NODE_NAME -o jsonpath='{.status.allocatable}{"\n"}'
kubectl top nodes
kubectl top pods -A --containers

kubectl top 依赖 Metrics Server,反映的是采集窗口内的使用量,不是历史峰值,也不能单独用于容量决策。

4.2 计算可用容量和故障余量

假设有 3 个可用区,每区 4 个节点,每个节点的可分配内存为 28 GiB。计划在任一区域故障后仍能承载全部工作负载,并保留 20% 运营余量。

总可分配内存:

Ctotal=3×4×28=336 GiBC_{total}=3 \times 4 \times 28=336\text{ GiB}

单区域故障后剩余:

Cafterzone=2×4×28=224 GiBC_{after-zone}=2 \times 4 \times 28=224\text{ GiB}

扣除 20% 余量后的安全容量:

Csafe=224×(10.2)=179.2 GiBC_{safe}=224 \times (1-0.2)=179.2\text{ GiB}

如果所有业务 Pod 的内存 request 总和为 165 GiB,那么在该假设下还剩:

179.2165=14.2 GiB179.2-165=14.2\text{ GiB}

但这只是总量判断。若 Pod 因 topologySpreadConstraints、节点 taint、GPU、架构或本地盘要求不能自由移动,14.2 GiB 可能无法组成足够大的可调度空间。

4.3 碎片导致“总量足够但 Pod Pending”

假设三个节点各有 4 GiB 可分配内存,当前空闲分别为 1 GiB、1 GiB、2 GiB。集群总空闲为 4 GiB,但一个新 Pod 需要 3 GiB request:

  • 总空闲:1+1+2=41+1+2=4 GiB;
  • 最大连续可用节点空间:22 GiB;
  • 因此 Pod 仍无法调度。

这就是碎片。诊断时不能只看总利用率,应检查:

kubectl get pod -A --field-selector=status.phase=Pending
kubectl describe pod PENDING_POD -n NAMESPACE
kubectl describe nodes

describe pod 的 Events 通常会说明是 Insufficient memory、节点选择器不匹配、污点未容忍,还是拓扑约束无法满足。扩容节点数量可能无效,真正需要的是更大节点、重新平衡工作负载、调整 request,或减少过度严格的拓扑约束。

4.4 QoS 和 OOM 的误判

Pod QoS 类别由资源配置决定:

  • Guaranteed:符合条件的容器 CPU 和内存 request、limit 都设置且相等;
  • Burstable:至少有一个 request 或 limit,但不满足 Guaranteed;
  • BestEffort:没有 CPU 和内存 request、limit。

这不是“Guaranteed 永不 OOM”。节点内存压力下,内核仍可能杀死进程;容器超过自身内存 limit 时也可能被 OOMKill。QoS 主要影响驱逐优先级和资源管理行为,不能替代合理的内存上限、缓存控制和负载测试。

对于内存,通常需要把 request 设为稳定工作集加上合理波动,把 limit 设为可接受的峰值,而不是任意设置成极大值。limit 过小会导致频繁 OOM,过大则可能掩盖内存泄漏并增加节点级故障半径。

容量模型还应包含:

  • DaemonSet、系统 Pod 和监控代理;
  • HPA 扩容后的最大副本数;
  • 发布期间 maxSurge 产生的临时副本;
  • 节点排空和故障迁移所需空间;
  • 本地临时存储和 inode;
  • API Server、etcd、云厂商配额;
  • 数据库连接数、消息分区、负载均衡后端数等非 CPU/内存瓶颈。

4.5 压测必须验证故障余量

一次合格的压测至少要记录:

  1. 目标流量、请求分布和数据规模;
  2. CPU、内存、网络、磁盘和连接池;
  3. p50、p95、p99 延迟及错误率;
  4. HPA 扩容延迟和副本上限;
  5. 节点故障、Pod 驱逐、依赖降级后的结果;
  6. 恢复后的积压处理速度。

例如,若队列消费速度为 rcr_c,生产速度为 rpr_p,积压量为 BB,且 rc>rpr_c>r_p,理论清空时间为:

T=BrcrpT=\frac{B}{r_c-r_p}

如果扩容后消费速度只从 100 提升到 120 条/秒,而生产速度为 110 条/秒,积压不会快速恢复,因为净清空速度只有 10 条/秒。这个计算比“CPU 还有 40% 空闲”更接近业务恢复能力。


五、观测检查:必须能回答“发生了什么、影响谁、为什么”

观测性(Observability)不是监控面板数量,而是根据外部输出推断系统内部状态的能力。生产环境至少要形成四类证据:

  • Metrics:时间序列数值,如请求量、错误率、延迟、资源使用;
  • Logs:离散事件和上下文;
  • Traces:跨服务请求的调用链;
  • Events:Kubernetes 对象状态变化的原因线索。

5.1 从 SLI 到告警

SLI 是实际测量指标,例如:

Availability=successful requeststotal requestsAvailability=\frac{successful\ requests}{total\ requests}

若一个月总请求 10 亿次,允许错误预算为 0.1%,则预算为:

109×0.001=10610^9 \times 0.001=10^6

告警不应只针对 Pod 重启次数,而应连接用户影响。例如:

  • 入口 5 分钟错误率超过阈值;
  • p99 延迟持续超过 SLO;
  • 可用端点数低于服务副本目标;
  • HPA 已达到最大副本且延迟继续上升;
  • Pending Pod 持续存在;
  • 节点 NotReady 或磁盘压力;
  • etcd 写入延迟、数据库连接耗尽或消息积压。

单纯使用 CPU 告警会漏掉数据库连接耗尽、证书过期和应用线程池饱和;单纯使用 Pod 状态又可能在用户已经失败时仍显示“Running”。

5.2 探针的生命周期和错误边界

  • startupProbe:应用启动阶段通过前,liveness 和 readiness 可以被延后;适合慢启动应用。
  • readinessProbe:决定是否接收流量,不代表进程一定健康。
  • livenessProbe:失败后 kubelet 可能重启容器,适合检测进程无法自行恢复的状态。

错误示例是把数据库连通性直接写进 liveness。数据库短暂故障时,所有 Pod 同时被重启,形成重启风暴,反而延长恢复时间。数据库依赖更适合影响 readiness 或由应用实现降级,除非进程确实已经无法继续工作。

探针失败的诊断顺序:

kubectl describe pod POD -n NAMESPACE
kubectl logs POD -n NAMESPACE --all-containers --previous
kubectl get endpointslice -n NAMESPACE -l kubernetes.io/service-name=api -o yaml

应区分:

  • 探针请求根本到不了容器;
  • 应用端口未监听;
  • 应用返回非 2xx;
  • 就绪被主动置为 false;
  • Service 没有端点;
  • Ingress 或网关配置错误。

5.3 事件不是审计日志

kubectl get events -A --sort-by=.lastTimestamp

Events 适合解释近期调度、拉镜像、挂载卷、探针和驱逐原因,但会被聚合、过期或丢失,不能作为长期审计证据。需要回答“谁在什么时候修改了 Deployment”时,应使用 API Server 审计日志、Git 记录、发布系统记录和云平台操作日志。

日志必须包含请求 ID、用户或租户标识、版本、错误类别和依赖调用结果;但不能记录访问令牌、密码和完整个人敏感数据。Tracing 的采样策略也要避免在高错误率时丢失关键失败样本。


六、发布检查:期望状态如何安全变成运行状态

6.1 Deployment 滚动更新的中间状态

Deployment 发布新镜像时,控制器通常创建新的 ReplicaSet,并逐步增加新 ReplicaSet、减少旧 ReplicaSet。maxSurge 控制期望副本数之外允许多出的 Pod,maxUnavailable 控制更新期间最多不可用的副本数。

若副本数为 10,配置:

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 2
    maxUnavailable: 1

则更新期间最多可有 12 个 Pod,按控制器的可用性判定,最多允许 1 个副本不可用。实际过程还受启动时间、readiness、调度容量、PDB 和终止宽限期影响,不是一次性替换。

查看发布状态:

kubectl rollout status deployment/api -n production --timeout=10m
kubectl rollout history deployment/api -n production
kubectl get rs,pod -n production -l app=api -o wide

成功标准不是“新 Pod 为 Running”,而是:

  • 新 Pod 通过 readiness;
  • Service 端点包含预期的新实例;
  • 错误率和延迟没有违反 SLO;
  • 旧版本副本按预期减少;
  • 没有持续的重启、拉镜像和探针失败事件。

回滚:

kubectl rollout undo deployment/api -n production
kubectl rollout status deployment/api -n production --timeout=10m

回滚 Deployment 只回退工作负载模板,不会自动回滚数据库 schema、外部配置、消息格式或已经执行的数据迁移。因此数据库变更必须设计为向前兼容,常用顺序是:

  1. 先发布能同时读旧、新 schema 的代码;
  2. 执行兼容的 schema 扩展;
  3. 切换写入路径;
  4. 验证所有旧 Pod 已退出;
  5. 最后再删除旧字段或旧格式。

直接发布“代码和破坏性 schema 删除”会使回滚失去意义。

6.2 Pod 终止不是立即杀进程

删除 Pod 时,通常先设置终止时间,kubelet 执行 preStop(如果存在),向容器发送 SIGTERM,等待 terminationGracePeriodSeconds,超时后发送 SIGKILL。与此同时,端点应从流量路径中移除,但网络传播存在延迟。

应用必须处理:

  • SIGTERM;
  • 停止接收新请求;
  • 等待已有请求完成;
  • 关闭连接池和消费者;
  • 避免 preStop 无限等待;
  • terminationGracePeriodSeconds 足以覆盖最长正常请求。

如果应用不处理 SIGTERM,滚动发布可能表现为大量 5xx;如果优雅退出时间过长,发布则会积压 Terminating Pod,并消耗额外容量。

6.3 金丝雀和发布闸门

金丝雀的核心不是“先部署一个副本”,而是把一小部分流量或实例暴露给新版本,并比较:

  • 错误率;
  • 延迟分位数;
  • 业务转化或任务成功率;
  • 资源使用;
  • 依赖调用失败;
  • 日志异常类型。

Service 按标签选择 Pod,若要进行精确流量比例控制,通常需要网关、服务网格或云厂商流量管理能力;仅靠两个 Service 的副本数并不能保证精确的请求比例,因为连接复用、客户端缓存和负载均衡算法都会影响实际分布。


七、运行手册:按证据、止损、恢复组织操作

运行手册(runbook)不是命令集合,而是针对一个已知故障模式定义:

  1. 触发条件;
  2. 影响判断;
  3. 证据采集;
  4. 止损动作;
  5. 修复动作;
  6. 验证条件;
  7. 回滚路径;
  8. 事后审计和改进。

7.1 Pod 大量 Pending

先区分调度问题和运行时问题:

kubectl get pods -n production
kubectl describe pod POD -n production
kubectl get nodes
kubectl describe nodes

如果 Events 显示 Insufficient cpuInsufficient memory,检查 request 总量、节点 allocatable、碎片和扩容配额。如果显示 taint、affinity 或 topology 约束不匹配,不能简单增加节点,还要确认新节点是否具有所需标签和容忍条件。

止损动作可能是暂时降低非关键工作负载、暂停发布或扩容节点;不应直接删除大量 Pod,因为这会进一步减少可用容量。验证条件是 Pod 进入 Running 且 readiness 成功,并确认业务 SLO 恢复。

7.2 Pod 反复重启

kubectl get pod POD -n production -o wide
kubectl describe pod POD -n production
kubectl logs POD -n production --previous
kubectl get pod POD -n production \
  -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}{"\n"}'

常见原因和证据不同:

  • OOMKilled:检查容器内存 limit、工作集、节点内存压力和最近流量;
  • CrashLoopBackOff:查看 --previous 日志、退出码和启动参数;
  • 探针失败:查看探针路径、端口、启动时间和依赖初始化;
  • 镜像拉取失败:检查 Registry、凭据、节点网络和镜像摘要;
  • 配置错误:比较 Deployment 模板、ConfigMap/Secret 版本和发布记录。

临时增加 liveness 的失败阈值可能减少误杀,但不能修复真正的内存泄漏或启动错误。任何参数调整都应保留变更记录和恢复值。

7.3 证书或凭据疑似泄露

正确顺序通常是:

  1. 确认泄露范围和使用位置;
  2. 立即撤销或轮换凭据;
  3. 更新 Secret 或外部密钥系统;
  4. 重启或触发工作负载重新读取凭据;
  5. 检查审计日志和依赖侧访问记录;
  6. 保留证据并完成事后审计。

仅执行:

kubectl delete secret leaked-secret -n production

通常不够,因为已经运行的进程可能仍持有旧凭据,etcd 备份、日志或 Git 历史中也可能留有副本。轮换后要验证旧凭据确实失效,并确认新凭据已被所有消费者加载。

7.4 控制面或区域故障

控制面不可用时,不要把“无法执行 kubectl”误认为“所有业务已经停止”。已有 Pod 可能继续服务,但故障期间可能无法扩缩容、重调度和发布。

区域或集群级恢复还需要考虑:

  • 控制面和 etcd 状态;
  • Kubernetes 资源清单;
  • PersistentVolume 中的数据;
  • 对象存储、数据库和消息系统;
  • DNS、证书、镜像仓库和身份系统;
  • 外部负载均衡和防火墙;
  • Secret、密钥和轮换状态。

灾难恢复必须通过定期演练证明。仅有 etcd 快照不等于完整恢复,因为业务数据可能位于外部数据库或卷中。恢复演练应测量实际 RTO/RPO,并验证恢复后的 DNS、凭据、网络策略和应用依赖,而不是只确认 API Server 能返回 200


八、检查命令和证据保存

生产检查应产生可审计证据,而不是只在终端人工观察。可以在只读权限下执行:

kubectl version
kubectl api-resources
kubectl get nodes -o wide
kubectl get ns --show-labels
kubectl get deploy,sts,ds -A
kubectl get pods -A -o wide
kubectl get pvc -A
kubectl get events -A --sort-by=.lastTimestamp
kubectl get pdb -A
kubectl get networkpolicy -A

执行这些命令时应注意:

  • kubectl version 的客户端和服务端版本偏差可能导致 API 或字段行为不同;
  • get all 并不包含所有资源,不能作为完整资产清单;
  • 输出可能包含敏感信息,保存前要脱敏;
  • kubectl describe 适合诊断,但输出不是稳定的机器接口;
  • JSONPath、事件和指标采集应记录采集时间、集群标识和命令版本。

生产变更前还应检查:

kubectl diff -f manifests/
kubectl apply --server-side --dry-run=server -f manifests/
kubectl apply -f manifests/

--dry-run=server 会经过 API Server 的字段校验和准入链,但不会持久化对象;它不能证明镜像能拉取、Pod 能调度或应用能通过健康检查。kubectl diff 也不能替代压测和发布演练。


九、最终验收应是“条件通过”,不是“配置存在”

一个工作负载可以在以下条件同时满足时进入生产:

  • 控制面和节点的故障域、quorum、升级和备份恢复路径已验证;
  • 副本、拓扑约束、PDB 与区域故障模型一致;
  • 每个容器有经过测量的 request,内存限制和 OOM 行为已验证;
  • 高峰、发布、节点故障和依赖降级后仍有容量余量;
  • RBAC、ServiceAccount、Pod 安全、网络策略和镜像来源可审计;
  • readiness、liveness、startup 探针分别承担正确职责;
  • 指标、日志、事件、追踪和审计能够关联到一次请求和一次变更;
  • 发布有暂停、金丝雀、回滚和 schema 兼容策略;
  • 值班人员能依据运行手册完成止损、证据采集、恢复和验证;
  • 备份、凭据轮换和灾难恢复演练的结果被记录,而不是只存在于设计文档中。

生产就绪的核心不是把所有配置都“加上”,而是证明每项配置与故障目标之间存在因果关系:为什么需要这个副本数,为什么这个 request 足够,为什么探针不会制造重启风暴,为什么权限不会扩大到集群级,为什么回滚能够恢复,为什么值班人员能在没有原作者参与时完成操作。只有这些关系能够被测试和复现,Kubernetes 工作负载才真正具备生产运行条件。


系列导航与关联阅读

官方资料

本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。