路线图

集群运维

星辉 2026-07-02 阅读 5 min 946 字 路线图
集群运维 封面

概述#

集群建好只是第一步,让集群持续稳定运行才是真正的运维工作。本章聚焦四个日常运维中最高频的场景:节点维护、版本升级、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 不会再被调度到这个节点。

bash
# 标记节点不可调度
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 安全地迁移到其他节点。这是节点维护前最重要的操作。

bash
# 标准 drain 操作
kubectl drain worker-node-1 \
  --ignore-daemonsets \      # 忽略 DaemonSet(它们不会被驱逐)
  --delete-emptydir-data \   # 允许删除使用 emptyDir 的 Pod
  --force \                  # 强制驱逐不受 ReplicaSet 管理的 Pod
  --timeout=300s             # 超时时间

drain 操作的标准流程

text
1. cordon → 禁止新 Pod 调度
2. drain  → 逐个驱逐 Pod,等待新 Pod 在其他节点 Ready
3. 维护   → 执行系统升级/维修
4. uncordon → 恢复调度

执行 drain 时发生了什么

  1. 节点被标记为不可调度(相当于自动做了 cordon)
  2. 对每个 Pod 发送 SIGTERM 信号
  3. 等待 Pod 的 terminationGracePeriodSeconds(默认 30 秒)
  4. 如果超时未退出,发送 SIGKILL 强制终止
  5. 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 会等待或失败
bash
# 如果 drain 卡住,检查是否有 PDB 限制
kubectl get pdb -A

# 查看哪些 Pod 还留在节点上
kubectl get pods --all-namespaces -o wide \
  --field-selector spec.nodeName=worker-node-1

1.4 uncordon — 恢复可调度#

维护完成后,恢复节点到正常状态:

bash
kubectl uncordon worker-node-1

# 确认状态
kubectl get nodes worker-node-1
# NAME            STATUS   ROLES    AGE   VERSION
# worker-node-1   Ready    <none>   30d   v1.28.0

1.5 完整实操示例#

bash
# === 场景: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 升级前的准备#

必须做的三件事

  1. 阅读 Release Notes:每个版本(尤其是跨大版本升级)都要先看官方 Release Notes,确认 breaking changes
  2. 备份 etcd:这是最重要的前置条件(详见第三节),没有备份的升级等于裸奔
  3. 确认版本兼容性:kubeadm 的 kubelet 小版本差不能超过 1(如 kubeadm v1.28 可以管理 kubelet v1.27-1.28)

2.2 kubeadm 升级三步走#

text
Step 1: 升级 kubeadm(管理工具本身)
Step 2: 升级 control plane 节点
Step 3: 升级 worker 节点

Step 1:升级 kubeadm#

bash
# 查看当前版本
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 plan

Step 2:升级 control plane 节点#

bash
# 在第一个 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 节点,逐个执行:

bash
# 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 — 备份#

bash
# 设置 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 定时执行):

bash
#!/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"
bash
# crontab 配置:每天凌晨 2 点备份
0 2 * * * /opt/scripts/etcd-backup.sh >> /var/log/etcd-backup.log 2>&1

3.3 etcdctl snapshot restore — 恢复#

这是最需要冷静的操作。恢复流程取决于集群的具体情况:

在 kubeadm 集群中恢复(单 master)

bash
# 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 nodes

3.4 备份策略#

策略说明
定期备份Cron 每天凌晨执行,保留 7-30 天
升级前必须备份每次 kubeadm upgrade 之前手动备份一次
重大变更前备份大规模资源修改、Namespace 删除等操作前备份
异地存储备份文件同步到 S3/OSS,防止节点故障导致备份丢失
备份验证定期在测试环境恢复一次,确认备份文件可用

四、证书管理与轮换#

K8s 集群通信依赖 TLS 证书,这些证书有有效期。证书过期是集群故障的常见原因之一。

4.1 检查证书过期时间#

bash
# 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 证书轮换#

bash
# 一键轮换所有证书
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 脚本定期检查。


小结#

集群运维的四个核心领域,按重要性和风险排序:

  1. etcd 备份:这是底线中的底线。没有备份就无法恢复,定期备份 + 升级前备份是最低要求
  2. 节点管理:cordon → drain → 维护 → uncordon,记住这个四步流程。drain 时注意 StatefulSet 不会被自动驱逐
  3. 版本升级:遵循 kubeadm 三步走,逐版本升级,先控制面后 worker,逐个节点来
  4. 证书轮换:kubeadm certs check-expiration 定期检查,过期前 renew all

这些操作看起来简单,但每一条都可能在操作失误时导致生产事故。核心原则只有一条:动手之前,先确认有可用的备份