1. Kubernetes 节点运维标准化的重要性
在容器化部署成为主流的今天,Kubernetes 作为事实标准的容器编排平台,其节点运维的规范性直接关系到业务连续性。生产环境中节点的上下线操作看似简单,实则暗藏诸多风险点:
- 不当的下线操作可能导致 Pod 被强制终止,引发服务中断
- 新增节点配置不一致会造成集群资源调度不均衡
- 缺乏标准流程容易导致操作遗漏,埋下安全隐患
我经历过一次因节点下线操作不规范导致的线上事故:运维人员直接拔除节点物理机,导致该节点上运行的 23 个 Pod 同时被强制终止,其中包括核心支付服务的 5 个实例。这次事件促使我们建立了完整的 SOP 流程,三年来再未发生过类似事故。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 节点下线标准操作流程
2.1 预检查清单
在执行下线操作前,必须完成以下检查:
-
资源评估:
bash复制
kubectl get pods -o wide --all-namespaces | grep <目标节点>确认该节点运行哪些 Pod,评估迁移影响范围
-
Drain 可行性验证:
bash复制
kubectl drain <节点名> --dry-run --ignore-daemonsets --delete-emptydir-data这个 dry-run 操作可以提前发现可能阻碍 Drain 的问题
-
关键指标检查:
- 集群剩余可调度 CPU/内存资源
- 其他节点的负载情况
- 待迁移 Pod 的副本数配置
2.2 正式下线操作
-
设置节点不可调度:
bash复制
kubectl cordon <节点名>这会阻止新 Pod 被调度到该节点
-
安全驱逐 Pod:
bash复制
kubectl drain <节点名> --ignore-daemonsets --delete-emptydir-data --grace-period=300参数说明:
--ignore-daemonsets:忽略 DaemonSet 管理的 Pod--delete-emptydir-data:清理 emptyDir 数据--grace-period:给 Pod 优雅终止的时间(秒)
-
确认节点状态:
bash复制
kubectl get nodes <节点名> -o wide确保 STATUS 显示为
Ready,SchedulingDisabled
2.3 下线后检查
-
验证所有工作负载已迁移:
bash复制
kubectl get pods -o wide --all-namespaces --field-selector spec.nodeName=<节点名>应该只显示 DaemonSet 管理的 Pod
-
检查集群整体状态:
bash复制
kubectl get componentstatuses kubectl top nodes
3. 节点新增标准操作流程
3.1 前置准备
-
硬件规格标准化:
- CPU/内存配置与现有节点一致
- 磁盘类型和容量统一
- 网络插件兼容性验证
-
系统配置基线:
ini复制# /etc/sysctl.d/k8s.conf 关键配置 net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-iptables = 1 fs.file-max = 2097152 -
软件版本控制:
- kubelet、kube-proxy 版本与集群一致
- 容器运行时版本匹配
- 操作系统内核版本兼容性
3.2 节点加入集群
-
获取加入命令:
bash复制
kubeadm token create --print-join-command输出示例:
bash复制kubeadm join 10.0.0.100:6443 --token abcdef.0123456789 \ --discovery-token-ca-cert-hash sha256:xxxxxx -
执行加入操作:
- 在新节点上以 root 执行上一步获取的命令
- 观察 kubelet 日志:
bash复制
journalctl -u kubelet -f
-
验证节点状态:
bash复制
kubectl get nodes <新节点名>等待 STATUS 变为
Ready
3.3 节点标签与污点配置
-
设置节点标签:
bash复制
kubectl label nodes <节点名> disktype=ssd topology.kubernetes.io/zone=zone-a -
配置污点(可选):
bash复制
kubectl taint nodes <节点名> dedicated=special-user:NoSchedule -
验证调度能力:
bash复制
kubectl describe node <节点名> | grep -A10 Allocatable
4. 关键问题排查指南
4.1 常见下线问题
问题1:Pod 无法被驱逐
现象:
code复制error: cannot delete Pods with local storage (use --delete-emptydir-data to override)
解决方案:
- 确认 Pod 是否使用 emptyDir
- 添加
--delete-emptydir-data参数 - 对于有状态服务,确保已配置适当的 PDB(PodDisruptionBudget)
问题2:DaemonSet Pod 阻止 Drain
处理方案:
- 必须添加
--ignore-daemonsets参数 - 对于需要下线的 DaemonSet Pod,先修改 DaemonSet 配置
4.2 常见新增节点问题
问题1:kubelet 无法注册
检查步骤:
- 验证节点与 master 的网络连通性
- 检查 token 是否过期(默认24小时)
- 查看 kubelet 证书配置
问题2:节点处于 NotReady 状态
诊断命令:
bash复制journalctl -u kubelet | grep -i error
kubectl describe node <节点名>
常见原因:
- 容器运行时未启动
- CNI 插件配置错误
- 内核参数未正确设置
5. RBAC 权限控制实践
5.1 最小权限原则
为节点运维操作创建专用 Role:
yaml复制apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: kube-system
name: node-maintainer
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "patch", "update"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "delete"]
- apiGroups: ["apps"]
resources: ["daemonsets"]
verbs: ["get", "list"]
5.2 服务账号绑定
yaml复制apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: node-maintainer-binding
namespace: kube-system
subjects:
- kind: ServiceAccount
name: node-ops
namespace: kube-system
roleRef:
kind: Role
name: node-maintainer
apiGroup: rbac.authorization.k8s.io
6. 自动化运维方案
6.1 使用 Cluster API
对于大规模集群,推荐使用 Cluster API 管理节点生命周期:
- 定义 MachineDeployment:
yaml复制apiVersion: cluster.x-k8s.io/v1beta1 kind: MachineDeployment metadata: name: worker-nodes namespace: default spec: replicas: 10 selector: matchLabels: cluster.x-k8s.io/deployment-name: worker-nodes template: spec: clusterName: production infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: AWSMachineTemplate name: worker-template
6.2 自定义控制器方案
实现节点自动修复的控制器逻辑:
go复制func (r *NodeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var node corev1.Node
if err := r.Get(ctx, req.NamespacedName, &node); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
if node.Spec.Unschedulable && isSafeToDelete(node) {
if err := r.deleteNodeResources(ctx, node); err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
7. 监控与告警配置
7.1 关键监控指标
-
节点状态变化:
promql复制changes(kube_node_status_condition{condition="Ready",status="true"}[15m]) > 0 -
Pod 驱逐事件:
promql复制count(kube_pod_status_reason{reason="Evicted"} by (namespace,pod)) -
资源压力:
promql复制node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.2
7.2 推荐告警规则
yaml复制- alert: NodeNotReady
expr: kube_node_status_condition{condition="Ready",status="false"} == 1
for: 5m
labels:
severity: critical
annotations:
summary: "Node {{ $labels.node }} is not ready"
description: "Node {{ $labels.node }} has been not ready for more than 5 minutes"
8. 变更管理最佳实践
-
变更窗口选择:
- 避开业务高峰时段
- 考虑跨区域部署的时区差异
- 预留回滚时间
-
分批操作策略:
- 每次操作不超过集群节点的 10%
- 间隔至少 15 分钟观察集群状态
-
回滚方案:
- 节点下线失败:取消 cordon 状态
- 节点加入失败:检查日志并重置节点
在实施节点变更时,我们团队遵循"变更三板斧"原则:
- 变更前:完整的 impact assessment
- 变更中:实时监控关键指标
- 变更后:业务功能验证
9. 文档与知识沉淀
建议维护以下文档:
- 节点规格矩阵表(含硬件配置、内核参数等)
- 历史变更记录(含变更时间、操作人、影响评估)
- 典型故障案例库
我们使用 Markdown 格式的 runbook 模板:
markdown复制# 节点下线 Runbook
## 影响服务
- 服务A (3个Pod)
- 服务B (2个Pod)
## 操作步骤
1. [x] 执行预检查
2. [ ] Cordone 节点
3. [ ] Drain 节点
## 验证清单
- [ ] 检查服务A端点
- [ ] 验证监控指标
10. 经验总结与建议
经过三年生产环境实践,我们总结了几个关键数字:
- 30分钟:建议单个节点下线操作总时长不超过这个时间
- 2次:重要变更前至少进行两次预演
- 5项:每次变更必须检查的核心指标数量
对于有状态服务,特别要注意:
- 确保存储卷能正确迁移
- 验证有状态应用的拓扑约束
- 检查客户端重连机制
最后分享一个实用技巧:在 Drain 操作前,可以先对目标节点上的 Pod 执行:
bash复制kubectl get pods --field-selector spec.nodeName=<节点名> -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' > pods-to-migrate.txt
这样可以生成明确的迁移清单,便于后续验证。
