发布时间:2022年4月29日
在日常的集群维护以及集群中应用升级过程中,经常性会遇到应用升级后 Pod
重启慢/反复重启、节点出现 NotReady
的情况,遇到这些场景时,需要从各个方面排查集群中出现了什么问题。本文会从实际生产中遇到的情况出发,介绍一些在集群问题排查中会经常使用到的命令,通过命令的组合使用,快速的定位并解决问题。
集群中的 worker
节点是一些应用的运行根本,所有应用服务的正常提供依赖于良好的节点状态。当节点状态出现问题时,可能是出现了以下问题。
NotReady
的情况会经常在性能较低的 worker
节点上出现。当大量的 Pod 因为应用亲和性要求或部分节点存在无意义污点而调度在性能较低的 worker 节点时,该节点压力增大会导致 kubelet 无法及时上报节点状态,同时运行在该节点上的应用会出现响应慢甚至无响应情况。
一般当应用出现问题时,我们会通过 kubectl -n namespace get pod -o wide | grep target-pod
命令查看该应用的状态是否正常,-o wide
会在输出 应用名称、容器数量、运行状态 的基础上,输出该应用在集群中的 VIP
以及其所在 worker
节点。
当我们确认应用运行状态正常时,仍然出现响应慢的状态,便可以通过 kubectl -n namespace get node -o wide
命令来查看集群中所有节点(包含 master
以及 worker
节点)的状态。该命令在输出 节点名称、节点状态、节点角色(master/node)、节点运行时间、节点 k8s 版本信息外,会额外输出 节点IP、系统版本、kernel 版本、docker版本信息。
当发现应用所在节点状态为 NotReady
时,可以通过常用的 ssh root@Node-IP
命令登录到目标 worker
节点中。一般在登录 NotReady
的节点服务器时,会出现连接缓慢的情况,需要耐心等待。如果长时间都无法登录节点,请判断节点服务器是否正常运行并尝试硬件重启对应节点。
当成功登录节点服务器后,可以通过 service kubelet status
来查看该节点中 kubelet
服务是否正常运转,此时会出现两种情况:
kubectl
服务停止运行,可以通过 service kubelet start
命令启动响应节点中的 kubelet
服务kubelet
服务正常运转,可以通过 service kubelet restart
命令重启 kubelet
服务一般情况下,通过重启 kubelet
服务操作便可以解决节点 NotReady
的问题,如果节点状态正常后,服务依然响应缓慢,可以继续在该节点中通过 top
命令查看该节点的负载情况,如果出现过高的负载,就需要适当将该节点中的应用调度至其他节点中运行。
在前面我们提到,当节点状态正常(Ready
)但应用仍响应缓慢时,可能是应用所在的节点出现了高负载的情况,此时我们可以通过 kubectl -n namespace top node
命令来查看集群中所有节点的负载情况,该命令会对应 namespace
下的所有节点的 节点名称、CPU性能、CPU占用率、内存大小、内存占用率 输出,如下图
在这些数据里,我们需要关心的时应用所在的 worker
节点状态,在两个性能指标(CPU占用率、内存占用率)中,我们主要关注 CPU 的使用情况,当 CPU 负载过高时就会出现进程调度缓慢甚至卡死的情况,此时我们就需要结合 节点命令与应用资源调度命令 来减轻高负载服务器的压力。
当一个节点出现高负载时,我们首先要做的是阻止更多的应用再调度到该节点中,此时我们就需要用到节点污点的相关命令。
简单来说,污点就是给一个节点打上一个特殊的印记并指明其调度属性,如:不可调度 NoSchedule
、不可调度且踢出相应应用 NoExecute
。
NoSchedule
污点就相当于相亲中的硬性条件,比如男生(worker
节点)不爱洗澡(Taint
,节点的污点),而女生(Pod
)又不能容忍(Toleration
,应用对节点的容忍度配置)一个不爱洗澡的男朋友/老公,那么女生肯定不会和该男生继续向下发展(即 Pod
不会调度在该节点上)。
NoExecute
污点相当于恋爱中的情侣,突然女生(worker
节点)变成了田园女权(Taint
),开始讨厌普信男(正常 Pod
,没有对应容忍度)立刻与男友分手(踢出正在运行的 Pod
),于是之后除了舔狗(添加了对应 Toleration
容忍度)外,其他男生无法靠近其分毫。
NoSchedule
与 NoExecute
效果的区别在于是否立即提出没有对应容忍度的服务 Pod
,在生产环境的节点调度中,我们要谨慎使用 NoExcute
效果的污点,因为其会导致大量正常运行的 Pod
不可控的重新启动,从而导致验证的生产事故。
给一个节点打污点的命令为 kubectl taint node node-name {taint-name}={taint-value}:{taint-effect}
,其中 taint-value
的值并不是必须的。取消目标节点对应污点命令为 kubectl taint node node-name {taint-name}={taint-value}:{taint-effect}-
。更多更详细的污点相关信息,请自行查询 k8s 官方文档。
在实际使用中,因为污点命令较为复杂,我们并不常通过直接给节点添加污点的方式阻止新应用调度在高负载节点上。一般会使用 kubectl cordon/drain node-name
命令来实现该功能。
从本质上来说,kubectl cordon
命令阻止节点调度的实现还是通过污点来实现的,cordon
命令会给相应的 worker
节点打上 node.kubernetes.io/unschedulable:NoSchedule
的污点,这样不能容忍该污点的应用便不会调度在该节点中。
相较于 cordon
命令通过 NoSchedule
污点来实现,drain
命令便与 NoSchedule
相似。被 drain
命令操作过的节点,除了会阻止新应用调度在自身外,还会将自身所有节点全部优雅的(平滑的、滚动的)踢出节点。
如果我们想要恢复被 cordon/drain
操作过节点的正常调度,直接使用 kubectl uncordon node-name
命令即可。
在阻止新的应用调度至高负载节点后,我们就需要将高负载节点中的部分应用移出该节点。相较于节点相关内容,重启应用相关命令将简单的多,我们常用的有这些:
kubectl -n namespace delete pod pod-name
:删除目标应用 Pod
并由 k8s 重新创建新的应用 Pod
,此时新启动的应用会调度在其他节点中kubectl -n namespace rollout restart resource-type resource-name
:重启指定名称的指定类型应用。其中 resource-type
可以为 deployment
/ statefulset
,而 resource-name
便是要重启的 deployment / statefulset
名称。此时 k8s 会重启相应资源并重新调度至新的节点。经过阻止调度与重启应用两个环节,节点状态便会恢复正常,为了防止因为该节点阻止调度状态的存在导致其他节点出现资源紧张出现的
调度死循环,我们需要及时通过 kubectl uncordon node-name
恢复该节点的正常调度。
集群的运行环境维护是一个极为庞大且细致的工作,本文仅是从最常见的问题出发,介绍了一些解决问题的命令与方案。
当集群出现问题时,需要我们熟练的通过各种命令的组合来判断问题的具体成因。对于集群调度逻辑的讨论,本文仅仅涉及到了污点相关内容,实际上在生产中我们还会使用节点亲和性、Pod间亲和性来强制要求 k8s 按照我们的意愿进行应用的调度。
集群环境运维知识的学习是一个长而持久的过程,与大家共勉