找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1459

积分

0

好友

187

主题
发表于 2026-2-14 06:12:10 | 查看: 32| 回复: 0

凌晨2点,生产环境的告警突然响起,提示一台Kubernetes节点意外宕机。

你的第一反应会是什么?业务是否会中断?运行其上的Pod和数据会不会丢失?需要手动介入迁移吗?

许多人误以为Kubernetes能够完全自动化地处理一切,但当真遇到节点需要下线时,超过80%的人可能并不清楚其底层的完整处理流程。节点优雅下线应该使用drain命令,直接关机更像是在“赌运气”。今天,我们就通过一次实战演练,来对比“主动优雅下线”与“突发宕机”两种场景下,Kubernetes究竟是如何处理的,并掌握正确的操作姿势。

1. 了解 kubectl drain 命令

在进行任何操作前,充分了解工具是关键。kubectl drain 命令用于安全排空一个节点,为维护或下线做准备。

# 使用说明
[root@k8s-node02 ~]# kubectl drain --help
#...
  --ignore-daemonsets=false: #忽略daemonset资源,默认关闭
Ignore DaemonSet-managed pods.
#...
Usage:
  kubectl drain NODE [options]

Use "kubectl options" for a list of global command-line options (applies to all commands).

2. 资源驱逐流程详解

2.1 驱逐前的资源检查

首先,我们查看目标节点 k8s-node02.dinginx.org 上正在运行的Pod,做到心中有数。

[root@k8s-node02 ~]# kubectl get pods -owide
NAME                          READY   STATUS    RESTARTS       AGE   IP            NODE                     NOMINATED NODE   READINESS GATES
stress-test-755885757-64fzq   1/1     Running   11 (55m ago)   11h   10.244.2.90   k8s-node02.dinginx.org   <none>           <none>
stress-test-755885757-pzhsq   1/1     Running   11 (38m ago)   11h   10.244.2.92   k8s-node02.dinginx.org   <none>           <none>

2.2 禁止调度(cordon)

在开始驱逐前,先使用 cordon 命令将节点标记为不可调度。这可以防止在驱逐过程中有新的Pod被调度到该节点上。

kubectl cordon k8s-node02.dinginx.org
# 一句话来讲,执行cordon后,节点不再接受新Pod调度,但已有的Pod仍会继续运行。
👉 简单理解:cordon只是“锁上门”,并不“赶走屋里的人”。

2.3 执行驱逐(drain)

现在,执行核心的 drain 命令。我们使用了两个常用参数:--ignore-daemonsets=true 用于忽略DaemonSet管理的Pod(如网络插件、日志收集器),--delete-emptydir-data 表示删除使用 emptyDir 卷的Pod的数据。

[root@k8s-node02 ~]# kubectl drain k8s-node02.dinginx.org --ignore-daemonsets=true --delete-emptydir-data
node/k8s-node02.dinginx.org already cordoned
Warning: ignoring DaemonSet-managed Pods: kube-flannel/kube-flannel-ds-h55jz, kube-system/filebeat-tvxxs, kube-system/kube-proxy-x7g7x, metallb-system/speaker-h95xf
evicting pod metallb-system/controller-66bdd896c6-lz59q
evicting pod kube-system/coredns-7699bffdb6-5crvp
evicting pod default/stress-test-755885757-64fzq
evicting pod default/stress-test-755885757-pzhsq
evicting pod kube-system/coredns-7699bffdb6-zwvkr
evicting pod kube-system/metrics-server-68d7bc6495-fqslz
pod/controller-66bdd896c6-lz59q evicted
pod/metrics-server-68d7bc6495-fqslz evicted
pod/coredns-7699bffdb6-5crvp evicted
pod/coredns-7699bffdb6-zwvkr evicted
pod/stress-test-755885757-pzhsq evicted
pod/stress-test-755885757-64fzq evicted
node/k8s-node02.dinginx.org drained

从输出可以看到,命令依次驱逐了除DaemonSet外的所有Pod,并最终显示节点已排空。这个过程是可控的,Kubernetes会遵循Pod的terminationGracePeriodSeconds设置,允许Pod进行优雅终止。

2.4 驱逐后资源检查

再次检查Pod分布,确认它们已被成功调度到其他可用节点(本例中是 k8s-node01)。

[root@k8s-node02 ~]# kubectl get pods -owide
NAME                          READY   STATUS    RESTARTS   AGE     IP            NODE                     NOMINATED NODE   READINESS GATES
stress-test-755885757-7gkz8   1/1     Running   0          3m14s   10.244.1.83   k8s-node01.dinginx.org   <none>           <none>
stress-test-755885757-fbp9g   1/1     Running   0          3m14s   10.244.1.80   k8s-node01.dinginx.org   <none>           <none>
#查看资源已被成功驱逐至node01节点上

2.5 查看节点状态

此时,节点的状态已变为 Ready,SchedulingDisabled,并且被自动添加了 node.kubernetes.io/unschedulable:NoSchedule 污点,确保了不会有新Pod被调度上来。

[root@k8s-master01 ~]# kubectl get nodes
NAME                       STATUS                     ROLES           AGE    VERSION
k8s-master01.dinginx.org   Ready                      control-plane   137d   v1.35.0
k8s-node01.dinginx.org     Ready                      <none>          137d   v1.35.0
k8s-node02.dinginx.org     Ready,SchedulingDisabled   <none>          137d   v1.35.0
#查看被驱逐的节点被打了污点
[root@k8s-master01 ~]# kubectl describe nodes k8s-node02.dinginx.org |grep Taints
Taints:             node.kubernetes.io/unschedulable:NoSchedule

3. 下线节点

当节点上的负载被安全驱逐后,我们就可以开始物理或虚拟层面的节点下线操作了。

3.1 停止 kubelet 服务

在目标节点上,停止并禁用 kubelet 服务。

[root@k8s-node02 ~]# systemctl disable --now  kubelet.service
Removed /etc/systemd/system/multi-user.target.wants/kubelet.service.
#查看集群状态,node02状态更新为NotReady
[root@k8s-node02 ~]# kubectl get nodes
NAME                       STATUS                        ROLES           AGE    VERSION
k8s-master01.dinginx.org   Ready                         control-plane   137d   v1.35.0
k8s-node01.dinginx.org     Ready                         <none>          137d   v1.35.0
k8s-node02.dinginx.org     NotReady,SchedulingDisabled   <none>          137d   v1.35.0

3.2 从集群中移除节点

如果该节点计划报废或重装,需要从集群控制面移除其信息。请注意,此操作不可逆,且需在节点上执行kubeadm reset等清理操作。

[root@k8s-node02 ~]# kubeadm reset -f #清除节点上的K8s组件
[root@k8s-master01 ~]# kubectl delete node k8s-node02.dinginx.org  #从集群API Server中删除节点信息

3.3 安全建议

对于永久下线且不再使用的节点,强烈建议执行重装系统操作,以彻底清除可能残留的敏感数据(如ServiceAccount token、容器镜像等),避免潜在的数据泄露风险。这属于运维安全的最佳实践。

4. 模拟直接宕机场景

现在,我们回到开头那个令人紧张的凌晨场景:如果节点是直接断电或宕机,Kubernetes又会如何反应?

4.1 模拟宕机

我们在节点上直接执行关机命令来模拟突发故障。

#模拟直接宕机
[root@k8s-node02 ~]# init 0

4.2 观察集群状态变化

在另一个节点(如k8s-node01)上,使用kubectl get -w命令持续观察节点和Pod状态。

[root@k8s-node01 ~]# kubectl get nodes -w
NAME                       STATUS   ROLES           AGE    VERSION
k8s-master01.dinginx.org   Ready    control-plane   137d   v1.35.0
k8s-node01.dinginx.org     Ready    <none>          137d   v1.35.0
k8s-node02.dinginx.org     Ready    <none>          137d   v1.35.0
k8s-master01.dinginx.org   Ready    control-plane   137d   v1.35.0
k8s-node01.dinginx.org     Ready    <none>          137d   v1.35.0
k8s-node02.dinginx.org     NotReady   <none>          137d   v1.35.0
k8s-node02.dinginx.org     NotReady   <none>          137d   v1.35.0
k8s-node02.dinginx.org     NotReady   <none>          137d   v1.35.0

# 观察pod变化,pod状态无变化
[root@k8s-master01 ~]# kubectl get pods -w
NAME                          READY   STATUS    RESTARTS   AGE
stress-test-755885757-7gkz8   1/1     Running   0          45m
stress-test-755885757-fbp9g   1/1     Running   0          45m

# 此时,原节点上的Pod可能仍显示Running状态,但实际上已无法提供服务。
# 最终等待 Node Controller 处理(默认约40秒心跳超时后),节点状态变为NotReady。
# 再过一段时间(取决于Pod驱逐等待时间,默认5分钟),这些Pod的状态会变为Unknown,随后被删除并由其控制器(如Deployment)在其他健康节点上重建。

4.3 底层机制解析

当节点突发宕机时,Kubernetes内部的处理流程是滞后的、被动的:

  1. kubelet 停止发送心跳:节点失联。
  2. Node 状态变为 NotReady:控制面的Node Controller检测到心跳超时(默认40秒)。
  3. Node Controller 标记不可调度:自动为节点添加 node.kubernetes.io/unreachable 污点。
  4. 等待 Pod 驱逐超时:对于NotReady节点上的Pod,会有一个容忍期(默认为5分钟)。
  5. 删除 Pod:容忍期过后,控制面会删除这些Unknown状态的Pod。
  6. 控制器重建 Pod:Pod的控制器(如Deployment)检测到Pod被删除,在其他Ready节点上创建新的Pod副本。

关键点:在长达数分钟的等待期内,原节点上的Pod虽然显示Running,但实际已宕机,这会导致服务中断,除非应用本身具备多副本和跨节点的高可用部署。这正是直接关机风险所在。

5. cordon, drain 与直接宕机对比

为了更清晰地理解三者的区别,可以参考下表:

操作 是否停止调度 是否迁移 Pod 是否可控 对服务影响
cordon 完全可控 无,现有Pod不受影响
drain 完全可控 优雅迁移,近乎零中断
直接关机 ❌ (延迟标记) ✅ (延迟重建) 不可控 服务必然中断,中断时长取决于故障发现与重建时间

总结

通过以上对比演示,我们可以清晰地看到:

  • kubectl drain 是节点计划内维护和下线的标准且推荐的操作。它实现了Pod的优雅驱逐和平滑迁移,是保障云原生应用服务连续性的关键运维动作。
  • 直接关机/宕机 是一种故障场景,Kubernetes的恢复机制是滞后的,会导致明确的服务中断时间窗口,应极力避免在生产环境中发生。

因此,请将“先drain,后操作”作为一条运维铁律。理解这些底层机制,能帮助我们在面对真正的集群运维问题时,做出更从容、正确的决策。希望这篇实战指南能对你有所帮助,如果你在实践过程中遇到其他问题,欢迎在技术社区交流探讨。




上一篇:Spring AI Alibaba实战:5小时重构MultiAgent系统,效率提升10倍
下一篇:AI编程现状观察:从Spotify最佳开发者不写代码到工程师倦怠预警
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-23 11:42 , Processed in 0.599505 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表