背景:ConfigMap和Secrets对于向应用传递pod调度、运行前预设的数据(如配置数据)是可行的。但是对于那些不能预先知道的数据(比如podIP、nodeName、RS等自动创建的pod的name)、那些已经在别处定义的数据(比如pod的label和annotation)呢?我们不想在多个地方重复同样的数据。

对于此类问题,可以通过使用Downward API解决。Downward API允许我们通过环境变量或文件(在downwardAPI卷中)传送pod和它环境的元数据。Downward API方式主要是将pod的specification和status中取得的数据作为环境变量和文件的值,如图8.1所示。
image.png

8.1.1 了解可用的元数据

Downward API可以将pod和容器的元数据传递给在它们内部运行的进程。目前我们可以给容器传递以下数据:

  • The pod’s name
  • The pod’s IP address
  • The namespace the pod belongs to
  • The name of the node the pod is running on
  • The name of the service account the pod is running under
  • The CPU and memory requests for each container

(requests: 请求的最小计算资源数量)

  • The CPU and memory limits for each container

(limits: 允许的最大计算资源数量)

  • The pod’s labels (只可以通过卷暴露)
  • The pod’s annotations (只可以通过卷暴露)

service account是pod访问API服务器时用来进行身份验证的账户。(第12章讲解)

CPU和内存的requests和limits它们代表了容器的请求使用量和容器被允许的最大使用量。(第14章讲解)

列表中的大部分既可以通过环境变量也可以通过downwardAPI卷传递给容器,但是label和annotation只可以通过卷暴露。

8.1.2 通过环境变量暴露元数据

创建一个简单的单容器pod

  1. cd /root/k8s/
  2. cat >downward-api-env.yaml <<'EOF'
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: downward
  7. spec:
  8. containers:
  9. - name: main
  10. image: 10.0.0.10:5000/luksa/busybox
  11. command: ["sleep", "9999999"]
  12. resources:
  13. requests:
  14. cpu: 15m
  15. memory: 100Ki
  16. limits:
  17. cpu: 100m
  18. memory: 6Mi
  19. env:
  20. - name: POD_NAME
  21. valueFrom:
  22. fieldRef:
  23. #引用metadata.name字段(值为downard)
  24. fieldPath: metadata.name
  25. - name: POD_NAMESPACE
  26. valueFrom:
  27. fieldRef:
  28. fieldPath: metadata.namespace
  29. - name: POD_IP
  30. valueFrom:
  31. fieldRef:
  32. fieldPath: status.podIP
  33. - name: NODE_NAME
  34. valueFrom:
  35. fieldRef:
  36. fieldPath: spec.nodeName
  37. - name: SERVICE_ACCOUNT
  38. valueFrom:
  39. fieldRef:
  40. fieldPath: spec.serviceAccountName
  41. - name: CONTAINER_CPU_REQUEST_MILLICORES
  42. valueFrom:
  43. resourceFieldRef:
  44. #引用spec.containers.resources.cpu
  45. resource: requests.cpu
  46. #对于resource字段,我们定义一个divisor(除数,基数单位)
  47. divisor: 1m #千分之一个CPU核(时间片)
  48. - name: CONTAINER_MEMORY_LIMIT_KIBIBYTES
  49. valueFrom:
  50. resourceFieldRef:
  51. resource: limits.memory
  52. divisor: 1Ki
  53. EOF
  54. kubectl create -f downward-api-env.yaml

对于CPU资源请求量和使用限制可以被设定为1,表示整颗CPU的计算能力,也可以设定为1m,即千分之一核的计算能力。
对于内存的资源请求和使用限制可以设定为1(byte),也可以是1k(kilobyte),1Ki(kibibyte),1M(megabyte),1Mi(mebibyte),等等。

(资源计量)The serialization format:

::= (Note that may be empty, from the “” case in .)
::= |
::= | . | . | .
::= “+” | “-“
::= |
::= 0 | 1 | … | 9
::= | |
::= Ki | Mi | Gi | Ti | Pi | Ei (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
::= “e” | “E”
::= m | “” | k | M | G | T | P | E (Note that 1024 = 1Ki but 1000 = 1k ; I didn’t choose the capitalization.)

m(milli,千分之一),1k(kilobyte),1Ki(kibibyte),1M(megabyte),1Mi(mebibyte)
kilo: 十进制千
kibi: 二进制千
mega: 十进制百万
mebi: 二进制百万

观察pod中的环境变量

kubectl get po downward -o yaml
image.png

kubectl describe po downward
#部分环境变量的值没有显示,需要kubectl exec downward — env查看。
image.png

kubectl exec downward -- env
image.png

8.1.3 通过downwardAPI卷中文件来传递元数据

cd /root/k8s/

cat >downward-api-volume.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: downward
  #将通过downwardAPI卷来暴露这些labels和annotations。
  labels:
    foo: bar
  annotations:
    key1: value1
    key2: |
      multi
      line
      value
spec:
  containers:
  - name: main
    image: 10.0.0.10:5000/luksa/busybox
    command: ["sleep", "9999999"]
    resources:
      requests:
        cpu: 15m
        memory: 100Ki
      limits:
        cpu: 100m
        memory: 6Mi
    volumeMounts:
    #在/etc/downward目录挂载downward卷。
    - name: downward
    #mountPath可以不是容器中存在的目录。
      mountPath: /etc/downward
  volumes:
  - name: downward
  #定义一个downwardAPI卷。
    downwardAPI:
      items:
      - path: "podName"
      #pod的名称将被写入/etc/downward/podName文件。
        fieldRef:
          fieldPath: metadata.name
      - path: "podNamespace"
        fieldRef:
          fieldPath: metadata.namespace
      - path: "labels"
      #pod的标签将被写入/etc/downward/labels文件。
        fieldRef:
          fieldPath: metadata.labels
      - path: "annotations"
      #pod的注解将被写入/etc/downward/annotations文件。
        fieldRef:
          fieldPath: metadata.annotations
      - path: "containerCpuRequestMilliCores"
        resourceFieldRef:
          containerName: main
          resource: requests.cpu
          divisor: 1m
      - path: "containerMemoryLimitBytes"
        resourceFieldRef:
          containerName: main
          resource: limits.memory
          divisor: 1
EOF

kubectl create -f downward-api-volume.yaml

我们定义了一下叫作downward的卷,并通过/etc/downward目录挂载到容器中。卷所包含的文件(path)会通过spec.volumes中的downwardAPI.items属性来定义。在downwardAPI.items中指定了元数据被写入到的path(文件名),如图8.3所示。
image.png
image.png
#downwardAPI卷也有 ..data 符号链接。

注意:与configMap卷、secret卷一样,可以通过defaultMode属性改变文件的默认访问权限。

每个文件对应一个volumes.downwardAPI.items。挂载目录中文件的内容与上一例中环境变量的值是一样的。
image.png
#使用key=value格式;因key2的值包含多行,使用了 \n 。

修改labels和annotation

可以在pod运行的时候修改label和annotation,修改之后,Kubernetes会更新保存它们的文件 (比如上例中downwardAPI卷挂载目录下的对应文件labels、annotations),让pod可以获取最新的数据。

因为环境变量的值无法在pod或容器被创建之后更新,在环境变量方式下,一旦label和annotation被修改,新的值将无法暴露。

在卷的sepcification中引用容器级的元数据

image.png
理解很明显,因为卷是定义在pod级的,而不是容器级的。当我们在卷定义(specification)中引用某个容器的resources字段时,我们需要明确指定引用的容器的名称。这个规则对于只包含单容器的pod同样适用。

使用卷的方式来暴露容器的resource requests和limits的好处是:可以传递一个容器的resources字段到另一个容器(必须在同一个pod中)(通过挂载同一个卷?)。使用环境变量的方式,一个容器只能被传递它自身resource requests和limits的信息。

何时使用Downward API

当应用程序需要环境变量中的某些数据时,使用DownwardAPI会很有用。不过通过Downward API的方式获取的元数据是相当有限的,如果需要获取更多的元数据,需要使用直接访问 Kubemetes API Server的方式。