在Kubernetes集群运维中,节点管理是最基础却至关重要的操作之一。想象一下这样的场景:凌晨三点,你被警报声惊醒,发现某个工作节点出现硬件故障需要紧急维护。这时候,如何在保证业务连续性的前提下安全地将节点下线?又或者,当需要永久移除一个节点时,如何避免数据丢失和服务中断?这正是cordon、drain和delete三个命令大显身手的时候。
这三个看似简单的命令背后,隐藏着Kubernetes调度系统的精妙设计。它们虽然都能实现"节点不可用"的效果,但适用场景和底层机制却大相径庭。理解它们的差异,就像掌握外科医生的不同手术刀——在正确的时间使用正确的工具,才能让集群运维既高效又安全。
让我们先通过一个直观的对比表格,快速把握这三个命令的本质区别:
| 特性 | cordon | drain | delete |
|---|---|---|---|
| 调度状态 | SchedulingDisabled | SchedulingDisabled | 节点完全删除 |
| 现有Pod处理 | 保持运行 | 优雅驱逐并重新调度 | 强制终止 |
| 管理状态 | 仍受Master控制 | 仍受Master控制 | 从集群中移除 |
| 恢复方式 | uncordon | uncordon | 重新加入集群 |
| 适用场景 | 临时维护 | 计划性维护/升级 | 永久移除节点 |
| 影响范围 | 最小 | 中等 | 最大 |
kubectl cordon <node-name>就像是给节点贴上一个"施工中"的标签。执行后:
bash复制# 查看节点调度状态
kubectl get nodes -o wide
# 输出中的STATUS列会显示"SchedulingDisabled"
提示:cordon特别适合短期维护场景,比如快速检查节点日志或配置文件,完成后只需
kubectl uncordon即可恢复。
kubectl drain则像是专业的搬家团队,它会:
bash复制# 完整drain命令示例
kubectl drain <node-name> \
--ignore-daemonsets \
--delete-local-data \
--force \
--timeout=300s
kubectl delete node是最终的"告别"命令,它会:
bash复制# 删除节点后如需重新加入
kubeadm reset # 在节点上执行
systemctl restart kubelet
drain命令是三个操作中最复杂也最常用的,它的各种参数组合能应对不同的运维场景。
| 参数 | 作用场景 | 风险等级 |
|---|---|---|
--ignore-daemonsets |
处理运行DaemonSet Pod的节点(如kube-proxy) | 低 |
--delete-local-data |
强制删除使用emptyDir卷的Pod | 中 |
--force |
处理不受控制器管理的Pod(如静态Pod) | 高 |
--grace-period |
覆盖Pod配置的优雅终止时间 | 中 |
--timeout |
设置整个drain操作的超时时间 | - |
当遇到常见的drain错误时,可以这样应对:
DaemonSet阻止问题:
bash复制error: cannot delete DaemonSet-managed Pods
解决方案:添加--ignore-daemonsets参数
本地存储问题:
bash复制error: cannot delete Pods with local storage
解决方案:评估后添加--delete-local-data
无控制器Pod问题:
bash复制error: pods not managed by ReplicationController
解决方案:确认后使用--force参数
对于生产环境,建议采用分阶段drain策略:
bash复制# 第一阶段:仅标记不可调度(业务无感知)
kubectl cordon <node>
# 第二阶段:观察Pod分布情况
watch kubectl get pods -o wide
# 第三阶段:分批次驱逐
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
kubectl drain <node> --namespace=$ns \
--ignore-daemonsets \
--pod-selector='app notin (critical-app)' \
--timeout=10m
done
理解节点状态转换对于掌握命令本质至关重要。以下是简化的状态转换图:
code复制[正常节点]
│
├── cordon ──▶ [不可调度] ─── uncordon ──▶ [正常节点]
│ │
│ ├── drain ──▶ [维护中] ─── uncordon ──▶ [正常节点]
│ │
│ └── delete ──▶ [已删除] ── (需重新加入)
│
└── delete ──▶ [已删除]
关键状态说明:
不可调度状态:
维护中状态:
已删除状态:
基于多年集群运维经验,分享几个关键实践要点:
执行drain前务必检查:
集群资源余量:
bash复制kubectl describe nodes | grep -A 5 "Allocated resources"
PodDisruptionBudget状态:
bash复制kubectl get pdb -A
存储卷状态:
bash复制kubectl get pv,pvc -A
对于需要频繁执行的操作,建议封装为脚本:
bash复制#!/bin/bash
# safe_drain.sh
NODE=$1
TIMEOUT=${2:-300}
echo "[$(date)] Starting safe drain procedure for node $NODE"
# Step 1: Cordon first
kubectl cordon $NODE || exit 1
# Step 2: Check cluster capacity
kubectl get nodes -o json | jq -r '.items[] | select(.spec.unschedulable!=true) | .metadata.name' > available_nodes.txt
# Step 3: Drain with safety checks
kubectl drain $NODE \
--ignore-daemonsets \
--delete-local-data \
--force \
--timeout=${TIMEOUT}s \
--pod-selector='pod-template-hash' || exit 1
echo "[$(date)] Drain completed successfully for node $NODE"
建立完善的监控机制:
关键指标监控:
自动化回滚:
bash复制# 当异常指标触发警报时自动执行
kubectl uncordon $NODE
kubectl rollout restart deployment -n affected-namespace
在实际运维中,总会遇到一些教科书上没讲的特殊情况。
对于StatefulSet管理的Pod,需要额外注意:
bash复制# 获取StatefulSet Pod列表
kubectl get pods -l app=mysql -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | tac
# 逐个drain节点上的Pod
for pod in $(...); do
kubectl delete pod $pod --grace-period=60
kubectl wait pod $pod --for=delete --timeout=300s
done
当节点与控制平面失去连接时:
bash复制kubectl patch node $NODE -p '{"status":{"conditions":[{"type":"Ready","status":"False"}]}}'
对于超过100个节点的集群:
调整API Server参数:
yaml复制# /etc/kubernetes/manifests/kube-apiserver.yaml
- --max-mutating-requests-inflight=500
- --max-requests-inflight=1500
使用并行处理:
bash复制parallel -j 5 kubectl cordon ::: node{1..50}
考虑使用Cluster API进行机器级管理
无论使用哪种隔离方式,完善的恢复流程都同样重要。
标准恢复流程:
节点维护完成后执行:
bash复制kubectl uncordon $NODE
验证节点状态:
bash复制kubectl get node $NODE -o wide
kubectl describe node $NODE | grep -i taint
平衡Pod分布:
bash复制kubectl top pod -A | sort -k3 -n
对于已删除的节点,重新加入需要:
节点端清理:
bash复制kubeadm reset --force
rm -rf /etc/kubernetes /var/lib/kubelet
重新加入集群:
bash复制kubeadm join <control-plane-host>:<port> --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
验证节点状态:
bash复制kubectl get nodes -w
特别是对于有本地存储的Pod:
检查数据完整性:
bash复制kubectl exec -it $POD -- sh -c 'md5sum /data/*.db'
验证副本同步状态:
bash复制kubectl exec -it $POD -- sh -c 'redis-cli info replication'
监控业务指标24小时
在多年的生产环境运维中,我们发现约70%的节点维护问题都源于对drain命令参数理解不足或忽略了前置检查。特别是在处理有状态服务时,一个看似简单的drain操作可能会引发数据一致性问题。曾经遇到过一个案例:某金融系统在升级节点时,因未正确设置--grace-period导致数据库事务中断,最终引发长达2小时的服务降级。这也正是为什么我们需要深入理解每个命令背后的机制,而不仅仅是记住语法。