资讯动态

Kubernetes节点重启后NotReady?排查swap与kubelet的隐形冲突

发布时间:2026/10/21 3:33:54 来源:尧图企业网站定制
如果你经历过Kubernetes节点在重启之后直接变NotReady大概率第一眼都会怀疑网络插件、容器运行时或者磁盘出了问题。我上周就接了一个这样的告警某节点重启后kubectl get nodes里它的状态一直挂在NotReadykubelet服务起来又崩、崩了又起排查到最后才发现罪魁祸首是系统重启之后/etc/fstab把swap又老老实实挂载了回来。这个坑在Kubernetes搭建阶段其实非常常见尤其是物理机、自制镜像和没有彻底关闭swap的发行版上。今天就把这次完整的排查链路、底层的机制解释和几种可靠的解决方式梳理一遍给同样被重启折腾的人一份可以直接照做的清单。1. 重启后节点失联kubelet罢工的第一现场1.1 故障现象节点起不来集群状态直接变红那次事故发生在凌晨的节点滚动重启之后。我用kubectl get nodes一看目标节点状态是NotReadykubectl describe node里能看到类似Kubelet stopped posting node status的记录。这意味着kubelet已经停止向控制平面上报心跳控制平面在一定时间后就会把这个节点标记为不可用。进入节点后我先看了容器运行时containerd是活的crictl ps也能正常列出已有容器。再看网络物理网卡是通的节点IP也能Ping通。表面上看基础设施层完全正常但kubelet就是起不来——这已经不是“某个Pod挂了”的级别而是节点级故障所有调度到这台节点的Pod都会跟着遭殃。1.2 第一轮怀疑网络插件、运行时还是磁盘遇到节点重启后NotReady大多数人的第一个反应是CNI插件出问题了。我先是检查了calico相关Pod的状态发现它们根本处于ContainerCreating阶段而报错原因是kubelet没有给Pod心跳所以CNI组件也无法正常注册。这就形成了一个鸡生蛋蛋生鸡的局面CNI起不来Pod无法创建但CNI本身也是Pod它起不来是因为kubelet根本没正常起来。接着怀疑容器运行时。我用journalctl -u containerd看日志没有发现明显异常containerd进程稳定运行。又检查了磁盘空间和inodedf -h和df -i都正常。到这里问题基本锁定在kubelet这一层——它自己拒绝工作背后的原因需要从kubelet的日志里找。2. 为什么Kubernetes对swap零容忍机制层面的原因2.1 kubelet的硬性校验和它的历史逻辑Kubernetes从早期版本开始kubelet启动时就会主动检查系统的swap状态。你如果尝试直接用kubeadm init初始化一个开启了swap的节点会在预检阶段看到这样一条报错running with swap on is not supported. Please disable swap这不是Kubernetes团队拍脑袋做的限制而是基于容器编排的底层逻辑。容器的内存模型是有限制的requests表示最低保障limits表示硬上限。当内存达到limits之后容器应当被系统回收或重启而不是被swap悄悄接管。一旦swap开启内存页会被换到磁盘kubelet无法准确判断容器是否达到了真实的内存上限驱逐逻辑也就失去了准确性。我常给团队里的同事打一个比方集群的内存配额就像餐厅的固定餐桌requests是预订的座位limits是餐桌能坐的人数上限。swap是什么呢是在过道里临时加折叠椅。表面上看能多接待几个客人但一旦出现突发客流整个过道都会堵死客人也根本不知道该坐在哪——系统的行为变得不可预期这对基础设施来说是致命的。2.2 swap开启后的连锁后果CPU抖动与延迟不可控除了内存语义被破坏swap还会带来另一个容易被忽略的问题交换分区的读写会让系统CPU和I/O出现间歇性抖动。数据面Pod的延迟指标会突然出现毛刺表现为请求RT偶尔飙升到几秒。这种随机抖动在排查时极其痛苦因为你查网络、查代码、查数据库都可能找不到原因最后才发现是系统在频繁换页。更麻烦的是当内存压力增大时内核线程kswapd会持续占用CPU导致节点整体性能下降。在Kubernetes集群里一个节点上经常混部十几个容器如果swap让内存回收的节奏失控多个容器会同时出现性能劣化最终表现为整个节点的load average异常升高。这也是为什么很多云厂商的默认镜像在部署K8s之前都要专门做一次swap检查——它不是洁癖是长期运维沉淀出来的经验。3. 定位问题的完整排查链路从kubelet日志到swap状态3.1 第一步用kubelet日志撬开真正的报错当你确定问题在kubelet层后第一件事就是拉日志。我用的是这一组命令systemctl status kubelet journalctl -xeu kubelet --no-pager -n 100systemctl status能看到进程的当前状态和最后的退出码journalctl则能拿到详细的报错内容。我看到的日志是这样的关键行Failed to start ContainerManager failed to initialize top level QOS containers: failed to enforce memory QOS: unable to set memory cgroup limit: open /sys/fs/cgroup/... : no such file or directory日志里虽然指向cgroup路径但这只是表象。再往上翻几条就能看到kubelet启动时关于swap的警告这才是cgroup初始化失败的源头系统开了swapkubelet初始化内存QoS容器时cgroup v2的内存语义对swap处理不兼容直接导致启动流程中断。3.2 第二步30秒确认swap的真实状态看到“swap”这个词之后我立刻用三组命令交叉验证free -h swapon --show cat /proc/swapsfree -h能看到整体的swap使用量和总量swapon --show会列出所有激活的交换分区和文件/proc/swaps是内核视角下的swap使用明细。测试结果很明确节点的/dev/sda5分区已经作为swap被激活了——而重启之前我明明执行过swapoff -a把swap临时关掉了。这个发现非常关键重启前关掉swap不等于永久关掉swap。只要系统里存在某种机制在开机时把swap重新挂载那么每次重启都会复现同样的问题。3.3 第三步追查启动阶段谁把swap拉了起来Linux系统中/etc/fstab里如果有swap条目systemd在启动时会根据该条目生成对应的.swap单元并自动激活。所以我接下来检查的就是这个文件grep -i swap /etc/fstab输出果然有内容/dev/sda5 none swap sw 0 0问题到此彻底定位系统启动时按fstab条目重新挂载了swap分区kubelet随后启动发现自己运行在“带swap”的环境直接拒绝工作。这里的排查思路其实可以抽象成一条通用链路先确认kubelet拒绝的根因再确认swap是真被打开了最后找是谁在开机时把它打开的。很多人卡在第一步就直接去重装kubelet结果重启问题照样复现。3.4 排查小结三类常见“swap复活”来源如果只看fstab其实还不够保险。我在不同环境里遇到过三类不同的来源你在排查时可以直接对照来源类型表现方式排查命令fstab静态条目重启后swap自动挂载grep -i swap /etc/fstabsystemd swap单元存在独立的.swap单元文件systemctl list-units --typeswap云初始化脚本初始化阶段重新创建swap文件grep -r swap /etc/cloud/cloud.cfg如果是第一种改fstab就够如果是第二种可能需要处理systemd单元如果是第三种光改当前节点还不行得改镜像模板否则下次重建节点又踩一遍。4. 从临时关闭到永久生效两种可靠的swap处置方案4.1 临时方案swapoff -a只能救急当下最直接的处置是swapoff -a执行后立刻用free -h验证swap变为0然后重启kubeletsystemctl restart kubelet过一两分钟节点就能重新变为Ready。但这个方法有个致命缺陷它只针对当前运行级别生效。只要节点再次重启fstab里的swap条目会再次生效你之前做的所有操作全部白费。4.2 永久方案A注释fstab里的swap条目对于大多数场景最稳定的做法是直接修改/etc/fstab把swap条目注释掉。我习惯用下面这种写法既保留原始配置方便回滚又不会被fstab工具误读cp /etc/fstab /etc/fstab.bak.$(date %F) sed -i /swap/s/^/#/ /etc/fstab第一行先备份第二行把包含“swap”的行在行首加上#注释。然后执行swapoff -a并重启验证。用mount -a检查fstab语法是否正确也是可选的安全措施。这里要特别注意“swap”这个匹配关键字是有讲究的。如果你的fstab里恰好有某个普通目录的路径包含“swap”字样比如/data/myswapdir这个正则就会误伤。更稳妥的匹配方式是匹配\sswap\s或者直接精确匹配行尾的swap选项字段sed -i /^[^#].*\sswap\s/s/^/#/ /etc/fstab4.3 永久方案B用systemd单元兜底有些环境里fstab会被第三方工具重写比如云环境的初始化组件或者某些自研的配置管理脚本。这种情况下你注释掉fstab里的swap跑一次配置同步又会被加回来。我遇到过一次最后选择用systemd单元来兜底。思路是创建一个自定义服务在swap被挂载后立刻把它关闭。具体实现如下cat /etc/systemd/system/disable-swap.service EOF [Unit] DescriptionDisable swap Afterswap.target [Service] Typeoneshot ExecStart/sbin/swapoff -a [Install] WantedBymulti-user.target EOF systemctl daemon-reload systemctl enable --now disable-swap.service这个方案的精髓在于Afterswap.targetsystemd保证在swap挂载完成后才执行关闭动作所以无论哪个模块重新创建了swap这个单元都能在启动序列中把它二次关闭。用这种方式即使fstab被外部工具覆写集群节点的行为依然是安全的。另一个思路是直接mask掉swap相关的单元systemctl mask swap.target但是请注意mask命令会屏蔽掉所有swap挂载逻辑如果某些基础软件依赖swap分区比如休眠功能这会导致对应功能不可用。生产服务器基本都是常年不关机的休眠功能几乎没有意义所以mask方案在我实践过的场景里是安全的。但如果你不确定这台机器有没有特殊用途建议先确认再mask。4.4 验证步骤重启才是唯一的裁判改完配置后不能只看文件内容一定要真实重启一次。我自己的验证流程是这样的记录当前节点名和kubectl get nodes状态reboot重启节点等待3-5分钟后先执行free -h确认swap仍为0执行systemctl status kubelet确认服务处于正常状态确认kubelet上报状态后执行kubectl get nodes查看节点恢复Ready我特别强调一点如果在重启后swap依然存在但kubelet却正常工作了这种情况大概率是你在节点上手动改了kubelet配置让它忽略swap检查。这在测试环境可行但生产环境一定要警惕因为这等于主动放弃了一个重要的稳定性保障。5. 处理swap问题时的几个衍生场景版本差异与特殊配置5.1 新版本Kubernetes的NodeSwap实验特性随着版本演进Kubernetes对swap的态度也在微调。从较新版本开始社区引入了NodeSwap特性门控可以通过--feature-gatesNodeSwaptrue开启配合kubelet配置里的memorySwap字段让kubelet在保留swap的情况下运行。但我要明确一点这个特性在设计上仍然只允许容器使用交换空间来做某些受限场景的缓冲而且默认行为对大多数用户依然是不开启的。我见过好几个团队误以为新版本“支持swap了”就直接开启结果节点在内存压力下出现各种诡异延迟。如果你想尝试这个特性至少要做到两点第一完全理解kubelet配置中memorySwap.swapBehavior两个选项的差异——LimitedSwap只给非Guaranteed的Pod分配交换空间而UnlimitedSwap则完全放开第二先在测试集群跑几周观察/proc/pressure/memory数据确认不会影响核心服务的稳定性。5.2 swap文件的场景分离式交换文件更容易遗漏很多虚拟机上用的是swap文件而不是swap分区比如/swapfile。这类节点的问题在于fstab里的挂载行看起来和普通日志文件很像很容易在排查时被忽略。我的建议是在处理这类节点时除了直接编辑fstab最好还要把swap文件本身移除避免后续误操作导致分区空间被莫名占用swapoff /swapfile rm -f /swapfile执行完rm之前务必确认没有进程还在使用swap文件的内容否则会出现写入失败。一般swapoff成功执行后问题不大但保守起见我还是会先执行一次sync。5.3 只有单节点的测试环境可以适当变通如果你的集群只有一个节点而且是本地开发或学习用的swap的处理可以稍微灵活一些。有些人的机器还要跑其他应用关闭swap会影响本机性能这种情况下你可以不物理关闭swap而是在kubelet的systemd配置里给kubelet进程单独设置MemorySwap相关的隔离参数让kubelet运行在自己的内存组里不受系统整体swap策略影响。这种变通方案在严格的生产集群里不推荐但对于本机折腾Kubernetes的朋友来说确实能少一些对系统其他服务的侵入。前提是你必须知道自己在做什么并且把kubelet的降级风险控制在可接受的范围内。6. 踩过的几个衍生坑重启后问题的隐藏变种6.1 坑1云环境重启后fstab被初始化工具重写我遇到过一次最头疼的情况手工改了fstab、重启验证也通过了但过几天某次多云平台的自动运维操作过后swap又回来了。最后查到原因是云平台的初始化组件在启动时会重新生成fstab把我之前的注释覆盖掉了。这种问题的根治办法不是在单台机器上改文件而是要改镜像模板在自定义镜像的初始化配置里把swap相关的逻辑关闭掉。连带着还要检查系统镜像里是否有自动创建swap文件的定时任务或服务一并在模板层处理干净。6.2 坑2滚动升级时swap被“顺带”激活还有一次更隐蔽的情况某次集群滚动升级过程中升级脚本会先做节点重启但升级流程里有一项是“重新检查系统安全基线”这个检查项会把swap重新打开导致升级后的节点批量变成NotReady。排查时看日志才明白升级脚本的检查项是独立的它不感知Kubernetes对swap的要求。这类问题属于自动化流程层面的疏漏需要你在升级脚本里显式加入“禁用swap”的步骤并且放在重启用例确认之前执行。说白了就是要把swap检查当成集群生命周期管理的一部分而不是只在初始化时做一次。6.3 坑3“杀鸡取卵”——删除swap文件引发的启动异常有一次某同事为了彻底关闭swap不仅删除了fstab条目还把swap文件直接删了。理论上这没问题但如果这台机器的内核参数里设置了resume用于休眠恢复指向这个swap分区重启时操作系统会尝试从swap分区加载休眠镜像找不到之后会卡在启动阶段或进入紧急模式。我后来处理这类问题时都会顺手检查一下/etc/default/grub里的resume参数如果确认不需要休眠功能就把它一并清理干净。这是一个很容易被忽略的联动点值得记到你的排障清单里。6.4 坑4临时容器和InitContainer的内存行为差异做排障的时候不能只盯着节点本身还要注意容器层面的内存行为。swapoff -a之后原有容器如果是运行在开启swap时期创建的它的内存页并不会自动搬回RAM系统会在后台逐步回收。这个过程中容器的内存监控数据可能仍然显示较高使用率容易让人误判为内存泄漏。我自己遇到过一次关闭swap后某个Pod的memory.working_set一直居高不下查了很久才发现是系统在后台换页回收导致的延迟显示等几分钟之后数据自然恢复正常。这个现象虽然不是故障但在排障时会干扰判断记录下来能少走弯路。6.5 一点个人体会把swap检查做成节点启动的前置条件踩过这么多次坑之后我现在的做法是在任何涉及节点重启的操作前都会先跑一遍内置检查脚本确认swap状态、kubelet配置和fstab三者的状态一致。我在托管节点的配置管理代码里固定维护了一个“swap检查”的任务正式集群的所有节点都必须通过这个检查才能被调度工作负载。这样一来即使某次操作把swap重新弄回来了集群也会在节点注册之前就把问题暴露出来而不是等到NotReady告警响了再临时救火。另外我养成了一个习惯每次修改fstab之后都会端到端地重启一次节点再交付而不是仅仅跑完mount -a就认为万事大吉。因为你不知道这台机器的启动链路里还有没有其他组件会干扰行为只有重启验证过的配置才算数。这条经验虽然质朴但在实际操作里帮我挡住了很多潜在事故也希望它能帮你少加几次夜班。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑