Kubernetes Disk Pressure:产生原因 & 如何处理

理想情况下,Kubernetes 节点的存储资源是无限的,Pod 和容器也不会因为磁盘空间不足而运行失败。但是在现实世界中,节点的资源是有限的。节点无法凭空生成更多的磁盘空间,当节点的可用存储容量不足时,就会出现磁盘压力问题。不过我们可以通过正确的监控和管理,可以防止磁盘压力问题影响workload的性能。

什么是磁盘压力问题

在 Kubernetes 中,节点磁盘压力是指节点的可用磁盘空间不足,导致节点无法运行新的 Pod。当该问题发生时,Kubernetes 会触发节点压力驱逐(node-pressure eviction),将一些 Pod 从节点中驱逐出去,以释放磁盘空间。

重要的是,kubernetes 节点磁盘压力问题通常是逐个节点发生的。换句话说,一个节点的磁盘空间不足,不会影响其他节点的运行。这是因为 workload 只能使用所在节点的磁盘空间。因此,如果某个Pod开始占用过多的磁盘空间,它也只会使用所在节点的磁盘空间,而不会影响其他节点的运行。

也就是说,如果你部署了很多存储密集型的 Pod,那么大多数的节点甚至所有节点都可能面临磁盘压力问题。

我们还应明确知晓,节点磁盘压力发生在节点持久化存储空间不足时,而不是指由 RAM 提供的临时或短暂存储空间。

常见节点状态解析

磁盘压力是 Kubernetes 中可能影响节点的几种“压力”状态之一。为了更全面地理解节点磁盘压力,下面我们来讨论所有主要的“压力”状态。

内存压力(Memory Pressure)

内存压力是指节点可用内存不足的情况。这可能是由于应用程序存在内存泄漏,导致其随时间推移不断消耗更多内存;也可能仅仅是因为在某个节点上调度了过多的 Pod。

磁盘压力(Disk Pressure)

如前所述,磁盘压力发生在节点存储空间不足时。导致节点磁盘压力的潜在原因有多种,我们将在下文详细探讨。

PID压力(PID Pressure)

PID 压力是指节点上运行的进程过多,导致无法创建新进程。这是因为在 Linux 中,每个进程都会被分配一个唯一的进程标识符(PID)。

允许的最大 PID 数量因 Linux 发行版和配置而异,但通常高于32000才可能发生,这意味着 PID 压力是一个很少见的问题。然而,如果单个节点上运行了极大量的进程,就可能发生这种情况。此外,如果 Linux 中设置了安全规则,限制可同时运行的进程总数,也可能引发 PID 压力;某些 Linux 发行版会实施此类策略,以防止攻击者生成大量非法进程。

CPU不足(Lack of CPU)

节点缺少空闲 CPU (Lack of spare CPU)并不会直接触发 Pressure condition,但可能导致 Kubernetes 对 CPU 进行限流(throttling),从而使workload变慢,直到有更多 CPU 资源可用。

为什么你应该关注 Kubernetes 中的节点磁盘压力?

磁盘空间耗尽会给 Kubernetes 带来多种问题。

Pod驱逐和重新调度

如上所述,Kubernetes 可以将 Pod 从出现磁盘压力的节点上驱逐,然后在其他节点上重新调度。如果 Pod 能被快速重新调度,这可能不是什么大问题。但即便如此,在 Pod 在新节点上重启的过程中,仍可能出现一定的应用停机时间。

还存在一种风险:Kubernetes 无法找到新的节点来承载被驱逐的 Pod,因为没有节点具备足够的 CPU、内存和存储资源来支持它们。在这种情况下,基于这些 Pod 运行的所有应用将一直保持不可用,直到有合适的节点可用为止。

节点性能问题或崩溃

如果通过驱逐 Pod 无法迅速解决节点压力事件,受影响的节点可能因磁盘空间彻底耗尽而完全崩溃。

这是因为运行节点的 Linux 操作系统通常需要磁盘空间来完成诸如向日志文件追加数据、启动新进程等操作。大多数 Linux 发行版会预留一定量的磁盘空间(仅内核可用,应用程序无法使用),作为缓冲区,防止因磁盘空间耗尽而导致崩溃。但如果该缓冲区也被耗尽,操作系统就可能开始失效或运行极其缓慢。

集群性能与稳定性

如果集群中的多个节点都出现磁盘压力,整个集群的性能和稳定性可能会开始下降。

这是因为同时影响多个节点的磁盘压力问题可能导致无法调度新的 Pod(或因磁盘压力而被驱逐的 Pod)。因此,workload将开始宕机或变得响应缓慢。

还存在控制平面(即管理整个集群的核心 Kubernetes 组件)崩溃的风险,尤其是当你将控制平面节点同时配置为工作节点时(也就是说,同一节点既运行控制平面又运行 Pod)。

节点磁盘压力的常见原因

现在我们已经知道了节点磁盘压力的含义和原因,让我们来探讨一下导致它的常见原因。

磁盘压力的根本原因当然是可用磁盘空间不足,但节点之所以开始耗尽未使用的磁盘容量,背后还有若干具体原因。

应用日志耗尽本地存储

导致节点磁盘压力的首要原因之一是应用日志占用了大量磁盘空间。

容器通常会写入日志文件,以记录在运行过程中发生的事件。在大多数情况下,没有任何机制限制日志文件的大小。因此,如果不进行日志轮转(即删除旧日志或将旧日志移出节点以释放空间),日志文件会随着时间的推移变得越来越大。

糟糕的日志逻辑也可能导致日志文件过大,当容器向日志写入的数据超出应有范围时就会发生这种情况。

节点运行了太多Pod

在节点上运行过多的 Pod 也可能导致磁盘压力问题。

默认情况下,Kubernetes 会尝试根据各节点的资源总量以及每个 Pod 声明的资源请求与限制,来评估并调度 Pod。但有时它会计算失误,把超出节点承载能力的 Pod 调度到同一节点上;或者管理员设定的资源限制过高或过低,导致 Pod 被错误地放置。

此外,管理员可能会通过 DaemonSet 手动将 Pod 调度到特定节点。如果他们事先没有评估所选节点是否具备足够存储空间来承载所有待运行的 Pod,就可能导致 Pod 被部署到最终会耗尽磁盘空间的节点上。

存储请求配置不当

Pod 通过 PVC 申请存储,PVC 会绑定到一个 PV 上。这个 PV 可以是跨节点共享的存储(如云盘、NFS),也可以是绑定到特定节点本地磁盘的 Local PV。

如果有太多 Pod 试图共用同一个绑定到 Local PV 的 PVC(此时这多个 Pod 都会调度到同一个节点上),而该 Local PV 提供的存储容量不足以支撑所有 Pod,节点上的存储资源就会被耗尽,从而引发磁盘压力问题。

节点存储配置的改变

如果你修改了运行中节点的存储配置,可能会减少可供workload使用的存储容量。例如,在节点运行期间卸载一块磁盘,节点可提供的总存储空间就会随之减少。

在节点运行期间更改存储配置并不常见;通常你会先排空该节点。但由于需要更换磁盘驱动器等操作,仍可能对存储资源进行修改。

如何检测节点磁盘压力

要检测节点是否出现磁盘压力问题,你可以使用 kubectl describe node <node-name> 命令来查看节点的状态。

在节点状态输出中,你可以查找 Conditions 部分。如果该部分显示 DiskPressure: True,则表示节点当前存在磁盘压力问题。

你也可以使用 kubectl get nodes --show-labels 命令来查看所有节点的标签。如果某个节点的标签中包含 node.kubernetes.io/disk-pressure=true,则表示该节点当前存在磁盘压力问题。

你也可以直接登录到节点上,通过执行df -h命令来检查磁盘状况,该命令会显示节点上所有挂载的文件系统的使用情况,包括可用空间、已用空间和挂载点等信息。你可以根据这些信息来判断节点是否存在磁盘压力问题。

如果节点上的 /dev/sda1/dev/sdb1 等本地磁盘的可用空间不足,就可能导致磁盘压力问题。

如何排查 Kubernetes 中的节点磁盘压力

要排查节点磁盘压力问题,管理员通常应按照以下步骤进行。

1. 确认节点的磁盘压力

首先,登录到出现磁盘压力的节点,确认它确实即将耗尽存储空间。同样,你可以使用以下命令来完成此操作:

df -h

如果输出显示节点仍有不少空闲磁盘空间,那么可能是 Kubernetes 误判存在磁盘压力。此时,应检查节点上存储资源的权限设置,确保 Kubernetes 有权访问所有存储资源。

2. 分析Pod磁盘使用情况

确认节点确实存在磁盘压力问题后,深入排查 Pod 是如何使用磁盘资源的。

要完成此操作,请运行以下命令以获取节点上每个运行中的 Pod 的更多信息:

kubectl describe pod <pod-name> -n <namespace>

在输出中,查找 Volume 部分,即可查看 Pod 正在使用的 PVC(如果有)。

有了这些信息,你就可以查看映射到该 PVC 的存储资源,弄清楚里面到底存放了哪些数据。例如,如果发现其中存在体积巨大的日志文件,那就表明磁盘压力很可能是容器写入了过量日志所致。

3.分析其他磁盘使用情况

除了 Pod 占用的磁盘空间外,节点上还可能存在其他 Kubernetes 组件占用的存储资源。这些组件包括 kubelet、kube-proxy、containerd 等。通常可以在/var目录及其子目录(例如/var/lib/var/lib/containers)中找到这些组件的存储资源。

要检查这些组件是否占用了过多磁盘空间,请运行以下命令:

du -sh /var/lib/kubelet
du -sh /var/lib/kube-proxy
du -sh /var/lib/containerd

Kubernetes通常会在这些目录中存储容器镜像等数据,这些数据也可能会占用大量磁盘空间并导致节点磁盘压力问题。

如何修复节点磁盘压力问题

修复节点磁盘压力的最佳方法取决于其具体原因。你需要按照刚才介绍的 Kubernetes 排查步骤,先弄清楚问题到底出在哪里。

也就是说,修复节点磁盘压力问题的一般实践包括:

  • 增加存储容量:为节点增加存储容量是解决节点磁盘压力的一种方法。然而,这只有在确实拥有额外存储并将其映射到节点上时才可行,无论是通过直接挂载磁盘,还是使用 NFS 等协议挂载网络存储。同时,你可能还需要重新配置 PVC,以便新磁盘空间能被 Pod 使用。
  • 删除日志文件:删除日志文件是快速释放磁盘空间的方法。如果需要保留日志数据,请务必先将文件复制到外部位置(例如其他节点或网络附加存储)。
  • 删除容器镜像:删除节点本地存储的容器镜像可以释放磁盘空间。
  • 迁移非关键 Pod:停止非关键 Pod(或将它们迁移到其他节点)可以释放出可用磁盘空间。
  • 创建 RAM 盘:RAM 盘是一种看似持久化的本地存储,但实际上由服务器的易失性内存(RAM)提供。换句话说,RAM 盘允许你把内存“借”出来当作磁盘空间,临时扩充节点的存储容量。缺点是可用 RAM 会减少,可能引发内存压力;一旦系统突然掉电,RAM 盘里的数据会永久丢失。因此,RAM 盘只能作为缓解磁盘压力的短期手段,不宜长期依赖。
  • 调整资源限制:如果 Pod 的资源限制导致其被调度到不合适的节点,或导致过多 Pod 被调度到同一节点,请调整这些限制。

总结

节点磁盘压力问题是 Kubernetes 中常见的性能问题之一。通过监控节点状态、分析 Pod 磁盘使用情况以及检查其他组件占用的存储资源,管理员可以快速定位并解决磁盘压力问题。