Downward API
有兩種方式可以將 Pod 和 Container 字段呈現給運行中的容器:
- 環境變量
- 卷文件
這兩種呈現 Pod 和 Container 字段的方式統稱為 Downward API。
使用環境變量的方式
用 Pod 字段作為環境變量的值
Pod 的配置文件
cat test_pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: dapi-envars-fieldref
spec:
containers:
- name: test-container
image: busybox
command: [ "sh", "-c"]
args:
- while true; do
echo -en '\n';
printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE;
printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT;
sleep 10;
done;
env:
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: MY_POD_SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
restartPolicy: Never
這個配置文件中,你可以看到五個環境變量。env 字段是一個 EnvVars. 對象的數組。 數組中第一個元素指定 MY_NODE_NAME 這個環境變量從 Pod 的 spec.nodeName 字段獲取變量值。 同樣,其它環境變量也是從 Pod 的字段獲取它們的變量值。
說明: 本示例中的字段是 Pod 字段,不是 Pod 中 Container 的字段。
# 創建Pod
# kubectl apply -f test_pod.yaml
pod/dapi-envars-fieldref created
# 驗證 Pod 中的容器運行正常
# kubectl get pods
NAME READY STATUS RESTARTS AGE
dapi-envars-fieldref 1/1 Running 0 5s
# 查看容器日志,輸出信息顯示了所選擇的環境變量的值
# kubectl logs dapi-envars-fieldref
develop-worker-2
dapi-envars-fieldref
default
10.0.2.110
default
要了解為什么這些值在日志中,請查看配置文件中的command和 args字段。 當容器啟動時,它將五個環境變量的值寫入stdout。每十秒重復執行一次。
接下來,通過打開一個 Shell 進入 Pod 中運行的容器:
# kubectl exec -it dapi-envars-fieldref /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
# 在 Shell 中,查看環境變量
/ # printenv
MY_POD_SERVICE_ACCOUNT=default
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.3.255.1:443
DASHBOARD_KUBERNETES_DASHBOARD_SERVICE_HOST=10.3.255.88
HOSTNAME=dapi-envars-fieldref
DASHBOARD_KUBERNETES_DASHBOARD_PORT_9090_TCP_ADDR=10.3.255.88
SHLVL=1
HOME=/root
DASHBOARD_KUBERNETES_DASHBOARD_PORT_9090_TCP_PORT=9090
DASHBOARD_KUBERNETES_DASHBOARD_PORT_9090_TCP_PROTO=tcp
DASHBOARD_KUBERNETES_DASHBOARD_SERVICE_PORT=9090
DASHBOARD_KUBERNETES_DASHBOARD_PORT=tcp://10.3.255.88:9090
MY_POD_NAMESPACE=default
DASHBOARD_KUBERNETES_DASHBOARD_PORT_9090_TCP=tcp://10.3.255.88:9090
TERM=xterm
MY_POD_IP=10.0.2.110
KUBERNETES_PORT_443_TCP_ADDR=10.3.255.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
MY_NODE_NAME=develop-worker-2
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.3.255.1:443
KUBERNETES_SERVICE_HOST=10.3.255.1
PWD=/
DASHBOARD_KUBERNETES_DASHBOARD_SERVICE_PORT_HTTP=9090
MY_POD_NAME=dapi-envars-fieldref
用 Container 字段作為環境變量的值
包含一個容器的 Pod 的配置文件
# cat test_pod1.yaml
apiVersion: v1
kind: Pod
metadata:
name: dapi-envars-resourcefieldref
spec:
containers:
- name: test-container
image: busybox
command: [ "sh", "-c"]
args:
- while true; do
echo -en '\n';
printenv MY_CPU_REQUEST MY_CPU_LIMIT;
printenv MY_MEM_REQUEST MY_MEM_LIMIT;
sleep 10;
done;
resources:
requests:
memory: "32Mi"
cpu: "125m"
limits:
memory: "64Mi"
cpu: "250m"
env:
- name: MY_CPU_REQUEST
valueFrom:
resourceFieldRef:
containerName: test-container # 上面設置的容器名稱
resource: requests.cpu
- name: MY_CPU_LIMIT
valueFrom:
resourceFieldRef:
containerName: test-container
resource: limits.cpu
- name: MY_MEM_REQUEST
valueFrom:
resourceFieldRef:
containerName: test-container
resource: requests.memory
- name: MY_MEM_LIMIT
valueFrom:
resourceFieldRef:
containerName: test-container
resource: limits.memory
restartPolicy: Never
這個配置文件中,你可以看到四個環境變量。env 字段是一個 EnvVars. 對象的數組。數組中第一個元素指定 MY_CPU_REQUEST 這個環境變量從 Container 的 requests.cpu 字段獲取變量值。同樣,其它環境變量也是從 Container 的字段獲取它們的變量值。
說明: 本例中使用的是 Container 的字段而不是 Pod 的字段。
# 創建Pod
# kubectl apply -f test_pod1.yaml
pod/dapi-envars-resourcefieldref created
# 驗證 Pod 中的容器運行正常
# kubectl get pods
NAME READY STATUS RESTARTS AGE
dapi-envars-resourcefieldref 1/1 Running 0 6s
# 查看容器日志,輸出信息顯示了所選擇的環境變量的值
# kubectl logs dapi-envars-resourcefieldref
1
1
33554432
67108864
使用文件的方式
Pod字段
包含一個容器的 Pod的配置文件
# cat test_pod2.yaml
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: 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字段,不是Pod中容器的字段。
# 創建 Pod
# kubectl apply -f test_pod2.yaml
pod/kubernetes-downwardapi-volume-example created
# 驗證Pod中的容器運行正常
# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-downwardapi-volume-example 1/1 Running 0 47s
# 查看容器的日志,輸出顯示 labels 和 annotations 文件的內容
# kubectl logs kubernetes-downwardapi-volume-example
cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"
build="two"
builder="john-doe"
kubectl.kubernetes.io/last-applied-configuration="{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{\"build\":\"two\",\"builder\":\"john-doe\"},\"labels\":{\"cluster\":\"test-cluster1\",\"rack\":\"rack-22\",\"zone\":\"us-est-coast\"},\"name\":\"kubernetes-downwardapi-volume-example\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"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;\"],\"command\":[\"sh\",\"-c\"],\"image\":\"busybox\",\"name\":\"client-container\",\"volumeMounts\":[{\"mountPath\":\"/etc/podinfo\",\"name\":\"podinfo\"}]}],\"volumes\":[{\"downwardAPI\":{\"items\":[{\"fieldRef\":{\"fieldPath\":\"metadata.labels\"},\"path\":\"labels\"},{\"fieldRef\":{\"fieldPath\":\"metadata.annotations\"},\"path\":\"annotations\"}]},\"name\":\"podinfo\"}]}}\n"
kubernetes.io/config.seen="2021-05-17T15:31:02.676723051+08:00"
kubernetes.io/config.source="api"
# 進入 Pod 中運行的容器,打開一個 Shell
# kubectl exec -it kubernetes-downwardapi-volume-example -- sh
# 在該 Shell中,查看 labels 文件,輸出顯示 Pod 的所有標簽都已寫入 labels 文件
/ # cat /etc/podinfo/labels
cluster="test-cluster1"
rack="rack-22"
# 查看annotations文件,
/ # cat /etc/podinfo/annotations
build="two"
builder="john-doe"
kubectl.kubernetes.io/last-applied-configuration="{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{\"build\":\"two\",\"builder\":\"john-doe\"},\"labels\":{\"cluster\":\"test-cluster1\",\"rack\":\"rack-22\",\"zone\":\"us-est-coast\"},\"name\":\"kubernetes-downwardapi-volume-example\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"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;\"],\"command\":[\"sh\",\"-c\"],\"image\":\"busybox\",\"name\":\"client-container\",\"volumeMounts\":[{\"mountPath\":\"/etc/podinfo\",\"name\":\"podinfo\"}]}],\"volumes\":[{\"downwardAPI\":{\"items\":[{\"fieldRef\":{\"fieldPath\":\"metadata.labels\"},\"path\":\"labels\"},{\"fieldRef\":{\"fieldPath\":\"metadata.annotations\"},\"path\":\"annotations\"}]},\"name\":\"podinfo\"}]}}\n"
kubernetes.io/config.seen="2021-05-17T15:31:02.676723051+08:00"
# 查看/etc/podinfo目錄下的文件
/ # ls -al /etc/podinfo/
total 4
drwxrwxrwt 3 root root 120 May 17 07:31 .
drwxr-xr-x 1 root root 4096 May 17 07:31 ..
drwxr-xr-x 2 root root 80 May 17 07:31 ..2021_05_17_07_31_03.519523692
lrwxrwxrwx 1 root root 31 May 17 07:31 ..data -> ..2021_05_17_07_31_03.519523692
lrwxrwxrwx 1 root root 18 May 17 07:31 annotations -> ..data/annotations
lrwxrwxrwx 1 root root 13 May 17 07:31 labels -> ..data/labels
# 在輸出中可以看到,labels 和 annotations 文件都在一個臨時子目錄中。 在這個例子是..2021_05_17_07_31_03.519523692。 在 /etc/podinfo 目錄中,..data 是一個指向臨時子目錄 的符號鏈接。/etc/podinfo 目錄中,labels 和 annotations 也是符號鏈接。
/ # ls -al /etc/podinfo/..2021_05_17_07_31_03.519523692/
total 8
drwxr-xr-x 2 root root 80 May 17 07:31 .
drwxrwxrwt 3 root root 120 May 17 07:31 ..
-rw-r--r-- 1 root root 1102 May 17 07:31 annotations
-rw-r--r-- 1 root root 58 May 17 07:31 labels
# 用符號鏈接可實現元數據的動態原子性刷新;更新將寫入一個新的臨時目錄, 然后通過使用rename(2) 完成 ..data 符號鏈接的原子性更新。
# 說明: 如果容器以 subPath卷掛載方式來使用 Downward API,則該容器無法收到更新事件。
Container 字段
包含一個容器的 Pod 的配置文件
# cat test_pod3.yaml
apiVersion: v1
kind: Pod
metadata:
name: kubernetes-downwardapi-volume-example-2
spec:
containers:
- name: client-container
image: busybox
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 的容器中 limits.cpu 字段的值應保存在名為 cpu_limit 的文件中。
# 創建Pod
# kubectl apply -f test_pod3.yaml
pod/kubernetes-downwardapi-volume-example-2 created
# 查看pod是否運行正常
# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-downwardapi-volume-example-2 1/1 Running 0 7s
# 打開一個 Shell,進入 Pod 中運行的容器
# kubectl exec -it kubernetes-downwardapi-volume-example-2 -- sh
# 在 Shell 中,查看 cpu_limit 文件
/ # cat /etc/podinfo/cpu_limit
250
/ #
# 可以使用同樣的命令查看 cpu_request、mem_limit 和 mem_request 文件
Downward API 的能力
下面這些信息可以通過環境變量和 downwardAPI 卷提供給容器:
-
能通過 fieldRef 獲得的:
- metadata.name - Pod 名稱
- metadata.namespace - Pod 名字空間
- metadata.uid - Pod 的 UID
- metadata.labels['
'] - Pod 標簽 的值 (例如, metadata.labels['mylabel']) - metadata.annotations['
'] - Pod 的注解 的值(例如, - 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 - 節點 IP
- spec.serviceAccountName - Pod 服務帳號名稱, 版本要求 v1.4.0-alpha.3
- spec.nodeName - 節點名稱, 版本要求 v1.4.0-alpha.3
- status.hostIP - 節點 IP, 版本要求 v1.7.0-alpha.1
說明: 如果容器未指定 CPU 和內存限制,則 Downward API 默認將節點可分配值 視為容器的 CPU 和內存限制。
Downward API的動機
對於容器來說,有時候擁有自己的信息是很有用的,可避免與 Kubernetes 過度耦合。 Downward API 使得容器使用自己或者集群的信息,而不必通過 Kubernetes 客戶端或 API 服務器來獲得。
一個例子是有一個現有的應用假定要用一個非常熟悉的環境變量來保存一個唯一標識。 一種可能是給應用增加處理層,但這樣是冗余和易出錯的,而且它違反了低耦合的目標。 更好的選擇是使用 Pod 名稱作為標識,把 Pod 名稱注入這個環境變量中。