需求
k8s集群中的node节点需要升级内存,以应对服务迁移、pod扩缩容带来的资源短缺;或者node节点宕机了,这种情况需要关闭node节点进行维护,那么node节点服务器上的pod应该怎么办呢?
默认迁移
当node节点关闭时,k8s集群中不会立即发生自动迁移。如果node节点上的副本数为1,就会出现服务中断。事实上,情况并非如此。等待5分钟后,k8s会自动将宕机节点上的pod迁移到其他节点。
具体流程如下:
# 1. 模拟node节点关闭,stop kubeletsystemctl stop kubelet# 2. 集群状态# 此时停止的节点点处于NotReady状态# kubectl get nodeNAME STATUS ROLES AGE VERSIONk8s-3-217 Ready master 88d v1.18.2k8s -3-218 NotReady none 88d v1.18.2k8s-3-219 Ready none 88d v1.18.2# 3. 监控pod 状态,等待约5 分钟集群开始采取行动# kubectl get pod -n test -o Wide -n test -wNAME 就绪状态重新启动年龄IP 节点指定节点就绪GATEShelloworld-79956d95b4-q7jjg 1/1 运行0 19h 10.244.1.154 k8s-3-218 无nonehelloworld-79956d95b4-q7jjg 1/1 运行0 19h 10.2 44.1.154 k8 s-3-218 none none# 5 分钟后,pod 终止并重建helloworld-79956d95b4-q7jjg 1/1 Termination 0 19h 10.244.1.154 k8s-3-218 none nonehelloworld-79956d95b4-nnlrq 0/1 Pending 0 0s none none无nonehelloworld-79956d95b4-nnlrq 0/1 待处理0S 无K8S-3-219 无NonehellowOrld-79956D95B4-NNLRQ 0/1 Containercause 0 1S 无K8S-219 无Nonehellown 3S 10.244.2.215 K8S-3-219 无NonehellowOn -799 56d95b4-nnlrq 1/1 Running 0 66s 10.244.2.215 k8s-3-219 none none# 4、访问测试:pod重新迁移到其他节点时,服务不可用。 # Curl -X 192.168.3.219:80 Hello.test.cnhtmlHeadtitle503 服务暂时不可用/Title/HeadBody1503 服务暂时不可用LABLE/H1/Centerhrcenternginx/1.17.8/Center# 5、迁移后:正常访问# CURL -X 192.168.3.219:80 您好。 test.cn你好,世界!从上面的过程可以看出,down掉的node节点上的pod会被终止,然后在5分钟后重建,直到pod在新节点上启动并被readiness探针检测到正常并处于1\1运行状态。正式对外提供服务。因此,服务中断时间=5分钟关机时间+重建时间+服务启动时间+Readiness探针检测正常时间。
这里你可能有一个疑问:为什么Pod在5分钟后就开始迁移了?这时候我们就需要涉及到k8s中的Taint(污点)和Toleration(容错),它们是Kubernetes 1.6开始提供的高级调度功能。 Taint 和Toleration 协同工作,防止pod 被分配到不合适的节点。每个节点可以应用一个或多个Taints,这意味着不能容忍Taints 的Pod 将不会被该节点接受。如果将Toleration 应用于pod,则意味着这些pod 可以(但不要求)调度到具有匹配污点的节点上。
我们来具体分析一下:
# 1.检查停止的服务节点的状态# kubelet停止后,node节点自动添加Taints; # kubectl 描述节点k8s-3-218Name: k8s-3-218Roles: noneCreationTimestamp: 2020 年5 月22 日星期五11:36:16 +0800Taints: node.kuber netes.io/unreachable:NoExecute node.kubernetes.io/unreachable3 3360NoScheduleUnschedulable: falseLease: HolderIdentity: k8s-3-218 AcquireTime: 未设置RenewTime: 星期三, 2020 年8 月19 日09:31:22 +0800 Conditions: 类型状态LastHeartbeatTime LastTransitionTime 原因消息---- ------ -------- ---------- --------- --------- ------ ------- NetworkUnavailable False Tue, 18 Aug 2020 09:07:59 +0800 Tue, 18 Aug 2020 09:07:59 +0800 FlannelIsUp Flannel 正在该节点上运行MemoryPressure Unknown Wed , 2020 年8 月19 日09:29:56 +0800 否deStatusUnknown Kubelet 停止发布节点状态。 DiskPressure Unknown 2020 年8 月19 日星期三09:29:56 +0800 2020 年8 月19 日星期三09:32:07 +0800 NodeStatusUnknown Kubelet 停止发布节点状态。 PIDPressure Unknown 2020 年8 月19 日星期三09:29:56 +0800 0 NodeStatusUnknown Kubelet 停止发布节点状态。 Ready Unknown Wed, 19 Aug 2020 09:29:56 +0800 Wed, 19 Aug 2020 09:32:07 +0800 NodeStatusUnknown Kubelet 停止发布节点状态.省略.Events: none# 2. 查看pod 状态# kubectl describe pod helloworld-8565c46 87b-rrfmj- n testName: helloworld-8565c4687b-rrfmj Namespace: testPriority: 0.Node-Selectors: noneTolerations: node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300sEvents3336 0 none此时pod的Tolerations默认容忍度具有相应Taint 的节点节点的时间为300s,超过此时间pod 将被驱逐到其他可用节点。因此,该节点上的所有Pod 将在5 分钟后重新调度,在此期间服务将中断。
既然默认的Pod迁移无法避免服务中断,那么我们是否可以在节点宕机之前手动迁移节点呢?
手动迁移
为了避免等待默认的5分钟,我们还可以使用cordon、drain和uncordor命令来实现节点的主动维护。此时需要以下三个命令:
cordon:将该节点标记为不可调度,后续新的pod不会被调度到该节点,但该节点上的pod可以正常对外服务; dance:将该节点上的pod驱逐到其他可调度的节点上; uncordon:将节点标记为可调度;具体操作流程如下:
# 1.将节点标记为不可调度# kubectl cordon k8s-3-219node/k8s-3-219 cordoned # 查看节点状态,此时219标记为不可调度# kubectl get nodeNAME STATUS ROLES AGE VERSIONk8s-3-217 Ready master 89d v1.18.2k8s-3-218 Ready 无88d v1.18.2k8s-3-219 Ready,SchedulingDisabled 无88d v1.18.2# 2. Deport pod# kubectl dance k8s-3-219 --delete-local-data - -ignore -daemonsets --forcenode/k8s-3-219 已封锁警告: 忽略DaemonSet 管理的Pods: ingress-nginx/nginx-ingress-controller-gmzq6、kube-system/kube-flannel-ds-amd64-5gfwh、kube-system/kube- proxy-vdckkevicting pod kube-system/tiller-deploy-6c65968d87-75pfmevicting pod kube-system/metrics-server-7f96bbcc66-bgt7jevicting pod test/helloworld-79956d95b4-nnlrq# 参数如下: --delete-local- data 删除本地数据,甚至emptyDir也会被删除; --ignore-daemonsets 忽略DeamonSet,否则DeamonSet删除后会自动重建; --force 不带force参数只会删除节点上的ReplicationController、ReplicaSet、DaemonSet、StatefulSet或Job。添加后,所有Pod都会被删除; # 3.检查驱逐,219上的pod将迁移到218。 # kubectl get pod -n test -o WideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATEShelloworld-79956d95b4-gg58c 0/1 Running 0 20s 10.244. 1.165 k8s-3-218 none nonehelloworld-79956d95b4-nnlrq 1/1 Termination 0 7 7m 10.244.2.215 k8s-3-219 none none 此时与默认迁移的区别是pod会先重建,然后终止。此时的服务中断时间=重建时间+ 服务启动时间+ 就绪探针检测正常时间,必须等到1/1 运行服务才会正常。因此,从单个副本迁移时,服务端点是不可避免的。
如何实现平滑迁移?我们继续往下看。
平滑迁移
要实现平滑迁移,需要使用pdb(PodDisruptionBudget),即主动驱逐保护。默认迁移和手动迁移都会导致服务中断,但pdb在节点维护时可以保证不少于一定数量的pod正常运行,从而保证服务可用性。
以helloworld为例,由于只有一份副本,因此需要保证在维护期间迁移完成之前这份副本不会被终止。
# 从218 驱逐到219# 1. 将节点标记为不可调度# kubectl cordon k8s-3-218node/k8s-3-218 cordoned# 2. 创建新的pdbvim pdb-test.yamlapiVersion:policy/v1beta1kind: PodDisruptionBudgetmetadata: name: pdb-testnamespace33 36 0 testspec: minAvailable: 1 selector: matchLabels: app: helloworld# 2. Apply and view status# kubectl apply -f pdb-test.yaml# kubectl get pdb -n testNAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGEpdb-test 1 N/A 0 7s# 3. Eviction # kubectl排出k8s-3-218 --delete-local-data --ignore-daemonsets --forcenode/k8s-3-218已经cordonedWARNING:忽略DaemonSet管理的Pods: ingress-nginx/nginx-ingress-controller-hhb6h,kube-系统/kube-flannel-ds-amd64-pb4d7,kube-system/kube-proxy-rzdcjevicting pod kube-system/tiller-deploy-6c65968d87-ktqmmevicting pod kube-system/metrics-server-7f96bbcc66-6p6wmevicting pod test/helloworld- 79956d95b4-gg58 驱逐pod 'helloworld-79956d95b4-gg58c' 时出错(将在5 秒后重试): 无法驱逐pod,因为这会违反pod 的中断预算。驱逐pod test/helloworld-79956d95b4-gg58c 驱逐pod 'helloworld-79956d95b4-gg58c' 时出错(将在5 秒后重试): 无法驱逐pod,因为这会违反pod 的中断预算。pod/tiller-deploy-6c65968d87-ktqmm 驱逐#At这次,由于只有一份副本,可用的最小值为1,则ALLOWED 为0,因此在一份副本中不会通过pdb 发生驱逐。我们需要先扩容,调整副本数大于1。 # 3.手动调整副本数,设置副本数为2kubectl edit deploy helloworld -n test#再次检查pdb# kubectl get pdb -n testNAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGEpdb-test 1 N/A 1 13m#此时最小可用为1,ALLOWED为1,此时会自动计算数量。 # 4. 驱逐# kubectl duck k8s-3-218 --delete-local-data --ignore-daemonsets --force # 5. 维护后,调整节点为可调度# kubectl uncordon k8s-3-218 这次驱逐,由于helloworld始终维护一个pod提供服务,因此服务不会中断。最后将副本数调整为1并且将node节点调整为可调度,维护完成。
用户评论
k8s节点停机维护太头疼了~ 还是得想办法将pod迁移到其他可用节点.
有17位网友表示赞同!
有没有什么工具可以帮助自动迁移pod呢?直接操作手动有点费劲啊。
有20位网友表示赞同!
我之前遇到过这种情况,后来用kubectl drain命令把pod驱逐到别的地方再重启节点挺管用的.
有13位网友表示赞同!
DRAIN命令要注意哪些参数设置?我试了没效果...
有16位网友表示赞同!
迁移pod的时候要留意资源限制和服务依赖关系啊。
有13位网友表示赞同!
这种维护,最好提前做好预告通知,避免影响业务可用
有11位网友表示赞同!
如果节点停机时间长,需要考虑部署到多租户环境下.
有13位网友表示赞同!
是不是应该定期测试一下pod迁移方案呢?提前预演,万一出意外就能及时解决。
有10位网友表示赞同!
k8s真是个复杂的玩意儿...
有6位网友表示赞同!
这个节点维护策略文档在哪里找啊?
有9位网友表示赞同!
migrate pod, draining node, i need info!
有14位网友表示赞同!
集群升级的时候也要考虑pod迁移的问题吧
有12位网友表示赞同!
提前规划好资源分配,才能顺利进行node维护操作
有11位网友表示赞同!
这个drain命令的使用手册在哪里?
有16位网友表示赞同!
希望能找到一个全自动的pod迁移工具
有12位网友表示赞同!
有没有什么学习资源可以推荐一下?
有14位网友表示赞同!
希望migration能尽量快速,以减少业务受影响!
有10位网友表示赞同!