通过文件将 Pod 信息呈现给容器

此页面描述 Pod 如何使用 DownwardAPIVolumeFile 把自己的信息呈现给 Pod 中运行的容器。 DownwardAPIVolumeFile 可以呈现 Pod 和容器的字段。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:

要获知版本信息,请输入 kubectl version.

Downward API

有两种方式可以将 Pod 和 Container 字段呈现给运行中的容器:

这两种呈现 Pod 和 Container 字段的方式都称为 "Downward API"。

存储 Pod 字段

在这个练习中,你将创建一个包含一个容器的 Pod。Pod 的配置文件如下:

apiVersion: v1
kind: Pod
metadata:
  name: kubernetes-downwardapi-volume-example
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
  annotations:
    build: two
    builder: john-doe
spec:
  containers:
    - name: client-container
      image: k8s.gcr.io/busybox
      command: ["sh", "-c"]
      args:
      - while true; do
          if [[ -e /etc/podinfo/labels ]]; then
            echo -en '\n\n'; cat /etc/podinfo/labels; fi;
          if [[ -e /etc/podinfo/annotations ]]; then
            echo -en '\n\n'; cat /etc/podinfo/annotations; fi;
          sleep 5;
        done;
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: "labels"
            fieldRef:
              fieldPath: metadata.labels
          - path: "annotations"
            fieldRef:
              fieldPath: metadata.annotations

在配置文件中,你可以看到 Pod 有一个 downwardAPI 类型的卷,并且挂载到容器中的 /etc/podinfo 目录。

查看 downwardAPI 下面的 items 数组。 每个数组元素都是一个 DownwardAPIVolumeFile 对象。 第一个元素指示 Pod 的 metadata.labels 字段的值保存在名为 labels 的文件中。 第二个元素指示 Pod 的 annotations 字段的值保存在名为 annotations 的文件中。

创建 Pod:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-volume.yaml

验证Pod中的容器运行正常:

kubectl get pods

查看容器的日志:

kubectl logs kubernetes-downwardapi-volume-example

输出显示 labelsannotations 文件的内容:

cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

build="two"
builder="john-doe"

进入 Pod 中运行的容器,打开一个 Shell:

kubectl exec -it kubernetes-downwardapi-volume-example -- sh

在该 Shell中,查看 labels 文件:

/# cat /etc/podinfo/labels

输出显示 Pod 的所有标签都已写入 labels 文件。

cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

同样,查看 annotations 文件:

/# cat /etc/podinfo/annotations

查看 /etc/podinfo 目录下的文件:

/# ls -laR /etc/podinfo

在输出中可以看到,labelsannotations 文件都在一个临时子目录中。 在这个例子,..2982_06_02_21_47_53.299460680。 在 /etc/podinfo 目录中,..data 是一个指向临时子目录 的符号链接。/etc/podinfo 目录中,labelsannotations 也是符号链接。

drwxr-xr-x  ... Feb 6 21:47 ..2982_06_02_21_47_53.299460680
lrwxrwxrwx  ... Feb 6 21:47 ..data -> ..2982_06_02_21_47_53.299460680
lrwxrwxrwx  ... Feb 6 21:47 annotations -> ..data/annotations
lrwxrwxrwx  ... Feb 6 21:47 labels -> ..data/labels

/etc/..2982_06_02_21_47_53.299460680:
total 8
-rw-r--r--  ... Feb  6 21:47 annotations
-rw-r--r--  ... Feb  6 21:47 labels

用符号链接可实现元数据的动态原子性刷新;更新将写入一个新的临时目录, 然后通过使用 rename(2) 完成 ..data 符号链接的原子性更新。

退出 Shell:

/# exit

存储容器字段

前面的练习中,你将 Pod 字段保存到 DownwardAPIVolumeFile 中。 接下来这个练习,你将存储 Container 字段。这里是包含一个容器的 Pod 的配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: kubernetes-downwardapi-volume-example-2
spec:
  containers:
    - name: client-container
      image: k8s.gcr.io/busybox:1.24
      command: ["sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          if [[ -e /etc/podinfo/cpu_limit ]]; then
            echo -en '\n'; cat /etc/podinfo/cpu_limit; fi;
          if [[ -e /etc/podinfo/cpu_request ]]; then
            echo -en '\n'; cat /etc/podinfo/cpu_request; fi;
          if [[ -e /etc/podinfo/mem_limit ]]; then
            echo -en '\n'; cat /etc/podinfo/mem_limit; fi;
          if [[ -e /etc/podinfo/mem_request ]]; then
            echo -en '\n'; cat /etc/podinfo/mem_request; fi;
          sleep 5;
        done;
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "250m"
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: "cpu_limit"
            resourceFieldRef:
              containerName: client-container
              resource: limits.cpu
              divisor: 1m
          - path: "cpu_request"
            resourceFieldRef:
              containerName: client-container
              resource: requests.cpu
              divisor: 1m
          - path: "mem_limit"
            resourceFieldRef:
              containerName: client-container
              resource: limits.memory
              divisor: 1Mi
          - path: "mem_request"
            resourceFieldRef:
              containerName: client-container
              resource: requests.memory
              divisor: 1Mi

在这个配置文件中,你可以看到 Pod 有一个 downwardAPI, 并且挂载到容器的 /etc/podinfo 目录。

查看 downwardAPI 下面的 items 数组。每个数组元素都是一个 DownwardAPIVolumeFile

第一个元素指定在名为 client-container 的容器中, 以 1m 所指定格式的 limits.cpu 字段的值应保存在名为 cpu_limit 的文件中。 divisor 字段是可选的,默认值为 1,表示 CPU 的核心和内存的字节。

创建Pod:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-volume-resources.yaml

打开一个 Shell,进入 Pod 中运行的容器:

kubectl exec -it kubernetes-downwardapi-volume-example-2 -- sh

在 Shell 中,查看 cpu_limit 文件:

/# cat /etc/podinfo/cpu_limit

你可以使用同样的命令查看 cpu_requestmem_limitmem_request 文件.

Downward API 的能力

下面这些信息可以通过环境变量和 downwardAPI 卷提供给容器:

  • 能通过 fieldRef 获得的:

    • metadata.name - Pod 名称
    • metadata.namespace - Pod 名字空间
    • metadata.uid - Pod 的 UID
    • metadata.labels['<KEY>'] - Pod 标签 <KEY> 的值 (例如:metadata.labels['mylabel']
    • metadata.annotations['<KEY>'] - Pod 的注解 <KEY> 的值 (例如:metadata.annotations['myannotation']
  • 能通过 resourceFieldRef 获得的:
    • 容器的 CPU 约束值
    • 容器的 CPU 请求值
    • 容器的内存约束值
    • 容器的内存请求值
    • 容器的巨页限制值(前提是启用了 DownwardAPIHugePages 特性门控
    • 容器的巨页请求值(前提是启用了 DownwardAPIHugePages 特性门控
    • 容器的临时存储约束值
    • 容器的临时存储请求值

此外,以下信息可通过 downwardAPI 卷从 fieldRef 获得:

  • metadata.labels - Pod 的所有标签,以 label-key="escaped-label-value" 格式显示,每行显示一个标签
  • metadata.annotations - Pod 的所有注解,以 annotation-key="escaped-annotation-value" 格式显示,每行显示一个标签

以下信息可通过环境变量获得:

  • status.podIP - Pod IP 地址
  • spec.serviceAccountName - Pod 服务帐号名称
  • spec.nodeName - 调度器总是尝试将 Pod 调度到的节点的名称
  • status.hostIP - Pod 分配到的节点的 IP

投射键名到指定路径并且指定文件权限

你可以将键名投射到指定路径并且指定每个文件的访问权限。 更多信息,请参阅 Secret

Downward API 的动机

对于容器来说,有时候拥有自己的信息是很有用的,可避免与 Kubernetes 过度耦合。 Downward API 使得容器使用自己或者集群的信息,而不必通过 Kubernetes 客户端或 API 服务器来获得。

一个例子是有一个现有的应用假定要用一个非常熟悉的环境变量来保存一个唯一标识。 一种可能是给应用增加处理层,但这样是冗余和易出错的,而且它违反了低耦合的目标。 更好的选择是使用 Pod 名称作为标识,把 Pod 名称注入这个环境变量中。

接下来

  • 参阅 PodSpec API 定义,该 API 定义 Pod 所需状态。
  • 参阅 Volume API 定义,该 API 在 Pod 中定义通用卷以供容器访问。
  • 参阅 DownwardAPIVolumeSource API 定义,该 API 定义包含 Downward API 信息的卷。
  • 参阅 DownwardAPIVolumeFile API 定义,该 API 包含对对象或资源字段的引用,用于在 Downward API 卷中填充文件。
  • 参阅 ResourceFieldSelector API 定义,该 API 指定容器资源及其输出格式。