<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>驱逐风暴 on Zero Day Notes</title>
        <link>https://blog.5772447.xyz/tags/%E9%A9%B1%E9%80%90%E9%A3%8E%E6%9A%B4/</link>
        <description>Recent content in 驱逐风暴 on Zero Day Notes</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh</language>
        <lastBuildDate>Mon, 13 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.5772447.xyz/tags/%E9%A9%B1%E9%80%90%E9%A3%8E%E6%9A%B4/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>一次 Kubernetes 节点磁盘压力触发 kubelet 驱逐导致业务 Pod 批量被删的排查记录</title>
            <link>https://blog.5772447.xyz/posts/c0337851/</link>
            <pubDate>Mon, 13 Jul 2026 00:00:00 +0000</pubDate>
            <guid>https://blog.5772447.xyz/posts/c0337851/</guid>
            <description>&lt;h2 id=&#34;问题背景&#34;&gt;&lt;a href=&#34;#%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;问题背景&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;我们的生产集群由 6 个 kubeadm 搭建的节点组成，容器运行时为 containerd，跑着订单、支付、用户中心等二十余个无状态服务，外加 Prometheus、CoreDNS 等系统组件。节点是云上 ECS，系统盘 100G，且 containerd 的镜像与可写层目录（&lt;code&gt;/var/lib/containerd&lt;/code&gt;）、容器日志目录（&lt;code&gt;/var/log/containers&lt;/code&gt;、&lt;code&gt;/var/log/pods&lt;/code&gt;）都落在同一块系统盘上——也就是说，kubelet 关注的 &lt;code&gt;imagefs&lt;/code&gt; 与 &lt;code&gt;nodefs&lt;/code&gt; 实际共用一个分区。集群已稳定运行大半年，直到一次常规发版后的第二天上午，告警把人叫醒。&lt;/p&gt;&#xA;&lt;h2 id=&#34;故障现象&#34;&gt;&lt;a href=&#34;#%e6%95%85%e9%9a%9c%e7%8e%b0%e8%b1%a1&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;故障现象&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;早上九点，监控群里连续弹出节点状态告警：3 个节点在 5 分钟内从 &lt;code&gt;Ready&lt;/code&gt; 反复横跳到 &lt;code&gt;NotReady&lt;/code&gt; 再恢复，行为极具节律性。同期业务侧开始抖动——订单接口 P99 从 80ms 飙到 2s 以上，部分请求返回 503。&lt;/p&gt;&#xA;&lt;p&gt;登录集群查看，现象更具体：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;kubectl get pod -A&lt;/code&gt; 里，订单服务（Deployment，期望 5 副本）可用副本在 1~2 之间反复横跳，被驱逐的 Pod 在别的节点重建后不久又被驱逐，形成「驱逐风暴」。&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;kubectl describe node &amp;lt;node&amp;gt;&lt;/code&gt; 的 Conditions 里 &lt;code&gt;DiskPressure&lt;/code&gt; 为 &lt;code&gt;True&lt;/code&gt;，Taints 出现 &lt;code&gt;node.kubernetes.io/disk-pressure:NoSchedule&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;节点上 kubelet 日志高频刷屏：&lt;code&gt;Evicting pod ... because node had condition: [DiskPressure]&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;登录问题节点 &lt;code&gt;df -h&lt;/code&gt;，系统盘使用率 92%，「看起来还有空间」，但关键服务已经崩了。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;排查过程&#34;&gt;&lt;a href=&#34;#%e6%8e%92%e6%9f%a5%e8%bf%87%e7%a8%8b&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;排查过程&#xD;&#xA;&lt;/h2&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;确认驱逐类型&lt;/strong&gt;：&lt;code&gt;kubectl describe node&lt;/code&gt; 直接给出 &lt;code&gt;DiskPressure=True&lt;/code&gt; 和对应的 taint，说明是磁盘压力触发了 kubelet 的软/硬驱逐，而非 OOM（内存压力会显示 &lt;code&gt;MemoryPressure&lt;/code&gt;）。这把方向从「应用内存问题」扭到了「磁盘空间/阈值」。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;对照默认驱逐阈值&lt;/strong&gt;：kubelet 的 &lt;code&gt;eviction-hard&lt;/code&gt; 默认是 &lt;code&gt;imagefs.available&amp;lt;15%&lt;/code&gt;、&lt;code&gt;nodefs.available&amp;lt;10%&lt;/code&gt;。问题节点系统盘可用 8%（100%-92%），已经低于 &lt;code&gt;nodefs.available&amp;lt;10%&lt;/code&gt; 的硬阈值；而 &lt;code&gt;imagefs&lt;/code&gt; 与 &lt;code&gt;nodefs&lt;/code&gt; 共用同一分区，自然也一同越线。这正是「df 看着还有空间却被驱逐」的原因——阈值触发的是「可用量百分比」，不是「是否 100% 满」。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;定位占用大户&lt;/strong&gt;：&lt;code&gt;du -sh /var/lib/containerd&lt;/code&gt; 占 61G，&lt;code&gt;/var/log/containers&lt;/code&gt; + &lt;code&gt;/var/log/pods&lt;/code&gt; 占 23G。进一步看，containerd 用默认 &lt;code&gt;json-file&lt;/code&gt; 日志驱动且&lt;strong&gt;没有设置 &lt;code&gt;max-size&lt;/code&gt;/&lt;code&gt;max-file&lt;/code&gt;&lt;/strong&gt;，某几个业务 Pod 因有人把日志级别临时调到 DEBUG 且忘了改回，单容器日志文件涨到 8G；镜像侧，每次发版都拉新 tag，旧镜像层从未 GC，节点上堆积了同一服务的十几个历史版本。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;还原驱逐风暴链路&lt;/strong&gt;：&lt;code&gt;DiskPressure&lt;/code&gt; 触发后，kubelet 按 QoS 从低优先级（BestEffort/Burstable）开始驱逐 Pod；被删的 Pod 由 Deployment 在其它节点重建，但新节点同样磁盘吃紧，于是又被驱逐——形成循环。同时 &lt;code&gt;NoSchedule&lt;/code&gt; taint 阻止 Pod 调度回原节点，但已运行 Pod 仍会因 &lt;code&gt;imagefs&lt;/code&gt; 继续上涨被二次驱逐，节点在 Ready/NotReady 间反复横跳。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;交叉验证&lt;/strong&gt;：对比同配置但日志量小的健康节点，使用率 70%，从未触发。锁定根因——&lt;strong&gt;日志无轮转 + 镜像不 GC 导致 imagefs 慢性膨胀，最终越过 15%/10% 阈值&lt;/strong&gt;。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;解决方案&#34;&gt;&lt;a href=&#34;#%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;解决方案&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;止血（先恢复）&lt;/strong&gt;：对问题节点 &lt;code&gt;kubectl drain &amp;lt;node&amp;gt; --ignore-daemonsets --delete-emptydir-data&lt;/code&gt; 排空；手动清理——&lt;code&gt;crictl rmi $(crictl images -q)&lt;/code&gt; 清旧镜像、&lt;code&gt;truncate -s 0&lt;/code&gt; 超大日志文件，把使用率压到 60% 以下，节点 &lt;code&gt;DiskPressure&lt;/code&gt; 自动解除、回到 &lt;code&gt;Ready&lt;/code&gt;，被驱逐的 Pod 在新节点稳定重建。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;根治（调参与治理）&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;调整 kubelet 驱逐配置，给足缓冲：&lt;code&gt;eviction-hard=imagefs.available&amp;lt;10%,nodefs.available&amp;lt;5%&lt;/code&gt;，并设 &lt;code&gt;eviction-minimum-reclaim&lt;/code&gt; 保证单次回收量；增大 &lt;code&gt;eviction-pressure-transition-period&lt;/code&gt;（默认 5m）避免抖动。&lt;/li&gt;&#xA;&lt;li&gt;日志驱动改造：containerd 配置 &lt;code&gt;io.containerd.grpc.v1.cri&lt;/code&gt; 下的 &lt;code&gt;log&lt;/code&gt; 段，&lt;code&gt;max-size=100m&lt;/code&gt;、&lt;code&gt;max-file=3&lt;/code&gt;，或对关键业务接 fluentd/采集到 ES，从根上限制单文件体积。&lt;/li&gt;&#xA;&lt;li&gt;镜像治理：节点设置 &lt;code&gt;--image-gc-high-threshold=70 --image-gc-low-threshold=50&lt;/code&gt;，或加 cron 定期 &lt;code&gt;crictl rmi&lt;/code&gt; 清悬空镜像。&lt;/li&gt;&#xA;&lt;li&gt;容量隔离：把 containerd 根目录迁移到&lt;strong&gt;独立数据盘&lt;/strong&gt;，分离 &lt;code&gt;imagefs&lt;/code&gt; 与 &lt;code&gt;nodefs&lt;/code&gt;，避免镜像/日志与系统盘互相挤占。&lt;/li&gt;&#xA;&lt;li&gt;业务侧：评审并收敛日志级别，禁止生产环境 DEBUG 全量输出。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;根因分析&#34;&gt;&lt;a href=&#34;#%e6%a0%b9%e5%9b%a0%e5%88%86%e6%9e%90&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;根因分析&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;这次故障表面是「磁盘要满了」，本质是&lt;strong&gt;日志与镜像缺少生命周期管理，导致 imagefs 慢性膨胀越过 kubelet 驱逐阈值&lt;/strong&gt;；而 &lt;code&gt;imagefs&lt;/code&gt;/&lt;code&gt;nodefs&lt;/code&gt; 共用系统盘又把风险放大了一倍。kubelet 的磁盘压力驱逐本是自我保护机制，但当阈值与容量规划不匹配、且增长源（日志/镜像）无人治理时，保护机制反而成了业务抖动源。&lt;/p&gt;&#xA;&lt;h2 id=&#34;预防措施&#34;&gt;&lt;a href=&#34;#%e9%a2%84%e9%98%b2%e6%8e%aa%e6%96%bd&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;预防措施&#xD;&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;基础设施层强制：容器日志驱动必须设 &lt;code&gt;max-size&lt;/code&gt;/&lt;code&gt;max-file&lt;/code&gt;，不允许裸 &lt;code&gt;json-file&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;节点级 &lt;code&gt;imageGC&lt;/code&gt; 阈值 + 定期清理脚本，控制镜像层堆积。&lt;/li&gt;&#xA;&lt;li&gt;containerd 使用独立数据盘，分离 &lt;code&gt;imagefs&lt;/code&gt; 与 &lt;code&gt;nodefs&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;监控前移：对 &lt;code&gt;nodefs.available&lt;/code&gt;/&lt;code&gt;imagefs.available&lt;/code&gt; 设 20%/25% 告警，&lt;strong&gt;先于&lt;/strong&gt; kubelet 阈值；同时监控 &lt;code&gt;Evicted&lt;/code&gt;/&lt;code&gt;Terminating&lt;/code&gt; Pod 数量与节点 &lt;code&gt;DiskPressure&lt;/code&gt; 发生次数。&lt;/li&gt;&#xA;&lt;li&gt;建立日志级别评审机制，生产环境默认 INFO，禁止 DEBUG 常开。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;总结&#34;&gt;&lt;a href=&#34;#%e6%80%bb%e7%bb%93&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;总结&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;磁盘压力驱逐常被误判为「磁盘 100% 满了」。真正要治理的是日志轮转、镜像 GC 与容量隔离这些基础设施层的生命周期问题，以及 eviction 阈值与容量规划的匹配——在基础设施层把增长源管住，远比在应用层一次次救火更根本。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;延伸阅读&#34;&gt;&lt;a href=&#34;#%e5%bb%b6%e4%bc%b8%e9%98%85%e8%af%bb&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;延伸阅读&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;K8s 节点与驱逐&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://blog.5772447.xyz/posts/e01939e6/&#34; &gt;记一次 K8s 节点 kubelet 证书轮换失败导致节点 NotReady 风暴的排查&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://blog.5772447.xyz/posts/d4683312/&#34; &gt;K8s 节点磁盘压力致 Pod 批量驱逐的定位&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
        </item></channel>
</rss>
