Downward API

前面我们从 Pod 的原理到生命周期介绍了 Pod 的一些使用,作为 Kubernetes 中最核心的资源对象、最基本的调度单元,我们可以发现 Pod 中的属性还是非常繁多的,前面我们使用过一个volumes的属性,表示声明一个数据卷,我们可以通过命令kubectl explain pod.spec.volumes去查看该对象下面的属性非常多,前面我们只是简单使用了hostPath和emptyDir{}这两种模式,其中还有一种模式叫做downwardAPI,这个模式和其他模式不一样的地方在于它不是为了存放容器的数据也不是用来进行容器和宿主机的数据交换的,而是让 Pod 里的容器能够直接获取到这个 Pod 对象本身的一些信息。

目前Downward API提供了两种方式用于将 Pod 的信息注入到容器内部:

  • 环境变量:用于单个变量,可以将 Pod 信息和容器信息直接注入容器内部
  • Volume 挂载:将 Pod 信息生成为文件,直接挂载到容器内部中去

环境变量

我们通过Downward API来将 Pod 的 IP、名称以及所对应的 namespace 注入到容器的环境变量中去,然后在容器中打印全部的环境变量来进行验证,对应资源清单文件如下:(env-pod.yaml)

# 作用:是为了验证Downward API当中的环境变量的配置  之前介绍过volumes的hostPath和emptyDir{} 其实还有一种模式叫做Downward API
# 这个模式和其他模式不一样的地方在于它不是为了存放容器的数据也不是用来进行容器和宿主机的数据交换的,而是让 Pod 里的容器能够直接获取到这个 Pod 对象本身的一些信息
apiVersion: v1
kind: Pod
metadata:
  name: env-pod
  namespace: default
spec:
  containers:
    - name: env-pod
      image: busybox
      command: ["/bin/sh","-c","env","sleep 600"]
      env:                                      # 设置环境变量
        - name: POD_NAME                          # 要设置的环境变量的名称
          valueFrom:                              # 值来自于哪里
            fieldRef:                             # 是来自于一个属性的引用
              fieldPath: metadata.name            # 具体引用的路径   由于 Pod 的 name 和 namespace 属于元数据,是在 Pod 创建之前就已经定下来了的,所以我们可以使用 metata 就可以获取到了
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP   # 对于 Pod 的 IP 则不一样,因为我们知道 Pod IP 是不固定的,Pod 重建了就变了,它属于状态数据,所以我们使用 status 这个属性去获取

# 很疑惑status.podIP哪里来的啊?
# 如果想查看已经生成的一些pod的信息 可以通过命令 kubectl get pods pod的名称 -o yaml   就可以查看到pod的内部的一些具体信息 比如ip namespace ......  如果要获取其他的 就可以使用status.hostIP或者
# status.qosClass等信息都可以获取到的!  这就将pod的一些内部信息写入到了容器内的环境变量当中去了

# 通过环境变量获取这些信息的方式,不具备自动更新的能力。所以,一般情况下,都建议使用 Volume 文件的方式获取这些信息,因为通过 Volume 的方式挂载的文件在 Pod 中会进行热更新

我们可以看到上面我们使用了一种新的方式来设置 env 的值:valueFrom,由于 Pod 的 name 和 namespace 属于元数据,是在 Pod 创建之前就已经定下来了的,所以我们可以使用 metata 就可以获取到了,但是对于 Pod 的 IP 则不一样,因为我们知道 Pod IP 是不固定的,Pod 重建了就变了,它属于状态数据,所以我们使用 status 这个属性去获取。另外除了使用fieldRef获取 Pod 的基本信息外,还可以通过resourceFieldRef去获取容器的资源请求和资源限制信息。

我们直接创建上面的 Pod:

$ kubectl create -f env-pod.yaml
pod "env-pod" created

Pod 创建成功后,我们可以查看日志:

POD_IP=10.244.2.19
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
HOSTNAME=env-pod
SHLVL=1
HOME=/root
POD_NAME=env-pod
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.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
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
POD_NAMESPACE=default
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/

我们可以看到 Pod 的 IP、NAME、NAMESPACE 都通过环境变量打印出来了。

上面打印 Pod 的环境变量可以看到有很多内置的变量,其中大部分是系统自动为我们添加的,Kubernetes 会把当前命名空间下面的 Service 信息通过环境变量的形式注入到 Pod 中去:

$ kubectl get svc -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   4d21h

env-pod.yaml

# 作用:是为了验证Downward API当中的环境变量的配置  之前介绍过volumes的hostPath和emptyDir{} 其实还有一种模式叫做Downward API
# 这个模式和其他模式不一样的地方在于它不是为了存放容器的数据也不是用来进行容器和宿主机的数据交换的,而是让 Pod 里的容器能够直接获取到这个 Pod 对象本身的一些信息
apiVersion: v1
kind: Pod
metadata:
  name: env-pod
  namespace: default
spec:
  containers:
    - name: env-pod
      image: busybox
      command: ["/bin/sh","-c","env","sleep 600"]
      env:                                      # 设置环境变量
        - name: POD_NAME                          # 要设置的环境变量的名称
          valueFrom:                              # 值来自于哪里
            fieldRef:                             # 是来自于一个属性的引用
              fieldPath: metadata.name            # 具体引用的路径   由于 Pod 的 name 和 namespace 属于元数据,是在 Pod 创建之前就已经定下来了的,所以我们可以使用 metata 就可以获取到了
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP   # 对于 Pod 的 IP 则不一样,因为我们知道 Pod IP 是不固定的,Pod 重建了就变了,它属于状态数据,所以我们使用 status 这个属性去获取

# 很疑惑status.podIP哪里来的啊?
# 如果想查看已经生成的一些pod的信息 可以通过命令 kubectl get pods pod的名称 -o yaml   就可以查看到pod的内部的一些具体信息 比如ip namespace ......  如果要获取其他的 就可以使用status.hostIP或者
# status.qosClass等信息都可以获取到的!  这就将pod的一些内部信息写入到了容器内的环境变量当中去了

# 通过环境变量获取这些信息的方式,不具备自动更新的能力。所以,一般情况下,都建议使用 Volume 文件的方式获取这些信息,因为通过 Volume 的方式挂载的文件在 Pod 中会进行热更新

Volume挂载

Downward API除了提供环境变量的方式外,还提供了通过 Volume 挂载的方式去获取 Pod 的基本信息。接下来我们通过Downward API将 Pod 的 Label、Annotation 等信息通过 Volume 挂载到容器的某个文件中去,然后在容器中打印出该文件的值来验证,对应的资源清单文件如下所示:(volume-pod.yaml)

# 作用:Downward除了提供环境变量的方式外,还提供了通过volume挂载的方式去获取pod的基本信息
# 这个yaml清单当中我们将演示通过Downward将pod的LabelAnnotation等信息通过Volume挂载到容器的某个文件中去,然后在容器中打印出该文件的值来验证
apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
  namespace: default
  labels:
    k8s-app: test-volume
    node-env: test
  annotations:                             # 里面可以配置各种各样的参数 最终挂载到容器内的某个文件当中去
    own: youdianzhishi
    build: test
spec:
  volumes:                                  # 声明挂载
    - name: podinfo                           # 挂载的名称 这个在容器当中会被用到
      downwardAPI:                            # downwardAPI 的形式挂载
        items:                                # 参数
          - path: labels                        # path 表示路径 也就是说会生成labels文件 里面存放的是 metadata.labels里面的内容
            fieldRef:                           # 声明是来自于一个属性的引用
              fieldPath: metadata.labels        # 确定这个属性的引用是谁
          - path: annotations                   # 同上
            fieldRef:
              fieldPath: metadata.annotations
  containers:
    - name: volume-pod
      image: busybox
      args:
        - sleep
        - "3600"
      volumeMounts:                            # 挂载文件
        - name: podinfo                          # 挂载的名称 podinfo 这个要和上边的spec.volumes.name保持名称一致
          mountPath: /etc/podinfo                # 挂载到容器的目录的位置

# 通过挂载  最终会在容器内部的 /etc/podinfo目录下生成labels和annotations两个文件  两个文件当中的内容分别为metadata.labels和metadata.annotations里面的内容
# 通过环境变量获取这些信息的方式,不具备自动更新的能力。所以,一般情况下,都建议使用 Volume 文件的方式获取这些信息,因为通过 Volume 的方式挂载的文件在 Pod 中会进行热更新

我们将元数据 labels 和 annotaions 以文件的形式挂载到了/etc/podinfo目录下,创建上面的 Pod:

$ kubectl create -f volume-pod.yaml
pod "volume-pod" created

创建成功后,我们可以进入到容器中查看元信息是不是已经存入到文件中了:

$ kubectl exec -it volume-pod /bin/sh -n kube-system
/ # ls /etc/podinfo/
..2019_11_13_09_57_15.990445016/  annotations
..data/                           labels
/ # cat /etc/podinfo/labels
k8s-app="test-volume"
/ # cat /etc/podinfo/annotations
build="test"
kubectl.kubernetes.io/last-applied-configuration="{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{\"build\":\"test\",\"own\":\"youdianzhishi\"},\"labels\":{\"k8s-app\":\"test-volume\",\"node-env\":\"test\"},\"name\":\"volume-pod\",\"namespace\":\"kube-system\"},\"spec\":{\"containers\":[{\"args\":[\"sleep\",\"3600\"],\"image\":\"busybox\",\"name\":\"volume-pod\",\"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="2019-11-13T17:57:15.320894744+08:00"
kubernetes.io/config.source="api"

我们可以看到 Pod 的 Labels 和 Annotations 信息都被挂载到/etc/podinfo目录下面的 lables 和 annotations 文件了。

目前,Downward API支持的字段已经非常丰富了,比如:

1. 使用 fieldRef 可以声明使用:

spec.nodeName - 宿主机名字
status.hostIP - 宿主机IP
metadata.name - Pod的名字
metadata.namespace - PodNamespace
status.podIP - Pod的IP
spec.serviceAccountName - PodService Account的名字
metadata.uid - Pod的UID
metadata.labels['<KEY>'] - 指定<KEY>Label值
metadata.annotations['<KEY>'] - 指定<KEY>Annotation值
metadata.labels - Pod的所有Label
metadata.annotations - Pod的所有Annotation

2. 使用 resourceFieldRef 可以声明使用:

容器的 CPU limit
容器的 CPU request
容器的 memory limit
容器的 memory request

注意:

需要注意的是,Downward API能够获取到的信息,一定是 Pod 里的容器进程启动之前就能够确定下来的信息。而如果你想要获取 Pod 容器运行后才会出现的信息,比如,容器进程的 PID,那就肯定不能使用Downward API了,而应该考虑在 Pod 里定义一个 sidecar 容器来获取了

在实际应用中,如果你的应用有获取 Pod 的基本信息的需求,一般我们就可以利用Downward API来获取基本信息,然后编写一个启动脚本或者利用initContainer将 Pod 的信息注入到我们容器中去,然后在我们自己的应用中就可以正常的处理相关逻辑了。

除了通过 Downward API 可以获取到 Pod 本身的信息之外,其实我们还可以通过映射其他资源对象来获取对应的信息,比如 Secret、ConfigMap 资源对象,同样我们可以通过环境变量和挂载 Volume 的方式来获取他们的信息,但是,通过环境变量获取这些信息的方式,不具备自动更新的能力。所以,一般情况下,都建议使用 Volume 文件的方式获取这些信息,因为通过 Volume 的方式挂载的文件在 Pod 中会进行热更新。

volume-pod.yaml

# 作用:Downward除了提供环境变量的方式外,还提供了通过volume挂载的方式去获取pod的基本信息
# 这个yaml清单当中我们将演示通过Downward将pod的LabelAnnotation等信息通过Volume挂载到容器的某个文件中去,然后在容器中打印出该文件的值来验证
apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
  namespace: default
  labels:
    k8s-app: test-volume
    node-env: test
  annotations:                             # 里面可以配置各种各样的参数 最终挂载到容器内的某个文件当中去
    own: youdianzhishi
    build: test
spec:
  volumes:                                  # 声明挂载
    - name: podinfo                           # 挂载的名称 这个在容器当中会被用到
      downwardAPI:                            # downwardAPI 的形式挂载
        items:                                # 参数
          - path: labels                        # path 表示路径 也就是说会生成labels文件 里面存放的是 metadata.labels里面的内容
            fieldRef:                           # 声明是来自于一个属性的引用
              fieldPath: metadata.labels        # 确定这个属性的引用是谁
          - path: annotations                   # 同上
            fieldRef:
              fieldPath: metadata.annotations
  containers:
    - name: volume-pod
      image: busybox
      args:
        - sleep
        - "3600"
      volumeMounts:                            # 挂载文件
        - name: podinfo                          # 挂载的名称 podinfo 这个要和上边的spec.volumes.name保持名称一致
          mountPath: /etc/podinfo                # 挂载到容器的目录的位置

# 通过挂载  最终会在容器内部的 /etc/podinfo目录下生成labels和annotations两个文件  两个文件当中的内容分别为metadata.labels和metadata.annotations里面的内容
# 通过环境变量获取这些信息的方式,不具备自动更新的能力。所以,一般情况下,都建议使用 Volume 文件的方式获取这些信息,因为通过 Volume 的方式挂载的文件在 Pod 中会进行热更新
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐