bigywater.tech
Kubernetes · 111

Downward API:把 Pod 信息注入容器

容器怎么知道自己叫什么、跑在哪个节点、有多少 CPU?环境变量和卷里的 Downward API 有什么区别?改了标签,容器里能看到吗?没写 limits 会得到什么?这篇把所有支持的写法和拒绝的写法都跑一遍。

1 秒
改了 Pod 标签后,downwardAPI 卷里的 labels 文件更新所用的时间(环境变量不变)
500 / 1
限 500m 的容器,divisor 1m 时 CPU_LIMIT_M=500,默认 divisor 时 CPU_LIMIT=1
allocatable
没写 limits 的容器通过 resourceFieldRef 读到的是节点的 allocatable(10 核、7935Mi)

环境变量:fieldRef

实测 · 本机执行输出
  POD_NAME  = me
  POD_NS    = k105
  POD_UID   = 11ee8209-ac71-4533-a7e5-5e83b29c2681
  NODE_NAME = kx-main-worker
  SA_NAME   = default
  POD_IP    = 10.244.1.149
  HOST_IP   = 172.18.0.2
  LABEL_APP = demo
  ANN_OWNER = team-a
  对照 Pod 对象:uid=11ee8209-ac71-4533-a7e5-5e83b29c2681;podIP=10.244.1.149;hostIP=172.18.0.2

能直接读的:metadata.name → POD_NAME=me、metadata.namespace → k105、metadata.uid(与 Pod 对象一致)、spec.nodeName → kx-main-worker、spec.serviceAccountName → default、status.podIP → 10.244.1.149、status.hostIP → 172.18.0.2;单个标签和注解也可以:metadata.labels['app'] → demo、metadata.annotations['owner'] → team-a。全程不用调 API Server,也不需要 RBAC 和令牌。

resourceFieldRef 与 divisor

实测 · 本机执行输出
  CPU_LIMIT    = 1           (limits.cpu(默认 divisor 1,向上取整成核))
  CPU_LIMIT_M  = 500         (limits.cpu, divisor: 1m)
  MEM_LIMIT    = 134217728   (limits.memory(默认字节))
  MEM_LIMIT_MI = 128         (limits.memory, divisor: 1Mi)
  MEM_REQ_MI   = 64          (requests.memory, divisor: 1Mi)
  没有写 limits 的容器:CPU_LIMIT_M=10000、MEM_LIMIT_MI=7935——退回节点的 allocatable(节点 cpu=10 核、memory=8124516Ki),不是 0 或空

容器 limits.cpu 是 500m:默认 divisor 下 CPU_LIMIT=1(向上取整成核),divisor: 1m 时 CPU_LIMIT_M=500;limits.memory 是 128Mi:默认 MEM_LIMIT=134217728(字节),divisor: 1Mi 时 MEM_LIMIT_MI=128,requests.memory 的 MEM_REQ_MI=64。没有写 limits 的容器:CPU_LIMIT_M=10000、MEM_LIMIT_MI=7935,正好是节点的 allocatable(10 核、8124516Ki),不是 0 或空——所以应用按这个数字调线程数时,没设 limit 的容器会把整个节点当成自己的。

卷会更新,环境变量不会

实测 · 本机执行输出
  /etc/podinfo/labels:['app="demo"', 'tier="web"'];annotations:['owner="team-a"'];cpu_limit=500
  用 kubectl label 把 tier 改成 db 并新增 extra=yes:文件 1 秒后变化:['app="demo"', 'extra="yes"', 'tier="db"']
  同一时刻环境变量 LABEL_TIER 仍是 'web'(创建时的值),而文件里已经是 tier="db"——环境变量在容器启动时一次性取值;要看到变化就得读卷里的文件
  文件路径是符号链接:annotations -> ..data/annotations;cpu_limit -> ..data/cpu_limit;labels -> ..data/labels(同 ConfigMap 的 ..data 原子切换)

downwardAPI 卷里的文件:labels 是 app="demo"、tier="web" 每行一个,annotations 里有 owner="team-a",cpu_limit 是 500。用 kubectl label 把 tier 改成 db 并新增 extra=yes:文件 1 秒后变成 app="demo"、extra="yes"、tier="db";而环境变量 LABEL_TIER 仍是创建时的 web。文件路径是 ..data 下的符号链接,更新是原子切换。与 ConfigMap 挂载常见的 60~90 秒延迟(见 k56)不同,这里约 1 秒就同步了。

白名单

实测 · 本机执行输出
  环境变量 fieldPath: metadata.labels(整个 map,环境变量里):退出码 1;The Pod "bad" is invalid: spec.containers[0].env[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.labels": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.hostIPs", "status.podIP", "status.podIPs"
  环境变量 fieldPath: status.phase:退出码 1;The Pod "bad" is invalid: spec.containers[0].env[0].valueFrom.fieldRef.fieldPath: Unsupported value: "status.phase": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.hostIPs", "status.podIP", "status.podIPs"
  环境变量 fieldPath: spec.containers[0].name:退出码 1;The Pod "bad" is invalid: spec.containers[0].env[0].valueFrom.fieldRef.fieldPath: Invalid value: "spec.containers[0].name": error converting fieldPath: field label not supported: spec.containers[0].name
  环境变量 fieldPath: metadata.name 拼写错 metadata.nam:退出码 1;The Pod "bad" is invalid: spec.containers[0].env[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.nam": error converting fieldPath: field label not supported: metadata.nam
  卷里写 status.phase:退出码 1;* spec.volumes[0].downwardAPI.fieldRef.fieldPath: Unsupported value: "status.phase": supported values: "metadata.annotations", "metadata.labels", "metadata.name", "metadata.namespace", "metadata.uid"

不支持的写法在创建时就被拒:环境变量 fieldPath: metadata.labels(整个 map)→ Unsupported value,supported values 只有 metadata.name、metadata.namespace、metadata.uid、spec.nodeName、spec.serviceAccountName、status.hostIP(s)、status.podIP(s);status.phase 同样被拒;spec.containers[0].name、拼错的 metadata.nam 报 field label not supported。卷里支持的 fieldPath 是另一张表:metadata.annotations、metadata.labels、metadata.name、metadata.namespace、metadata.uid,写 status.phase 被拒。要别的信息,还得调 API Server(并配 RBAC)。

分步演示

Downward API
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。三节点 kind 集群 kx-main(Kubernetes v1.37.0),Pod 固定在 worker;环境变量与文件内容用 kubectl exec 读取;“1 秒”是 1 秒间隔轮询的结果;非法 fieldPath 用 --dry-run=server 验证;每种情形各一次(各次运行里 Pod 的 uid、IP 不同)
    • 没有验证:status.podIPs / hostIPs 在双栈下的格式;limits.ephemeral-storage 与 hugepages 的 resourceFieldRef;环境变量 expansion($(VAR))引用 Downward API 变量;downwardAPI 卷的 mode 与 defaultMode;projected 卷里组合 downwardAPI(见 k55);Pod 里的 /etc/hosts、hostname 与 Downward API 的关系
    京ICP备2026067502号-1京公网安备11010502063487号