集群运维
概述#
集群建好只是第一步,让集群持续稳定运行才是真正的运维工作。本章聚焦四个日常运维中最高频的场景:节点维护、版本升级、etcd 数据保护和证书管理。这些操作都涉及生产环境的"性命攸关"操作,请务必在理解原理后再动手。
一、节点管理三命令:cordon / drain / uncordon#
节点总会有需要维护的时候——升级内核、更换硬件、排查底层故障。K8s 提供了三个命令来安全地管理节点状态,避免维护期间影响业务。
1.1 命令速查#
| 命令 | 作用 | 对现有 Pod 的影响 | 对新 Pod 的影响 |
|---|---|---|---|
kubectl cordon <node> | 标记节点为不可调度 | 不影响已有 Pod | 不再调度新 Pod |
kubectl drain <node> | 驱逐所有 Pod | 安全驱逐所有 Pod | 不再调度新 Pod |
kubectl uncordon <node> | 恢复可调度 | 不影响 | 恢复调度 |
1.2 cordon — 标记不可调度#
cordon 相当于在节点上挂了个"禁止入内"的牌子,之前已经跑在上面的 Pod 不受影响,但新的 Pod 不会再被调度到这个节点。
# 标记节点不可调度
kubectl cordon worker-node-1
# 查看节点状态(SchedulingDisabled)
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# worker-node-1 Ready,SchedulingDisabled <none> 30d v1.28.0
# worker-node-2 Ready <none> 30d v1.28.0适用场景:临时的节点排查、准备进行 drain 操作的前置步骤。
1.3 drain — 驱逐所有 Pod#
drain 会将该节点上的所有 Pod 安全地迁移到其他节点。这是节点维护前最重要的操作。
# 标准 drain 操作
kubectl drain worker-node-1 \
--ignore-daemonsets \ # 忽略 DaemonSet(它们不会被驱逐)
--delete-emptydir-data \ # 允许删除使用 emptyDir 的 Pod
--force \ # 强制驱逐不受 ReplicaSet 管理的 Pod
--timeout=300s # 超时时间drain 操作的标准流程:
1. cordon → 禁止新 Pod 调度
2. drain → 逐个驱逐 Pod,等待新 Pod 在其他节点 Ready
3. 维护 → 执行系统升级/维修
4. uncordon → 恢复调度执行 drain 时发生了什么:
- 节点被标记为不可调度(相当于自动做了 cordon)
- 对每个 Pod 发送 SIGTERM 信号
- 等待 Pod 的
terminationGracePeriodSeconds(默认 30 秒) - 如果超时未退出,发送 SIGKILL 强制终止
- Pod 的控制器(Deployment/StatefulSet 等)在其他节点重新创建
注意事项:
- StatefulSet 的 Pod 不会被自动驱逐:StatefulSet 的 Pod 有"粘性标识",drain 不会自动驱逐它们。需要确认 Pod 在其他节点上重建成功后再继续
- DaemonSet 的 Pod:默认被
--ignore-daemonsets忽略,因为 DaemonSet 在每个节点上都会有一个 - 裸 Pod(不属于任何控制器):需要加
--force才会被驱逐,并且会被直接删除(不会在其他地方重建) - 使用了 emptyDir 的 Pod:需要加
--delete-emptydir-data,数据会丢失 - 有 PDB(PodDisruptionBudget)的 Pod:如果驱逐会导致违反 PDB 限制,drain 会等待或失败
# 如果 drain 卡住,检查是否有 PDB 限制
kubectl get pdb -A
# 查看哪些 Pod 还留在节点上
kubectl get pods --all-namespaces -o wide \
--field-selector spec.nodeName=worker-node-11.4 uncordon — 恢复可调度#
维护完成后,恢复节点到正常状态:
kubectl uncordon worker-node-1
# 确认状态
kubectl get nodes worker-node-1
# NAME STATUS ROLES AGE VERSION
# worker-node-1 Ready <none> 30d v1.28.01.5 完整实操示例#
# === 场景:worker-node-1 需要升级内核 ===
# Step 1: 标记不可调度
kubectl cordon worker-node-1
# Step 2: 驱逐所有 Pod
kubectl drain worker-node-1 \
--ignore-daemonsets \
--delete-emptydir-data \
--timeout=300s
# Step 3: 观察驱逐进度
kubectl get pods -A -o wide | grep worker-node-1 # 应该逐渐减少
# Step 4: 在节点上执行维护(SSH 到节点)
# ssh worker-node-1 "apt upgrade && reboot"
# Step 5: 节点恢复后,解除封锁
kubectl uncordon worker-node-1
# Step 6: 确认节点和 Pod 状态
kubectl get nodes
kubectl get pods -A -o wide | grep worker-node-1 # 新 Pod 开始调度二、版本升级思路#
K8s 的版本升级是一个相对标准化的流程。社区推荐的官方工具是 kubeadm,升级思路是先控制面,后工作节点。
2.1 升级前的准备#
必须做的三件事:
- 阅读 Release Notes:每个版本(尤其是跨大版本升级)都要先看官方 Release Notes,确认 breaking changes
- 备份 etcd:这是最重要的前置条件(详见第三节),没有备份的升级等于裸奔
- 确认版本兼容性:kubeadm 的 kubelet 小版本差不能超过 1(如 kubeadm v1.28 可以管理 kubelet v1.27-1.28)
2.2 kubeadm 升级三步走#
Step 1: 升级 kubeadm(管理工具本身)
↓
Step 2: 升级 control plane 节点
↓
Step 3: 升级 worker 节点Step 1:升级 kubeadm#
# 查看当前版本
kubeadm version
# 确定目标版本(假设从 1.28.x 升级到 1.29.x)
TARGET="1.29.x-1.1" # 具体版本号取决于操作系统包管理器
# Ubuntu/Debian
apt-get update
apt-get install -y kubeadm=1.29.6-1.1
# CentOS/RHEL
yum install -y kubeadm-1.29.6
# 验证 kubeadm 升级成功
kubeadm version
# 查看升级计划(dry-run,不会真的执行)
kubeadm upgrade planStep 2:升级 control plane 节点#
# 在第一个 control plane 节点上执行
kubeadm upgrade apply v1.29.6
# 如果升级其他 control plane 节点
kubeadm upgrade node
# 升级 kubelet 和 kubectl
apt-get install -y kubelet=1.29.6-1.1 kubectl=1.29.6-1.1
systemctl daemon-reload
systemctl restart kubelet
# 验证
kubectl get nodes
# 应该看到 control plane 节点显示新版本Step 3:升级 worker 节点#
对每个 worker 节点,逐个执行:
# 1. 驱逐 Pod
kubectl drain worker-node-1 --ignore-daemonsets --delete-emptydir-data
# 2. SSH 到节点,升级 kubeadm
ssh worker-node-1
kubeadm upgrade node
# 3. 升级 kubelet
apt-get install -y kubelet=1.29.6-1.1
systemctl daemon-reload
systemctl restart kubelet
# 4. 恢复调度
exit # 退出 worker 节点
kubectl uncordon worker-node-1
# 5. 等待 Pod 就绪后再做下一个节点2.3 升级策略要点#
| 要点 | 说明 |
|---|---|
| 必须逐版本升级 | 不能从 1.28 直接跳到 1.30,必须 1.28 → 1.29 → 1.30 |
| 升级前备份 etcd | 这是唯一能保证出问题后恢复的手段 |
| worker 节点逐个来 | 一次只 drain 一个 worker,确认 Pod 迁移正常再继续 |
| 避开业务高峰期 | 即使 Pod 会自动迁移,也有短暂的不可用窗口 |
| 测试环境先验证 | 先在 staging 集群验证升级流程 |
三、etcd 备份与恢复#
etcd 是 K8s 集群的"大脑"——所有集群状态(Pod、Service、ConfigMap、RBAC 等)都以 key-value 形式存储在 etcd 中。如果 etcd 数据丢失,集群基本等于要重建。
3.1 为什么 etcd 备份是底线#
| 备份场景 | 后果 |
|---|---|
| etcd 数据损坏 | 所有 kubectl 操作失败,但生效 Pod 仍在运行 |
| etcd 数据丢失 | 集群状态全部丢失,只能重建 |
| 误删 Namespace | 该 Namespace 下的所有资源瞬间消失 |
有了 etcd 备份,以上场景都可以恢复到备份时间点的状态。
3.2 etcdctl snapshot save — 备份#
# 设置 etcdctl 环境变量
export ETCDCTL_API=3
export ETCDCTL_ENDPOINTS="https://127.0.0.1:2379"
export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt
export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/server.crt
export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/server.key
# 执行快照备份
etcdctl snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db
# 验证快照完整性
etcdctl snapshot status /backup/etcd-snapshot-20260630.db -w table备份脚本(设为 cron 定时执行):
#!/bin/bash
# /opt/scripts/etcd-backup.sh
set -e
BACKUP_DIR="/data/etcd-backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/etcd-snapshot-${TIMESTAMP}.db"
RETENTION_DAYS=7
export ETCDCTL_API=3
mkdir -p "${BACKUP_DIR}"
etcdctl snapshot save "${BACKUP_FILE}" \
--endpoints="https://127.0.0.1:2379" \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
etcdctl snapshot status "${BACKUP_FILE}" -w table
# 压缩节省空间
gzip "${BACKUP_FILE}"
# 清理 7 天前的旧备份
find "${BACKUP_DIR}" -name "*.db.gz" -mtime +${RETENTION_DAYS} -delete
echo "Backup completed: ${BACKUP_FILE}.gz"# crontab 配置:每天凌晨 2 点备份
0 2 * * * /opt/scripts/etcd-backup.sh >> /var/log/etcd-backup.log 2>&13.3 etcdctl snapshot restore — 恢复#
这是最需要冷静的操作。恢复流程取决于集群的具体情况:
在 kubeadm 集群中恢复(单 master):
# Step 1: 停止 API Server 和 etcd(移出 manifests 目录)
cd /etc/kubernetes/manifests/
mv kube-apiserver.yaml /tmp/
mv etcd.yaml /tmp/
sleep 10 # 等待容器停止
# Step 2: 备份当前数据目录
mv /var/lib/etcd /var/lib/etcd.broken.$(date +%Y%m%d)
# Step 3: 从快照恢复
etcdctl snapshot restore /backup/etcd-snapshot-20260630.db \
--data-dir=/var/lib/etcd \
--name=master \
--initial-cluster=master=https://127.0.0.1:2380 \
--initial-advertise-peer-urls=https://127.0.0.1:2380
# Step 4: 恢复 manifests,kubelet 自动重启
mv /tmp/etcd.yaml /etc/kubernetes/manifests/
mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/
# Step 5: 等待 Pod 启动,验证
watch crictl ps | grep -E "etcd|apiserver"
kubectl get nodes3.4 备份策略#
| 策略 | 说明 |
|---|---|
| 定期备份 | Cron 每天凌晨执行,保留 7-30 天 |
| 升级前必须备份 | 每次 kubeadm upgrade 之前手动备份一次 |
| 重大变更前备份 | 大规模资源修改、Namespace 删除等操作前备份 |
| 异地存储 | 备份文件同步到 S3/OSS,防止节点故障导致备份丢失 |
| 备份验证 | 定期在测试环境恢复一次,确认备份文件可用 |
四、证书管理与轮换#
K8s 集群通信依赖 TLS 证书,这些证书有有效期。证书过期是集群故障的常见原因之一。
4.1 检查证书过期时间#
# kubeadm 一键检查
kubeadm certs check-expiration
# 输出示例:
# CERTIFICATE EXPIRES RESIDUAL TIME
# admin.conf Jun 30, 2025 10:00 UTC 364d
# apiserver Jun 30, 2025 10:00 UTC 364d
# apiserver-etcd-client Jun 30, 2025 10:00 UTC 364d
# ...kubeadm 创建的证书默认有效期是 1 年。
4.2 证书轮换#
# 一键轮换所有证书
kubeadm certs renew all
# 轮换后重启控制面组件(如果是 static pod 方式,kubelet 会自动检测并重启)
# 如果 kubelet 没有自动重启,手动操作:
crictl pods | grep -E "kube-apiserver|kube-controller|kube-scheduler|etcd" | awk '{print $1}' | xargs -I {} crictl stopp {}
# 更新 kubeconfig 中的证书
cp /etc/kubernetes/admin.conf ~/.kube/config生产环境建议:在证书过期前 30 天设置告警,使用监控或 cron 脚本定期检查。
小结#
集群运维的四个核心领域,按重要性和风险排序:
- etcd 备份:这是底线中的底线。没有备份就无法恢复,定期备份 + 升级前备份是最低要求
- 节点管理:cordon → drain → 维护 → uncordon,记住这个四步流程。drain 时注意 StatefulSet 不会被自动驱逐
- 版本升级:遵循 kubeadm 三步走,逐版本升级,先控制面后 worker,逐个节点来
- 证书轮换:kubeadm certs check-expiration 定期检查,过期前 renew all
这些操作看起来简单,但每一条都可能在操作失误时导致生产事故。核心原则只有一条:动手之前,先确认有可用的备份。