生产集群运维——升级、备份、故障排查与容量规划
把 Kubernetes 集群跑起来是第一步,让它在生产环境中长期稳定运行才是真正的挑战。版本升级、etcd 备份恢复、节点维护、故障排查、容量规划——这些 Day-2 运维场景,每一个都有足以让集群中断服务的操作风险。Kubernetes 为这些场景提供了明确的工具和流程约定,但操作顺序和细节上的失误仍然是生产事故的主要来源。
版本升级是最高风险的操作之一。Kubernetes 的 API 在版本间存在兼容性约束(skew policy),错误的升级顺序——例如先升级 kubelet 而非先升级 control plane——会导致集群进入不一致状态,轻则调度异常,重则 API Server 无法与节点通信。etcd 备份看似简单,但备份时机(必须在升级前)、恢复流程(必须停止 API Server 再恢复)、验证方式(恢复后检查关键对象)如果有任何一步出错,都可能导致数据丢失或集群无法启动。
节点维护(drain + upgrade + uncordon)有 PodDisruptionBudget(PDB)这道安全网,但前提是 PDB 本身配置正确。容量规划需要理解"节点总量"和"可分配量"的差距,以及 etcd 的存储上限对集群规模的约束。本篇把这些场景的关键知识点串联起来,形成可在生产中参考的操作框架。
操作流程全景
1 | |
版本升级:顺序与 Skew Policy
版本偏斜策略(Skew Policy)
Kubernetes 的组件版本兼容性有明确约定(截至 K8s 1.28):
- kubelet 版本最多落后 API Server 3 个次版本(kube-apiserver 1.28,kubelet 最低 1.25)
- kube-controller-manager 和 kube-scheduler 版本不能高于 kube-apiserver
- kubectl 版本与 API Server 最多差 1 个次版本
这意味着升级必须从 control plane 开始,节点(kubelet)跟着升级,而不能反过来。在滚动升级过程中,集群会短暂处于混合版本状态,但只要遵守 skew policy,API Server 能正确处理较旧 kubelet 的请求。
升级前准备
在任何升级操作前,有三件事必须完成:
etcd 快照备份(见后文详细说明)。确认所有 workload 的 PodDisruptionBudget 已正确配置,避免 drain 时超出预期地中断服务。检查 API deprecation:每个 Kubernetes 版本都会废弃部分 API(如 1.22 移除 extensions/v1beta1/Ingress),在升级前运行 kubectl deprecations(或使用 Pluto 工具)扫描集群中使用了即将废弃 API 的对象。
Control Plane 升级
对于 kubeadm 管理的集群:
1 | |
HA 集群中,kube-apiserver 每次只升级一个节点,保持其他节点继续服务请求。API Server 前面通常有负载均衡器(如 HAProxy 或云服务商 LB),升级期间负载均衡器自动把流量路由到健康的 API Server 实例。
节点升级
1 | |
PodDisruptionBudget:保护关键服务
PDB 是 drain 操作的安全阀。它告诉 Kubernetes:“在任何时刻,这组 Pod 中最少要有 N 个处于可用状态(minAvailable),或者最多有 M 个处于不可用状态(maxUnavailable)”。
1 | |
drain 操作通过 Eviction API 驱逐 Pod(而非直接删除),Eviction API 会检查 PDB 约束。如果驱逐某个 Pod 会导致违反 PDB,API Server 返回 429(Too Many Requests),drain 等待并重试,直到 Pod 在其他节点重启并就绪后才继续驱逐。
PDB 配置错误(如 minAvailable 等于总副本数)会导致 drain 永久阻塞。应该把 minAvailable 设为副本数减 1(允许滚动维护),或者使用百分比(如 minAvailable: "80%")。
etcd 备份与恢复
备份操作
etcd 集群有 leader 和 follower。备份必须从 leader 节点执行(或使用 --endpoints 指定任意节点,etcdctl 自动转发到 leader):
1 | |
备份频率建议:生产环境每小时一次,升级前额外执行一次。备份文件应存储到集群外部(S3、NFS、本地备份服务器),避免 etcd 节点故障时备份同时丢失。
恢复操作
etcd 恢复是破坏性操作,必须严格按顺序执行:
1 | |
HA 集群的恢复更复杂:需要在所有 etcd 节点上执行恢复(使用相同的快照,但各自的 --name 和 --initial-advertise-peer-urls 不同),重新建立 etcd 集群 quorum。
故障排查工具链
层次化排查路径
1 | |
常见故障模式
Pod 卡在 Pending:检查节点资源(kubectl describe node 看 Allocatable vs Requests),检查 PVC 是否绑定,检查调度约束(nodeSelector、affinity、taint/toleration)。
Pod 反复 CrashLoopBackOff:kubectl logs --previous 看上一次崩溃前的日志,kubectl describe pod 看退出码(OOMKilled 表示内存超限,exit code 1/2 表示应用错误)。
节点 NotReady:kubectl describe node 看 Conditions 字段,检查 kubelet 状态(systemctl status kubelet),检查容器运行时(systemctl status containerd),检查节点磁盘、内存压力(DiskPressure、MemoryPressure condition)。
容量规划关键指标
Node Allocatable
每个节点的可分配资源不等于节点总资源。kubelet 会预留一部分给操作系统和 Kubernetes 自身组件:
1 | |
kubectl describe node 中的 Allocatable 字段显示实际可供 Pod 使用的资源量。Capacity 是总量。两者差值通常在 200-500m CPU 和 1-2Gi 内存之间(具体取决于 kubelet 配置)。
容量规划应基于 Allocatable,而非 Capacity。一个 4 核 8G 的节点,实际可分配的可能是 3.6 核和 6.5G。
关键指标清单
etcd DB size:etcdctl endpoint status 中的 DB SIZE。etcd 的存储上限默认 2GB(--quota-backend-bytes),超出后集群进入只读模式。对象数量多(大量小对象)或对象本身大(ConfigMap 存了大文件)都可能导致 etcd 膨胀。
Namespace resource quota 使用率:kubectl describe resourcequota -n <namespace> 查看 CPU/内存的已用量和上限。临近上限时新 Pod 无法创建(quota exceeded 错误)。
API Server QPS:kubectl get --raw /metrics | grep apiserver_request_total,高 QPS 下 API Server 响应延迟增大,Informer 的 list-watch 也会受影响。
集群多控制面高可用架构
生产集群的 control plane 通常以 3 节点 HA 部署(奇数个 etcd 节点保证 quorum)。各组件的 HA 机制不同:
etcd 集群通过 Raft 协议选出 leader,读写由 leader 处理,follower 同步。任意少数节点故障,集群仍可服务(3 节点容忍 1 故障,5 节点容忍 2 故障)。etcd 节点间延迟要求严格,推荐 RTT < 10ms,最好同地域部署。
kube-apiserver 是无状态的,多个副本通过负载均衡器(LB)对外提供服务。任一 API Server 故障,LB 自动摘除,其余副本继续服务。etcd 连接由每个 API Server 独立维护。
kube-controller-manager 和 kube-scheduler 使用 leader election(基于 Lease 对象),同一时刻只有一个副本是 active,其余处于待机状态。active 副本故障后,其余副本在 lease 超期(默认 15s)后竞争新 leader,切换期间对应的控制循环暂停。
跨可用区部署考量
三个 control plane 节点分布在三个可用区(AZ),任一 AZ 故障不影响集群可用性。工作节点同样跨 AZ 分布,配合 Pod 的 topologySpreadConstraints(见第 05 篇调度器),确保应用 Pod 也分散在多个 AZ。
etcd 跨 AZ 部署会增加节点间延迟,Raft 写操作需要等待多数节点确认,延迟上升会传导到 API Server 的写操作响应时间。对延迟敏感的集群可以考虑把 etcd 节点限制在同一 AZ(接受单 AZ 故障风险),把 API Server 跨 AZ 部署(接受 etcd 单点)。
变更管理与风险控制
渐进式变更
生产集群的任何变更都应遵循渐进式原则:先在开发/预发环境验证,再在生产环境的一个节点或少量 Pod 上验证,确认无问题后推广。kubeadm 升级支持逐节点操作,正是为了实现这种渐进式验证。
ConfigMap 和 Secret 的变更可以用 Deployment 的 Rolling Update 机制验证:先把新配置挂载到少量 Pod(通过 maxSurge/maxUnavailable),观察一段时间后再继续。对于无法通过 Pod 重启热更新的配置变更(如 etcd 参数、kubelet 配置),需要更谨慎的节点级别操作。
变更记录与回滚
kubectl rollout history deployment myapp 记录 Deployment 的变更历史(最近 10 次,由 revisionHistoryLimit 控制),每次变更对应一个 ReplicaSet。kubectl rollout undo deployment myapp 回滚到上一个版本,--to-revision=N 指定版本号。
对于更大范围的变更(如 Helm upgrade),helm rollback 提供 Release 级别的回滚。对于集群配置变更(如 kubeadm 升级),需要提前备份 etcd 快照,回滚路径是恢复 etcd 快照后重新安装旧版本组件。
可运行实验
1 | |
实验结果映射到 K8s 对象
kubectl cordon 为节点添加 taint node.kubernetes.io/unschedulable:NoSchedule,同时设置 node.spec.unschedulable=true。调度器在 Filter 阶段检查这个标志,不会把新 Pod 调度到被 cordon 的节点。
kubectl drain 通过 Eviction API(/api/v1/namespaces/<ns>/pods/<name>/eviction)请求驱逐每个 Pod,而不是直接 DELETE。Eviction API 会检查 PDB 约束,PDB 违反时返回 429,drain 等待后重试。DaemonSet Pod 被 --ignore-daemonsets 跳过是因为驱逐后它们会立即在同一节点重建,无意义。
etcdctl snapshot save 产生的 .db 文件是 etcd 的 boltdb 数据库快照,包含快照时刻所有 key 的数据。恢复时 etcdctl snapshot restore 从快照创建新的 data-dir,原有的 etcd 成员信息(peer URLs、cluster token)在恢复命令中重新指定,与快照中的成员信息无关。
节点自动修复
云服务商托管集群(GKE、EKS、AKS)通常提供节点自动修复(Node Auto-Repair)功能:定期检测节点健康状态,发现 NotReady 持续超过阈值的节点自动重建。自建集群可以用 Node Problem Detector(DaemonSet)检测节点异常(如内核 OOM、磁盘 I/O 错误),配合 Cluster Autoscaler 或自定义 Controller 实现节点替换。
关键区别:自动修复替换的是节点(VM 重建),不是 Pod。替换过程相当于一次 drain + 删除旧节点 + 新节点加入,PDB 仍然有效。理解这个区别有助于正确设置 PDB 和 terminationGracePeriodSeconds,避免节点替换时服务中断。
关键运维工具补充
Pluto(fairwinds):扫描集群中使用了已废弃或已移除 Kubernetes API 版本的资源,在升级前提前发现兼容性问题。比手动查阅 CHANGELOG 更可靠,CI 中可作为升级前 gate。
Velero:Kubernetes 应用层备份工具,备份范围比 etcd 快照更细粒度——可以只备份特定 namespace 的对象和 PVC 数据,也可以跨集群迁移。与 etcd 快照的区别:etcd 快照是集群级别的全量状态备份,恢复时需要停集群;Velero 支持应用级别的增量备份和在线恢复,两者互补而非替代。
kubent(kube-no-trouble):类似 Pluto,专注于 API deprecation 检测,支持 Helm Release 扫描(检查 Helm Chart 内部使用的 API 版本),输出比 kubectl deprecations 更详细的问题报告。
模式提炼
生产运维的核心是"在不中断服务的前提下完成系统变更"。Kubernetes 为此提供了三层保护:PDB 限制同时不可用 Pod 数量(保护应用 SLO);drain 的 Eviction API 保证 PDB 在节点维护时得到遵守(保护操作安全);etcd 快照提供集群状态的点时恢复能力(保护数据安全)。三层机制叠加,构成生产级运维的安全框架。
升级操作遵循"先备份、再升级上层、最后升级节点"的顺序,每一步都可以暂停和回滚。这不是 Kubernetes 特有的设计,而是所有分布式系统滚动升级的通用原则:保持向后兼容的版本共存窗口,逐步切换,验证后继续。
工程迁移表
| 传统运维场景 | Kubernetes 等价 | 关键差异 |
|---|---|---|
| 数据库主从切换 | etcd leader 选举 + control plane HA | 自动 leader 选举,无需手动切换 |
| 蓝绿部署 | Deployment rollingUpdate + PDB | 声明式,自动处理就绪检查 |
| JVM rolling restart | kubectl rollout restart + PDB | Pod 粒度,跨节点分散 |
| MySQL mysqldump 备份 | etcdctl snapshot save | 集群状态快照,非应用数据备份 |
| 服务器下线维护 | kubectl cordon + drain + uncordon | 自动迁移 Pod,遵守 PDB |
常见误解
误解一:etcd 自动备份
etcd 本身没有内置的自动备份机制。etcdctl snapshot save 是手动操作,需要外部工具(CronJob、备份 Operator、云服务商的 etcd 备份功能)来实现定期自动化。很多在云服务商托管 Kubernetes(EKS、GKE、AKS)的用户不需要关心 etcd 备份(云服务商负责),但自建集群必须自己规划。漏掉备份的集群,在 etcd 数据损坏时没有恢复手段。
误解二:drain 立即完成
drain 是异步操作,受 PDB、Pod 优雅终止时间(terminationGracePeriodSeconds)、Pod 迁移后的 readiness probe 等多个因素影响,可能需要几分钟甚至更长时间。--timeout 参数控制等待上限,超时后 drain 报错,但节点已 cordon(不接收新调度),已驱逐的 Pod 不会回来。需要检查超时原因(通常是 PDB 或长 termination grace period),处理后继续 drain 剩余 Pod。
误解三:所有 kubelet 版本必须和 API Server 完全一致
这是 skew policy 的常见误解。Kubernetes 允许 kubelet 版本最多落后 API Server 3 个次版本(1.28 的集群可以有 1.25 的 kubelet)。这个设计支持节点的滚动升级:在节点升级期间,集群中同时存在旧版本 kubelet 是正常状态,不需要(也不可能)在同一时刻把所有节点同步升级。
练习
-
在 kind 集群中创建一个 Deployment(3 副本),配置 PDB(
minAvailable: 2),尝试 drain 其中一个节点,观察 Pod 迁移过程(kubectl get pods -o wide -w)。然后把 PDB 改为minAvailable: 3,再次尝试 drain,观察阻塞行为,理解 PDB 配置错误的影响。 -
在 control plane 节点上执行 etcd 快照备份(需要有访问 etcd 证书的权限),用
etcdctl snapshot status验证快照的完整性,记录快照中的 key 数量和快照大小。创建一些 ConfigMap 和 Deployment,再备份一次,对比两次快照的差异。 -
用
kubectl describe node查看一个节点的 Capacity 和 Allocatable,计算差值,找出 kube-reserved 和 system-reserved 的配置(通常在 kubelet 的启动参数或配置文件中),理解为什么节点的实际可用资源小于硬件标称值。
系列导航
- 00 核心概念导读
- 01 API Server 与声明式 API
- 02 etcd 与持久化
- 03 控制器模式与 Informer 机制
- 04 Pod 生命周期
- 05 调度器深入
- 06 kubelet 与容器运行时
- 07 网络模型与 CNI
- 08 Service 与 kube-proxy
- 09 Ingress 与 Gateway API
- 10 存储体系
- 11 配置与密钥管理
- 12 RBAC 与安全模型
- 13 资源管理与自动伸缩
- 14 CRD 与 Operator 模式
- 15 Helm 与应用打包
- 16 可观测性
- 17 生产集群运维(本篇)
