限制 Kubernetes 本地临时存储的容量

限制 Kubernetes 本地临时存储的容量

参考文章:如何限制KUBERNETES本地临时存储的容量

本文结合生产环境(K8s 1.28 + Docker)实际配置,深入讲解 ephemeral storage 的原理与最佳实践。

1. 问题背景

作为 Kubernetes 平台的提供方,必须对”流氓”应用做出限制,防止它们滥用 CPU、内存、磁盘等资源。

  • Kubernetes 提供了对 CPU、内存 的限制(requests/limits),可以防止应用无限制使用系统资源
  • Kubernetes 提供的 PVC(如 cephfs、RBD)也支持容量限制
  • 但早期版本没有限制 container 的 rootfs 容量
  • 容器 log 默认存储在 /var/lib/kubelet/,rootfs 在 /var/lib/docker,两者默认在宿主机 node 的根分区
  • 恶意应用可在容器内大量写入(如 dd),迅速造成宿主机 node 根分区文件系统满
  • Linux 根分区使用率达 100% 时非常危险,可能导致整个节点不可用

2. 概念与覆盖范围

什么是本地临时存储(Local Ephemeral Storage)

Kubernetes 1.8 引入新 resource:local ephemeral storage(本地临时存储),对应特性 LocalStorageCapacityIsolation1.10 开始转入 beta 状态并默认开启。到 K8s 1.28,该特性已稳定成熟。

临时存储覆盖范围

ephemeral-storage 限制覆盖以下内容:

类型 说明 存储位置
emptyDir volumes Pod 内共享的临时卷 节点本地磁盘
container logs 容器标准输出/错误日志(kubectl logs 看到的) /var/log/containers//var/log/pods/
image layers 容器镜像的只读层 节点本地存储
container writable layers 容器可写层(rootfs,容器内写入的文件) 节点本地磁盘

⚠️ 重要注意: 本地临时存储管理只对 root 分区 有效。如果定制了相关参数(如 kubelet --root-dir 指向非根分区),则不会生效。

3. 配置方法

3.1 基本配置

每个 container 可配置以下字段:

spec.containers[].resources.limits.ephemeral-storage
spec.containers[].resources.requests.ephemeral-storage

单位说明: 默认为 byte,也可使用:

  • 十进制:E, P, T, G, M, K(如 129M = 129,000,000 bytes)
  • 二进制:Ei, Pi, Ti, Gi, Mi, Ki(如 123Mi = 123 × 2²⁰ bytes)

实际使用中建议用二进制单位(Gi, Mi),与磁盘容量计算一致。

3.2 配置示例

设置临时存储最大为 2Gi:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
spec:
  selector:
    matchLabels:
      run: nginx
  template:
    spec:
      containers:
      - image: nginx
        name: nginx
        resources:
          limits:
            ephemeral-storage: 2Gi
          requests:
            ephemeral-storage: 2Gi

3.3 验证方法

进入容器执行 dd if=/dev/zero of=/test bs=4096 count=1024000,尝试创建超过限制的文件:

# Pod 被 Evict,controller 重新创建新 Pod
nginx-75bf8666b8-89xqm    1/1     Running     0    1h
nginx-75bf8666b8-pm687    0/1     Evicted     0    2h

4. 驱逐机制详解

4.1 Kubelet Evict Manager

Evict Pod 的动作由 kubelet 完成。每个节点上的 kubelet 启动一个 evict manager,每 10秒evictionMonitoringPeriod)检查一次,ephemeral storage 的检查也在此时完成。

4.2 检查顺序

kubelet 按以下顺序依次检查:

  1. Pod 的 emptyDiremptyDirLimitEviction
  2. Pod 级临时存储podEphemeralStorageLimitEviction
  3. Container 级临时存储containerEphemeralStorageLimitEviction

4.3 Container 级检查

比较简单:依次检查每个 container 的临时存储使用量和设置的 limits,超过则将 Pod 加入 evicted 列表。

4.4 Pod 级检查

限制值计算:

max(sum(所有container的ephemeral-storage limits), initContainer1, initContainer2, ...)
  • 统计 Pod 所有 container(不包括 init container)的 ephemeral storage limits 之和
  • init container 指定的是 Pod 配额的最低需求
  • 当所有 container 的配额之和超过 init container 指定的配额时,忽略 init container 的值

实际用量计算:

  • 指定了 ephemeral-storage 的 container 的使用量
  • 指定 ephemeral-storage 的 container 的使用量(⚠️ 注意:仍计入 Pod 总用量)
  • emptyDir 的使用量

关键点: 当实际用量超过限制值时,kubelet 将 Pod Evict,等待 controller 重新创建并调度。

5. Requests 的作用

设置的 local ephemeral storage requests 在 evict manager 处理过程中没有用到,但它不是没用的。

创建 Pod 后,scheduler 将 Pod 调度到集群中某个 node 上。scheduler 保证该 node 上所有 Pod 的 local ephemeral storage requests 总和不会超过 node 的根分区容量

简单理解:
limits → kubelet 用,决定何时 Evict Pod
requests → scheduler 用,决定 Pod 调度到哪个 node

6. Inode 保护

问题现象

磁盘写入报磁盘满,但 df -h 查看容量并未 100%,可能是因为 inode 耗尽

现状

  • podLocalEphemeralStorageUsage 统计了 container 或 pods 使用的 inode 数量
  • 当前 Kubernetes 不支持对 Pod 的临时存储设置 inode 的 limits/requests
  • 如果 node 进入 inode 紧缺状态,kubelet 将 node 设置为 DiskPressure不再接收新的 Pod 请求

生产环境中,大量小文件(如日志碎片、临时缓存文件)可能耗尽 inode,需关注。

7. emptyDir sizeLimit

emptyDir 也是一种临时存储,需要限制使用。

Pod 级别检查时

emptyDir 的使用量计入 Pod 总用量,过量使用会导致 Pod 被 kubelet Evict。

emptyDir 独立限制配置

volumeMounts:
- mountPath: /cache
  name: cache-volume
volumes:
- emptyDir:
    medium: Memory        # 使用内存作为存储介质,获得极好的读写性能
    sizeLimit: 64Mi       # 容量上限,超过后 Pod 被 kubelet evict
  name: cache-volume

medium: Memory 表示使用 tmpfs(内存文件系统),数据不写入磁盘但占用容器内存,需同时考虑 memory limits。

8. 生产配置有效性分析

以下基于实际 Helm values 配置进行分析:

8.1 当前配置摘要

# 主容器资源配置
resources:
  requests:
    cpu: 500m
    memory: 8192Mi
    ephemeral-storage: 10Gi
  limits:
    cpu: 2000m
    memory: 8192Mi
    ephemeral-storage: 10Gi

# skywalking agent sidecar 资源配置
skywalking:
  agent:
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
      limits:
        cpu: 200m
        memory: 256Mi
      # ⚠️ 未设置 ephemeral-storage

# 日志目录挂载到 PVC
mount:
  volumeMounts:
    - mountPath: /AppHome/logs
      subPath: '{{ .Release.Name }}'
      name: log-data
    - mountPath: /AppLogs
      subPath: '{{ .Release.Name }}'
      name: log-data
  volumes:
    - name: log-data
      persistentVolumeClaim:
        claimName: jar-log-data-pvc

8.2 有效性判断

配置项 有效性 说明
主容器 ephemeral-storage: 10Gi 有效 能限制主容器的可写层(rootfs)和容器日志写入量
日志挂 PVC 好实践 /AppHome/logs/AppLogs 写入 PVC,不消耗临时存储
JVM -Dcsp.sentinel.log.dir=/tmp ⚠️ 风险 Sentinel 日志写入容器 /tmp,属于临时存储,长期运行会占用空间
skywalking sidecar 未设 ephemeral-storage ⚠️ 风险 sidecar 的临时存储用量计入 Pod 总用量,但不计入 Pod 限制值
容器日志(stdout/stderr) ⚠️ 风险 存储在节点 /var/log/containers/,属于临时存储,需关注日志量

8.3 ⚠️ 核心风险详解:sidecar 未设 ephemeral-storage

这是一个重要但容易被忽视的问题:

Pod 级临时存储限制值计算:

Pod限制 = sum(所有设置了ephemeral-storage limits的container)

Pod 级临时存储实际用量计算:

Pod用量 = 所有container的临时存储用量(无论是否设置了limits) + emptyDir用量

也就是说:

  • skywalking sidecar 未设 ephemeral-storage → 其用量不计入限制值
  • skywalking sidecar 未设 ephemeral-storage → 其用量仍计入 Pod 总用量
  • 结果:Pod 实际用量可能超过限制值,触发 Evict

如果 skywalking agent 在 /agent 目录(initMountPath)写入大量数据,这些数据会占用临时存储但不被限制值覆盖。

8.4 K8s 1.28 + Docker 环境说明

Kubernetes 1.28 已移除内置 dockershim(v1.24 起移除),使用 Docker 作为底层运行时需部署 cri-dockerd 作为 CRI 适配器。

ephemeral-storage 功能不受 CRI 影响,仍由 kubelet 管理:
– kubelet 通过 CRI 获取容器 stats
– kubelet 自己计算临时存储用量并做 eviction 判断
– 无论底层是 docker、containerd 还是其他 CRI,ephemeral-storage 限制行为一致

9. 优化建议

9.1 为所有 sidecar 补齐 ephemeral-storage

skywalking:
  agent:
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
        ephemeral-storage: 1Gi    # 补齐
      limits:
        cpu: 200m
        memory: 256Mi
        ephemeral-storage: 2Gi    # 补齐

建议 sidecar 的 ephemeral-storage limits 设为 1Gi~2Gi,根据实际观察调整。

9.2 修改 Sentinel 日志路径

将 Sentinel 日志写入已挂 PVC 的目录:

jvm:
  opts: ...
    -Dcsp.sentinel.log.dir=/AppHome/logs/sentinel    # 从 /tmp 改为 PVC 挂载路径

9.3 配置容器日志轮转

在 kubelet 配置中设置日志轮转参数(节点级别):

# /var/lib/kubelet/config.yaml
maxLogSize: 10Mi          # 单个日志文件最大大小
logRotationPolicy: Rotate  # 启用日志轮转

或通过 containerRuntimeConfig 调整:

apiVersion: machineconfiguration.openshift.io/v1
kind: ContainerRuntimeConfig
spec:
  containerRuntimeConfig:
    logSizeMax: "50Mi"
    logRotationMaxFiles: 5

9.4 合理设置 ephemeral-storage requests

确保 requests 值合理,让 scheduler 能正确调度:

resources:
  requests:
    ephemeral-storage: 5Gi    # 不需要和 limits 一致,按实际需求设置
  limits:
    ephemeral-storage: 10Gi   # 限制最大用量

9.5 监控临时存储使用

# 查看节点临时存储容量
kubectl describe node <node-name> | grep -A5 "Allocatable"

# 查看 Pod 临时存储使用量
kubectl top pod <pod-name> --containers

# 查看节点磁盘压力状态
kubectl describe node <node-name> | grep -i "DiskPressure"

10. 总结

要点 说明
ephemeral-storage 有效 K8s 1.28 下该特性稳定可用,能限制容器可写层和容器日志
limits → Evict kubelet 每 10s 检查,超过 limits 触发 Pod Evict
requests → 调度 scheduler 保证节点上 Pod requests 总和不超过根分区容量
sidecar 必须补齐 未设 ephemeral-storage 的 sidecar 用量计入 Pod 总量但不计入限制值
日志挂 PVC 大量日志写入 PVC 是最佳实践,避免消耗临时存储
只对 root 分区有效 kubelet --root-dir 等参数可能影响生效范围
inode 需关注 当前不支持 inode limits,大量小文件可能耗尽 inode

相关笔记:
kubernetes临时存储限制参数-ephemeral storage(偏实操测试)
理解Kubernetes驱逐Pod(Pod 驱逐机制详解)

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇