Kubernetes 集群节点生命周期管理实战:从扩容、调度管控到证书续期
适用场景:基于 kubeadm 部署的生产级 K8s 集群(尤其适用于大规模 GPU / AI 计算集群)
操作风险:节点摘除涉及 Pod 驱逐,证书续期涉及控制面重启,务必在维护窗口执行
核心经验:多轮扩容必有 token 管理痕迹;证书续期必须所有 Master 节点都执行
一、背景与典型运维场景
在 AI/GPU 集群的日常运维中,节点的生命周期操作远比普通业务集群频繁:
- 多轮扩容:业务增长导致需要分批加入大量 GPU 节点,每批使用不同 token
- 节点维护:硬件故障、内核升级、驱动更新需要安全驱逐 Pod 并禁调度
- 资源标记:不同批次、不同型号(如 A100-SXM4-80GB)的 GPU 需要精细化打标
- 证书过期:kubeadm 集群运行一年后 API Server 证书过期,导致节点 NotReady
下面把实际用过的命令和手法按场景拆解。
二、节点加入:多轮扩容的正确姿势
2.1 标准 join 命令
bash
kubeadm join api.k8s.local:8443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
2.2 多轮扩容的 token 管理
生产环境中经历过多次扩容,每轮都会生成新的 token。当 token 过期(默认 24 小时)或丢失时,需要在 Master 上重新生成:
bash
生成新 token
kubeadm token create
获取 discovery-token-ca-cert-hash(如果没有现成的)
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | \
openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex
💡 经验:大规模扩容时建议将 join 命令、token、hash 值按批次记录到运维文档中,方便回溯。
三、节点调度管控:cordon / drain / uncordon
3.1 禁止调度(Cordon)
维护前先让节点不再接收新 Pod:
bash
#单节点
kubectl cordon <node-name>
#批量禁调度所有节点
kubectl cordon $(kubectl get nodes -o name)
#按 label 选择器批量禁调度
kubectl cordon -l gpu_type=nvidia_a100-80gb
3.2 安全驱逐(Drain)
bash
节点下线或重启前的标准操作,驱逐现有 Pod 并确保服务不中断:
kubectl drain <node-name> \
--delete-local-data \
--force \
--ignore-daemonsets
参数说明:
| 参数 | 作用 |
|---|---|
--delete-local-data |
删除依赖本地存储的 Pod(如 emptyDir) |
--force |
强制驱逐不属于 RC/RS/DaemonSet 的裸 Pod |
--ignore-daemonsets |
忽略 DaemonSet 管理的 Pod(如 kube-proxy、日志采集) |
3.3 恢复调度(Uncordon)
维护完成后重新开放节点:
bash
kubectl uncordon <node-name>
四、节点标签管理:精细化资源标记
在 GPU/AI 集群中,标签是调度器识别硬件能力的关键依据。以下是实际环境中按批次、按硬件型号打标的完整示例:
#GPU 驱动与型号标记
bash
kubectl label node <node-name> gpu_type=nvidia_a100-80gb
#网络加速能力标记
bash
kubectl label node <node-name> accelerator/rdma=true
集群角色标记(多租户/多集群场景)
bash
kubectl label node <node-name> ggg/role=eflops-gpu
kubectl label node <node-name> ggg/role=ai-cluster-gpu
kubectl label node <node-name> vgpu=true
扩容批次标记(用于按批次运维)
bash
kubectl label node <node-name> kuorong=20240319
标签命名规范建议:
| 维度 | 示例 | 用途 |
|---|---|---|
| 硬件型号 | gpu_type=nvidia_a100-sxm4-80gb |
调度器按卡型分配任务 |
| 网络能力 | accelerator/rdma=true |
识别是否支持 RDMA 高速网络 |
| 业务角色 | role=eflops-gpu |
区分不同业务线的节点池 |
| 运维批次 | kuorong=20240319 |
按扩容批次做批量操作 |
五、节点摘除与删除
节点彻底下线时的标准流程:
bash
1. 先 drain 驱逐(如果节点还在线)
kubectl drain <node-name> --delete-local-data --force --ignore-daemonsets
2. 删除节点对象
kubectl delete node <node-name>
3. 在节点本机执行 kubeadm reset(如果节点还能登录)
kubeadm reset -f
六、kubeadm 证书续期(最容易翻车的维护项)
6.1 检查证书过期时间
bash
kubeadm alpha certs check-expiration
输出会显示所有证书的过期时间,重点关注:
apiserver证书(默认 1 年)apiserver-kubelet-clientfront-proxy-clientetcd-server/etcd-peer/etcd-healthcheck-client
6.2 执行续期
bash
kubeadm alpha certs renew all
6.3 关键提醒:所有 Master 节点都要执行
这是生产环境最常见的坑:只在其中一台 Master 上续期证书,其他 Master 的 API Server 仍然使用旧证书,导致集群出现间歇性 NotReady。
正确流程:
bash
1. 在每台 Master 上分别执行续期
kubeadm alpha certs renew all
2. 重启控制面组件(每台 Master 上)
如果是 static pod 方式(大多数 kubeadm 集群)
直接删除旧 pod 让 kubelet 重建
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/ && sleep 5 && mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/
mv /etc/kubernetes/manifests/kube-controller-manager.yaml /tmp/ && sleep 5 && mv /tmp/kube-controller-manager.yaml /etc/kubernetes/manifests/
mv /etc/kubernetes/manifests/kube-scheduler.yaml /tmp/ && sleep 5 && mv /tmp/kube-scheduler.yaml /etc/kubernetes/manifests/
- 验证
bash
kubectl get nodes
七、踩坑清单
| 现象 | 排查方向 |
|---|---|
kubeadm join 报 token expired |
token 默认 24h 过期,重新 kubeadm token create |
| drain 卡住不动 | 有 Pod 没有 PodDisruptionBudget 或 PDB 限制太严,加 --pod-selector 缩小范围 |
| uncordon 后 Pod 不回来 | 检查是否有其他 taint/toleration 限制,或资源不足 |
| 证书续期后 API Server 起不来 | 检查 /etc/kubernetes/pki 下证书是否真的更新了,确认所有 Master 都执行了 |
| 节点 join 后一直 NotReady | 检查 CNI 插件是否正常、kubelet 是否运行、防火墙是否放行 10250 |
| label 打了不生效 | 确认 label key 格式合规(DNS 子域名格式),检查调度器日志 |
八、小结
K8s 节点生命周期管理的核心经验:
- 扩容有痕:每轮 join 的 token 和节点清单做好记录,方便回溯和排错
- 摘节点先 cordon 再 drain:顺序不能反,参数不能省
- 标签是调度的基石:GPU 型号、RDMA 能力、批次信息全部通过 label 表达
- 证书续期要全量:所有 Master 节点都要执行,且要重启控制面组件