以前我们做一个基于 Kubernetes 的管理界面时,没有给节点的 swap 预留监控位。
当时的想法很简单:Kubernetes 节点上的 swap 都要关掉,既然不会用,监控页面也就不用给它留位置了。
最近读到 Kubernetes 官方的一篇文章:《使用节点交换内存扩展 Kubernetes 工作负载》,又想起了这个取舍。
文章聊的是用本地 SSD 做 swap,让同一台节点容纳更多浏览器和 Python 沙箱。这些容器会占着不少内存,却经常在等用户的下一条指令。
读到这里,我的第一反应是:看来当年没留的那个位置,现在还真得补上。
当年为什么觉得用不到 swap
这个想法有它的背景。Linux 节点启用了 swap 时,kubelet 默认会拒绝启动,关掉 swap 长期以来也是搭建 Kubernetes 集群时的常见步骤。
而且 swap 确实会影响性能。进程要访问的数据如果被换到了磁盘上,就得先读回内存。内存紧张时,这种等待可能反复发生,对延迟敏感的服务尤其麻烦。
所以当时做界面,没有特别考虑 swap,我觉得也很正常。只是时间一长,“节点上要关 swap”就很容易被记成“Kubernetes 不需要 swap”。后来再考虑节点监控时,也就顺手把它略过去了。
其实 Kubernetes 对节点 swap 的支持已经推进了好几年:v1.22 时进入 Alpha,到 v1.34 已经 GA。AI 沙箱的需求,让这项能力又受到更多关注。
容器在等,内存还占着
说起 AI 基础设施,首先想到的往往是 GPU。但智能体干活的时候,还可能需要启动浏览器、跑 Python,或者在隔离沙箱里执行外部代码。这些环境也会带来不少 CPU 和内存开销。
这类容器有个特点:忙的时候需要不少内存,忙完之后又不能马上退出,因为还要保留会话和执行状态,等下一次交互。
一个两个还好,会话多了,等待中的容器也会占掉很大一部分内存。节点的 CPU 可能还没用满,RAM 已经不够了。
但进程保留着这些数据,不代表它一直在访问这些数据。等待期间,有些内存页可能很久都不会被碰到。
swap 在这里就有用了。Linux 可以把暂时不用的内存页换出去,把 RAM 腾给其他 Pod。等原来的会话再次访问这些数据时,再读回来。应用还是照常访问内存,换页由操作系统处理。
当然,读回来需要时间。能不能接受这点等待,就得看具体业务了。
能多放些 Pod,但也有代价
原文用本地 SSD 做 swap,测试了编译任务、浏览器和 Python 沙箱。比较显眼的一个结果是,gVisor Python 沙箱从 80 个会话增加到了 240 个,容量变成了原来的三倍。
不过文章里也有一个值得一起看的结果:编译任务的 RAM 限额压到 300 MB 时表现不错,再压到 200 MB,执行时间就增加了超过 40%。作者的解释是,这时连活跃工作集都被迫进入了 swap。
这几组数据是文章作者的测试结果。我还没有在自己的环境里做同样的验证,三倍这个数字也不能直接拿来规划别的业务。
现在的条件确实比以前好了。cgroup v2 能分别核算和限制内存与 swap,本地 NVMe SSD 也比机械磁盘快得多。但经常要用的数据如果不断被换出去、再读回来,性能照样会受影响。
所以我觉得,先看清楚负载的特点更重要:平时有多少会话在等,多少内存暂时用不到,以及这些会话同时忙起来的时候会怎么样。原文里的收益,很大程度上就来自那些可以暂时换出的数据。
监控页面该补些什么
回到我们当年做的管理界面,如果现在要补 swap 监控,我会先放上容量、用量和趋势,但不会只放一个使用率就结束。
毕竟,既然允许工作负载使用 swap,看到它有用量就不一定是坏事。还得知道哪些 Pod 在用、换页是否频繁,以及业务有没有变慢。
| 信息 | 想看清楚什么 |
|---|---|
| 节点 swap 状态、容量与使用量 | 节点是否配置了 swap,还有多少空间? |
| Pod 使用 swap 的策略与实际用量 | 哪些工作负载可以使用,哪些正在使用? |
| swap 换入、换出速率 | 数据是否正在频繁往返于 RAM 和存储? |
| 内存压力、存储延迟与业务延迟 | 换页是否已经影响节点或应用的响应? |
比如一台节点的 swap 使用量一直不低,但里面大多是暂时不用的数据,换入换出也很少;另一台节点用量没那么高,却一直在换页,业务响应也慢了。只看百分比,很容易忽略后面这种情况。
Kubernetes 已经提供了节点、Pod 和容器级别的 swap 指标。kubelet 的 /metrics/resource 端点可以提供下面四项数据,单位均为字节,表示当前用量或上限:
| 指标 | 层级 | 含义 |
|---|---|---|
node_swap_usage_bytes |
节点 | 节点当前的 swap 使用量 |
pod_swap_usage_bytes |
Pod | Pod 当前的 swap 使用量 |
container_swap_usage_bytes |
容器 | 容器当前的 swap 使用量 |
container_swap_limit_bytes |
容器 | 容器允许使用的 swap 上限 |
Pod 指标带有 namespace 和 pod 标签,容器指标还带有 container。有了这些数据,就可以从节点总览一路看到具体容器,知道它用了多少 swap、上限是多少。
有一点接入时要留意:NodeSwap 功能已经 GA,但上面这四个监控指标目前仍标为 ALPHA。 它们还没有稳定指标那样的兼容性保证。接入前要确认自己的集群和采集链路能拿到这些数据,升级时也需要留意指标是否有变化。
这四项指标都是用量或限额。换入、换出速率还需要从节点采集器等数据来源补充,再和延迟放到一起看。
页面上最好也把“没配 swap”“不允许 Pod 用”和“没采到数据”分开显示。这几种情况都写成 0,看着整齐,排查问题时却帮不上忙。
还有些地方不能混在一起
补监控时,配置也得一起看。failSwapOn: false 只是允许 kubelet 在启用了 swap 的节点上运行。要让符合条件的容器使用 swap,还需要配置 LimitedSwap。
另外,调度器目前不会把 swap 当成额外的可调度内存。所以页面上也别把 RAM 和 swap 加起来,直接显示成“节点可用内存”。两者的性能和调度含义都不一样。具体配置可以看官方的 swap 管理文档。
这次读文章,技术本身之外,让我印象比较深的还是那个没留的监控位。当时觉得不会用到,也就没再往下想。
现在有了新的负载,也有了相应的支持,节点监控页面自然得跟着改。真要补起来,我最想先看到的就是:哪个 Pod 在用 swap,以及它开始频繁换页时,业务到底慢了多少。