明明每个 Pod 都没写满,为什么还是被驱逐?emptyDir 监控与驱逐机制实战 📅 2026/6/26 3:31:49 明明每个 Pod 都没写满为什么还是被驱逐emptyDir 监控与驱逐机制实战在 Kubernetes 里emptyDir是最常见、也最容易被低估的临时存储能力。它看起来只是“给 Pod 挂一个临时目录”但一旦用在缓存、临时文件、构建产物、日志缓冲、模型中间结果等场景就会直接牵涉到节点磁盘容量、Pod 驱逐、服务稳定性和资源调度公平性。本文从实际场景出发讲清楚emptyDir到底适合什么场景emptyDir.sizeLimit能限制什么不能限制什么单个 Pod 超过sizeLimit为什么会被精准驱逐每个 Pod 都没超过自己的限制但节点磁盘达到阈值时为什么被驱逐的不一定是“最能写”的那个 Pod原生kubelet中未提供emptyDir的监控如何设计一套可落地的 emptyDir 监控方案 见 13节1. 一个真实且常见的问题假设我们在一个节点上跑了很多业务 Pod每个 Pod 都配置了emptyDirvolumes:-name:cacheemptyDir:sizeLimit:100Gi业务方理解是每个 Pod 最多用 100Gi 临时目录只要没超过 100Gi就应该不会被驱逐不会有问题。但线上可能出现这样的情况PodemptyDir sizeLimit实际使用pod-a100Gi80Gipod-b100Gi70Gipod-c100Gi60Gipod-d100Gi90Gipod-e100Gi75Gi每个 Pod 都没超过自己的100Gi限制。可是节点磁盘整体触发了 kubelet eviction threshold节点出现nodefs.available below eviction threshold然后 kubelet 开始驱逐 Pod。更令人困惑的是被驱逐的 Pod 不一定是当前写得最多的那个也不一定是最接近sizeLimit的那个。这就是很多人第一次遇到emptyDir问题时最不理解的地方。2. emptyDir 是什么emptyDir是 Kubernetes 提供的一种临时卷。它的生命周期跟 Pod 绑定Pod 创建 → emptyDir 创建 Pod 删除 → emptyDir 删除 即使容器异常了emptyDir仍然存在Pod 中的多个容器可以共享同一个emptyDir。例如apiVersion:v1kind:Podmetadata:name:emptydir-demospec:containers:-name:appimage:busyboxcommand:[sh,-c,sleep 3600]volumeMounts:-name:cachemountPath:/cache-name:sidecarimage:busyboxcommand:[sh,-c,sleep 3600]volumeMounts:-name:cachemountPath:/cachevolumes:-name:cacheemptyDir:{}这里app和sidecar都能访问/cache它们看到的是同一个临时目录。3. emptyDir 的常见使用场景emptyDir适合这些场景。3.1 临时缓存例如图片缩略图缓存文件下载缓存模型推理中间缓存Maven/npm/pip 构建缓存查询结果临时缓存。特点是丢了可以重建不需要持久保存。3.2 多容器共享文件一个 Pod 里有多个容器主容器生成文件sidecar 容器读取并上传init container 初始化配置文件日志 sidecar 读取应用输出。emptyDir可以作为它们之间的共享目录。3.3 临时计算中间结果例如视频转码、批处理、AI 任务、ETL 任务输入文件 → 中间文件 → 输出结果 → 上传对象存储中间文件不需要持久化适合放emptyDir。3.4 应用临时工作目录有些应用需要/tmp、/work、/cache这样的工作目录。为了避免直接写容器层可以挂载emptyDir。3.5 内存型临时目录emptyDir还支持内存介质volumes:-name:tmpemptyDir:medium:MemorysizeLimit:1Gi这类emptyDir使用 tmpfs速度快但会消耗内存。适合小文件高速缓存临时密钥文件对 IO 延迟敏感的小型临时数据。但不适合大文件。4. emptyDir 的能力边界emptyDir很方便但要记住几个边界。4.1 emptyDir 不是持久化存储Pod 删除后emptyDir数据就没了。如果数据不能丢应该使用PVC对象存储外部数据库分布式文件系统。4.2 emptyDir 默认使用节点本地磁盘没有指定medium: Memory时emptyDir默认使用节点本地存储。这意味着emptyDir 写多了本质上是在消耗节点磁盘。 emptydir 在容器中看到的不是一个独立的磁盘不想pvc那种在容器中df -h 那个目录看到的磁盘还是节点上那个kubelet所在的磁盘apiVersion: v1 kind: Pod metadata: name: test-cdl6k namespace: insight-system spec: containers: - args: ---config- /etc/test/config.yaml image: test:v20260625 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold:3httpGet: path: /healthz port: metrics scheme: HTTP initialDelaySeconds:20periodSeconds:10successThreshold:1timeoutSeconds:5name:testvolumeMounts: - mountPath: /opt/oomprotection/log name: log-dir volumes: - emptyDir: sizeLimit: 1Gi name: log-dir但进入容器看不到的log目录不是1G,而是节点磁盘多个 Pod 的emptyDir、容器日志、镜像层、容器可写层都可能共享节点的本地磁盘资源。4.3 emptyDir 的风险不是单个 Pod而是节点总量单个 Pod 看起来只用了几十 Gi但节点上几十个 Pod 加起来就可能把节点磁盘打满。这也是为什么emptyDir必须监控。5. emptyDir.sizeLimit 是什么我们可以给emptyDir配置sizeLimitvolumes:-name:cacheemptyDir:sizeLimit:100Gi它表达的是这个emptyDir卷最多希望使用 100Gi。注意这个限制是卷级别的。也就是说它限制的是某个 Pod 内某个 emptyDir volume 的使用量而不是整个节点磁盘。6. 单个 Pod 超过 sizeLimit为什么能精准驱逐假设某个 Pod 配置emptyDir:sizeLimit:100Gi当这个 Pod 的emptyDir实际使用超过 100Gi 时即使节点磁盘还有剩余空间kubelet 也会驱逐这个 Pod。因为这时问题很明确Pod A 的 emptyDir 上限是 100Gi Pod A 实际用了 101Gi Pod A 违反了自己的限制这是一个“局部配额超限”。类比一下你租了一个 100 平米仓库。整栋楼还有空房间不代表你可以占用 101 平米。你超出了自己的合同面积所以处理对象就是你。所以 kubelet 可以精准驱逐这个 Pod。可能看到的事件类似Usage of EmptyDir volume cache exceeds the limit 100Gi这个是大家都理解的kubelet也会检测虽然做不到实时但会检测检测到就驱逐对应的pod)7. 每个 Pod 都没超过 sizeLimit但节点磁盘到了阈值现在换一个场景。节点可用本地磁盘资源有限例如 500Gi。节点上有多个 PodPodemptyDir sizeLimitemptyDir 实际使用pod-a100Gi80Gipod-b100Gi80Gipod-c100Gi80Gipod-d100Gi80Gipod-e100Gi80Gipod-f100Gi80Gi每个 Pod 都没超过 100Gi。但是它们加起来6 x 80Gi 480Gi再加上容器日志镜像层容器可写层kubelet 目录系统组件占用其他 Pod 的临时文件。节点磁盘可能就触发了 kubelet 的 eviction threshold。这时问题不再是哪个 Pod 超过了自己的 sizeLimit而是节点整体临时存储资源不足。这属于“节点级资源压力”。8. 为什么节点磁盘压力时被驱逐的不一定是写满的那个 Pod这是理解 Kubernetes 驱逐机制的关键。8.1 单 Pod 超限时有明确违规者Pod A sizeLimit 100Gi Pod A used 101Gi 结论Pod A 违规所以驱逐 Pod A。8.2 节点磁盘压力时不一定有单个违规者Pod A sizeLimit 100Gi, used 80Gi Pod B sizeLimit 100Gi, used 80Gi Pod C sizeLimit 100Gi, used 80Gi Pod D sizeLimit 100Gi, used 80Gi 节点磁盘触发压力此时每个 Pod 都可以说我没超过自己的限制。所以 kubelet 不能简单地说“谁违规就驱逐谁”。它只能做节点级资源恢复节点磁盘压力 → 找一个最适合驱逐的 Pod → 驱逐后看节点是否恢复 → 如果没恢复再驱逐下一个9. 节点压力驱逐时kubelet 大概怎么选 Pod在节点资源压力下kubelet 不会简单按照“谁最接近 emptyDir.sizeLimit”排序。它通常会综合考虑Pod PriorityPod QoSPod 的资源 request当前实际使用量是否超过 request超过 request 的程度当前压力资源类型例如 memory、nodefs、imagefs、ephemeral-storagePod 是否使用了比自己声明更多的资源。这里可以看到没有把emptydir占用考虑在内所以极端场景可能出现节点磁盘压力但最终把所有pod都驱逐了(最后才驱逐到那个写的满的那个pod10. “最差会不会把所有 Pod 都驱逐一遍”理论上在极端情况下节点持续无法恢复磁盘压力kubelet 会持续选择 Pod 驱逐。所以从现象上看确实可能出现驱逐一个 Pod不够 再驱逐一个 Pod还不够 继续驱逐如果节点上大量 Pod 都在消耗本地存储并且释放效果不足或者新的 Pod 又被调度回来继续写可能造成一轮又一轮驱逐。但严格说kubelet 的目标不是“把所有 Pod 都驱逐一遍”。它的目标是驱逐尽量少的 Pod让节点资源回到安全阈值以上。只是当节点磁盘压力很严重、Pod 使用量都很高、没有合理 request/limit、调度器又继续把 Pod 调回来时就可能出现连续驱逐、反复驱逐甚至看起来像“把一批 Pod 都扫了一遍”。所以这类问题不应该靠事后驱逐兜底而应该靠合理设置ephemeral-storage.requests合理设置ephemeral-storage.limits监控 emptyDir 使用量监控节点磁盘压力控制单节点可调度的临时存储总量。11. emptyDir 和 ephemeral-storage 的关系这两个概念经常混在一起。11.1 emptyDir.sizeLimitvolumes:-name:cacheemptyDir:sizeLimit:100Gi它限制的是这个 emptyDir 卷最多使用多少11.2 ephemeral-storage request/limitresources:requests:ephemeral-storage:50Gilimits:ephemeral-storage:100Gi它描述的是容器/Pod 的本地临时存储资源需求和上限。它可能包括容器可写层容器日志emptyDir其他本地临时存储使用。11.3 推荐同时配置完整一点的配置应该像这样apiVersion:v1kind:Podmetadata:name:emptydir-practicespec:containers:-name:appimage:busyboxcommand:[sh,-c,sleep 3600]resources:requests:cpu:500mmemory:512Miephemeral-storage:50Gilimits:cpu:1memory:1Giephemeral-storage:100GivolumeMounts:-name:cachemountPath:/cachevolumes:-name:cacheemptyDir:sizeLimit:100Gi含义是配置作用emptyDir.sizeLimit: 100Gi限制该 emptyDir 卷最大使用量requests.ephemeral-storage: 50Gi告诉调度器这个 Pod 预计需要多少临时存储limits.ephemeral-storage: 100Gi限制该容器/Pod 的临时存储上限但更多时候**业务的pod不会配置ephemeral-storage**而很多pod的emptydir的上限又超出磁盘大小同时即使不超每个业务也希望看到磁盘的使用情况所以需要该emptydir的监控但从kubelet上报的指标中是没有的emptydir的指标只有pvc磁盘的指标所以需要补齐emptydir的使用量和sizelimite的记录关联到pod12. emptyDir Pod粒度监控方案应该怎么设计下面进入实战部分。目标不是只看节点磁盘而是建立三层监控Pod emptyDir 使用量 Pod ephemeral-storage 使用量 先不讨论 Node 磁盘 / ephemeral-storage 压力 (node指标在node-exporter开源组件中已经有13. 监控目标一单 Pod emptyDir 使用量13.1 为什么要监控单个 Pod 的emptyDir超过sizeLimit时可能被精准驱逐。所以要提前发现某个 Pod 的 emptyDir 使用量正在接近 sizeLimitemptydir limit采集使用量采集prometheus采集到两类指标后汇聚出使用率13.2 需要采集的指标理想情况下要能看到指标含义podPod 名称namespace命名空间node所在节点volumeemptyDir 卷名used_bytes当前使用量size_limit_bytes配置的 sizeLimitusage_ratio使用率计算usage_ratio used_bytes / size_limit_bytes由于kubelet中缺少emptydir指标所以我设计监控方案① sizelimit通过informer机制监听podpod的增删变化记录到指标中emptydir_size_limit_bytes{pod“pod名称”,namespace“xxx”,pod_uid“xxxx”path“log-dir 对应emptydir name”}pod_uid namespace 从pod的详细中取pod中配置了sizelimit的emptydir - emptyDir: sizeLimit: 1Gi name: log-dir② emptydir_used_bytes{pod_uid“xxxx”,path“log-dir”}在每个节点上采集所有emptydir中所有文件的大小组合定时采集,节点上每个emptydir路径节点上每个emptydir路径/var/lib/containers/kubelet/pods/a74bdf6a-bb18-48ab-9462-3c92c7eb1aff/volumes/kubernetes.io~empty-dir/log-dir a74bdf6a-bb18-48ab-9462-3c92c7eb1aff是pod_uid,log-dir为emptydir的名称路径③ 指标采集到prometheus通过rules pod_uid关联得到新指标emptydir_used_rateemptydir_used_bytes/ on(pod_id) group_right(pod,namespace) emptydir_size_limit_bytes13.3 告警建议级别条件说明warningemptyDir 使用率 70% 持续 10 分钟提前发现增长趋势criticalemptyDir 使用率 85% 持续 5 分钟需要人工介入emergencyemptyDir 使用率 95% 持续 1 分钟很可能即将驱逐14. 监控目标二节点磁盘和 eviction threshold节点级监控同样重要。14.1 要监控哪些磁盘Kubernetes 中常见概念名称含义nodefskubelet、emptyDir、日志等所在文件系统imagefs容器镜像所在文件系统如果单独分盘containerfs某些运行时中容器文件系统所在位置实际要看节点如何部署。14.2 关键指标指标含义node_filesystem_avail_bytes节点文件系统可用空间node_filesystem_size_bytes节点文件系统总空间kube_node_status_condition{condition“DiskPressure”}节点是否有磁盘压力kube_pod_status_reason{reason“Evicted”}Pod 是否被驱逐kube_event驱逐事件取决于事件采集方案14.3 节点级告警建议级别条件说明warningnodefs 使用率 75% 持续 10 分钟提前扩容或清理criticalnodefs 使用率 85% 持续 5 分钟接近 eviction 风险emergencynodefs 使用率 90% 或 DiskPressureTrue可能开始驱逐criticalEvicted Pod 数量 0已发生影响15. 监控维度不要只看节点也不要只看 Pod很多团队只做了节点磁盘告警节点磁盘 85% 告警这不够。因为等节点磁盘已经 85% 时问题可能已经很严重了。也有人只看 Pod 是否被 EvictedPod Evicted 告警这更晚。推荐监控顺序应该是单 Pod emptyDir 使用率上涨 → Pod ephemeral-storage 接近 request/limit → 节点 nodefs 使用率上涨 → 节点 DiskPressure → Pod Evicted越靠前越能提前处理。16. 治理建议不要只靠驱逐兜底emptyDir治理建议分三层。16.1 应用层治理应用应该控制临时文件生命周期定期清理临时文件避免无限增长日志中间结果及时上传对象存储大文件不要长期放 emptyDir缓存要有淘汰策略。16.2 Pod 配置治理每个使用大量emptyDir的 Pod 都建议配置resources:requests:ephemeral-storage:合理值limits:ephemeral-storage:上限值同时配置emptyDir:sizeLimit:上限值不要只配sizeLimit。21.3 集群层治理集群层面建议设置 ResourceQuota 限制 namespace 总 ephemeral-storage设置 LimitRange 给默认 request/limit对大量使用 emptyDir 的 workload 做节点隔离对构建类、批处理类任务使用专用节点池对 nodefs/imagefs 做容量监控采集 kubelet eviction event监控 Pod emptyDir 和 ephemeral-storage 使用排行。22. 实战建议哪些场景不适合 emptyDir以下场景不要优先用emptyDir场景建议数据不能丢使用 PVC 或外部存储单 Pod 需要几百 Gi 临时文件考虑专用节点池或 PVC多副本共享数据使用共享存储或对象存储长周期缓存使用外部缓存或持久化卷构建产物需要保留上传制品库或对象存储日志大量增长接入日志系统不要长期落 emptyDiremptyDir最适合的是生命周期短、可丢失、可重建、容量可控的临时数据。25. 总结emptyDir的问题本质不是“能不能挂一个临时目录”而是Pod 临时存储如何被限制、如何被调度、如何被监控、如何在节点压力下被驱逐。需要记住几个关键点emptyDir默认使用节点本地磁盘多个 Pod 会共享节点磁盘资源。emptyDir.sizeLimit是卷级上限不是调度 request。单个 Pod 超过自己的sizeLimitkubelet 可以精准驱逐这个 Pod。每个 Pod 都没超过sizeLimit但总和导致节点磁盘压力时kubelet 会按节点级 eviction 策略选择 Pod。节点压力驱逐时被驱逐的不一定是最接近sizeLimit的 Pod。如果资源压力持续kubelet 可能连续驱逐多个 Pod直到节点恢复安全阈值。不要只配置emptyDir.sizeLimit还要配置resources.requests/limits.ephemeral-storage。监控要覆盖 Pod emptyDir、Pod ephemeral-storage、Node nodefs/imagefs、DiskPressure 和 Evicted 事件。大量使用emptyDir的业务最好配套 ResourceQuota、LimitRange 和专用节点池。一句话总结emptyDir.sizeLimit解决的是“单个 Pod 不要越界”ephemeral-storage request/limit和节点监控解决的是“整个节点不要被所有 Pod 一起打爆”。