Kubernetes通过downwardAPI传递元数据

  应用往往需要获取所运行环境的一些信息,包括应用自身以及集群中其他组件的信息。Kubernetes可以通过环境变量以及DNS进行服务发现,但其他信息如何处理呢?下面将介绍特定pod和容器元数据如何被传递到容器,了解在容器中运行的应用如何便捷地与Kubernetes API服务器进行交互,从而获取在集群中部署资源的信息,并且进一步了解如何创建和修改这些资源。

 

1.通过Downward API传递元数据

  Kubernetes可以环境变量或者configMap和secret卷向应用传递配置数据。这对于pod调度、运行前预设的数据是可行的。但是对于那些不能预先知道的数据,比如pod的IP、主机名或者是pod自身的名称(当名称被生成,比如当pod通过ReplicaSet或类似的控制器生成时)呢?此外,对于那些己经在别处定义的数据,比如pod的标签和注解呢?不想在多个地方重复保留同样的数据。

  对于此类问题,可以通过使用Kubernetes Downward API解决。Downward API允许通过环境变量或者文件(在downwardAPI卷中)的传递pod的元数据。不要对这个名称产生困惑,Downward API的方式并不像REST endpoint那样需要通过访问的方式获取数据。这种方式主要是将在pod的定义和状态中取得的数据作为环境变量和文件的值。如图8.1所示。

 

1.1 了解可用元数据

  Downward API可以给在pod中运行的进程暴露pod的元数据。目前可以给容器传递以下数据:

    • pod的名称
    • pod的IP
    • pod所在的命名空间
    • pod运行节点的名称
    • pod运行所归属的服务账户的名称 •每个容器请求的CPU和内存的使用量
    • 每个容器可以使用的CPU和内存的限制
    • pod的标签
    • pod的注解

  这个清单中所列举的大部分项目,除了服务账户、CPU和内存的请求和限制概念,其他都无须进一步解释。其它篇章讲解。现在需了解,服务账户是pod访问API服务器时用来进行身份验证的账户。

  列表中的大部分项目既可以通过环境变量也可以通过downwardAPI卷传递给容器,但是标签和注解只可以通过卷暴露。部分数据可以通过其他方式获取(例如,可以直接从操作系统获取),但是DownwardAPI提供了一种更加便捷的方式。

  来看一个向容器化的进程传递元数据的例子。

 

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

  首先了解如何通过环境变量的方式将pod和容器的元数据传递到容器中。根据如下列出的manifest创建一个简单的单容器。

代码8.1 在环境变量中使用downwardAPI: downward-api-env.yaml
apiVersion: v1
kind: Pod
metadata:
  name: downward
spec:
  containers:
  - name: main
    image: busybox
    command: ["sleep", "9999999"]
    resources:
      requests:
        cpu: 15m
        memory: 100Ki
      limits:
        cpu: 100m
        memory: 4Mi
    env:
    - name: POD_NAME                           #引用pod manifest中的元数据名称字段,而不是设定一个具体的值
      valueFrom:
        fieldRef:
          fieldPath: metadata.name
    - name: POD_NAMESPACE
      valueFrom:
        fieldRef:
          fieldPath: metadata.namespace
    - name: POD_IP
      valueFrom:
        fieldRef:
          fieldPath: status.podIP
    - name: NODE_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName
    - name: SERVICE_ACCOUNT
      valueFrom:
        fieldRef:
          fieldPath: spec.serviceAccountName
    - name: CONTAINER_CPU_REQUEST_MILLICORES        #容器请求的CPU和内存使用量是引用resourceFieldRef字段,而不是fieldRef字段
      valueFrom:
        resourceFieldRef:
          resource: requests.cpu
          divisor: 1m                                                      #对于资源相关的字段,定义一个基数单位,从而生成每一部分的值
    - name: CONTAINER_MEMORY_LIMIT_KIBIBYTES
      valueFrom:
        resourceFieldRef:
          resource: limits.memory
          divisor: 1Ki

  当进程在运行时,它可以获取所有在pod的定义文件中设定的环境变量。图8-2展示了所有的环境变量以及变量值的来源。pod的名称、IP和命名空间可以通过pod_NAME、pod_IP和pod_NAMESPACE这几个环境变量分别暴露。容器运行的节点的名称可以通过NODE_NAME变量暴露。同样,服务账户可以使用环境变量SERVICE_ACCOUNT。也可以创建两个环境变量来保存容器请求使用的CPU的数量,以及容器被最大允许使用的内存数量。

  对于暴露资源请求和使用限制的环境变量,可以会设定一个基数单位。实际的资源请求直和限制直除以这个基数单位,所得的结果通过环境变量暴露出去。在前面的例子中,设定CPU请求的基数为1m(即1 millicore,也就是千分之一核CPU)。当设置资源请求15m时,环境变量CONTAINER_CPU_REQUEST_MILLICORES的值就是15。同样,我们设定内存的使用限制为4Mi(4 mebibytes),设定基数为1Ki(l Kbibyte),则环境变量 CONTAINER_MEMORY_LIMIT_KIBIBYTES的值就是 4096。

  对于CPU资源请求量和使用限制可以被设定为1,也就意味着整颗CPU的计算能力,也可以设定为1m,即千分之一核的计算能力。对于内存的资源请求和使用限制可以设定为1 (字节),也可以是1k(kilobute)或1Ki (kbibute),同样也可以设为1M (megavyte)或者1Mi(mebibyte),等等。

  在完成创建pod后,可以使用kubectl exec命令来查看容器中的所有环境变量,如下面的代码清单所示。

#代码 8.2 downward pod中的环境变量
$ kubectl exec downward env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=downward
CONTAINER_MEMORY_LIMIT_KIBIBYTES=4096
POD_NAME=downward
POD_NAMESPACE=default
POD_IP=10.0.0.10
NODE_NAME=gke-kubia-default-pool-32a2cac8-sgl7
SERVICE_ACCOUNT=default
CONTAINER_CPU_REQUEST_MILLICORES=15
KUBERNETES_SERVICE_HOST=10.3.240.1
KUBERNETES_SERVICE_PORT=443
...

  所有在这个容器中运行的进程都可以读取并使用它们需要的变量。

 

1.3 通过downwardAPI卷来传递元数据

  如果更倾向于使用文件的方式而不是环境变量的方式暴露元数据,可以定义一个downwardAPI卷并挂载到容器中。由于不能通过环境变量暴露,所以必须使用downwardAPI卷来暴露pod标签或注解。为什么不能后面讨论原因。

  与环境变量一样,需要显示地指定元数据字段来暴露份进程。下面我们将把前面的示例从使用环境变量修改为使用存储卷,如下面的代码清单所示。

#代码8.3 —个带有downwardAPi卷的pod示例:downward-api-volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: downward
  labels:
    foo: bar
  annotations:             #通过downwardAPI卷来暴露这些标签和注解
    key1: value1
    key2: |
      multi
      line
      value
spec:
  containers:
  - name: main
    image: busybox
    command: ["sleep", "9999999"]
    resources:
      requests:
        cpu: 15m
        memory: 100Ki
      limits:
        cpu: 100m
        memory: 4Mi
    volumeMounts:                                   #在/etc/downward目录下挂载这个downward卷
    - name: downward
      mountPath: /etc/downward
  volumes:                              
  - name: downward                               #通过将卷名字设定为downward来定义一个downwardAPI卷
    downwardAPI:
      items:
      - path: "podName"                           #pod的名称(来自manifest文件中的metadata.name字段)将被写入podName中
        fieldRef:
          fieldPath: metadata.name
      - path: "podNamespace"
        fieldRef:
          fieldPath: metadata.namespace
      - path: "labels"                                      #pod的标签将被保存到/etc/downward/labels文件中
        fieldRef:
          fieldPath: metadata.labels
      - path: "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

  现在没有通过环境变量来传递元数据,而是定义了一个叫作downward的卷,并且通过/etc/downward目录挂载到我们的容器中。卷所包含的文件会通过卷定义中的downwardAPI.items属性来定义。

  对于想要在文件中保存的每一个pod级的字段或者容器资源字段,都分别在downwardAPI.items中说明了元数据被保存和引用的path(文件名),如图8.3所示。

  从之前列表的manifest中删除原来的pod,并且新建一个pod。然后查看己挂载到downwardAPI卷目录的内容,存储卷被挂载在/etc/downward/目录下,列出目录中的文件,如下面的代码清单所示。

#代码8.4 downwordAPI卷中的文件
$ kubectl exec downward ls -lL /etc/downward
-rw-r--r--   1 root   root   134 May 25 10:23 annotations
-rw-r--r--   1 root   root     2 May 25 10:23 containerCpuRequestMilliCores
-rw-r--r--   1 root   root     7 May 25 10:23 containerMemoryLimitBytes
-rw-r--r--   1 root   root     9 May 25 10:23 labels
-rw-r--r--   1 root   root     8 May 25 10:23 podName
-rw-r--r--   1 root   root     7 May 25 10:23 podNamespace

  注意:与configMAp和secret卷一样,可以通过pod定义中downwardAPI卷的defaultMode属性来改变文件的访问权限设置。

  每个文件都对应了卷定义中的一项。文件的内容与之前例子中的元数据字段和值,这里不再重复展示。不过由于不能通过环境变量的方式暴露label和annotation,所以看一下暴露的这两个文件的代码清单。

#代码8.5 展示 downwardAPI卷中的标签和注解
$ kubectl exec downward cat /etc/downward/labels
foo="bar"
$ kubectl exec downward cat /etc/downward/annotations
key1="value1"
key2="multi\nline\nvalue\n"
kubernetes.io/config.seen="2016-11-28T14:27:45.664924282Z"
kubernetes.io/config.source="api"

  正如上面看到的,每一个标签和注解都以key=value的格式保存在单独的行中,如对应多个值,则写在同一行,并且用回车符\n连接。

  修改标签和注解

  可以在pod运行时修改标签和注解。当标签和注解被修改后, Kubernetes会更新存有相关信息的文件,从而使pod可以获取最新的数据。这也解释了为什么不能通过环境变量的方式暴露标签和注解,在环境变量方式下,一旦标签和注解被修改,新的值将无法暴露。

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

  需要说明一点,当暴露容器级的元数据时,如容器可使用的资源限制或者资源请求(使用字段resourceFieldRef),必须指定引用资源字段对应的容器名称,如下面的代码清单所示。

#代码8.6 在downwardAPI卷中引用容器级的元数据
spec:
  volumes:
  - name: downward
    downwardAPI:
      items:
      - path: "containerCpuRequestMilliCores"
        resourceFieldRef:
          containerName: main                     #必须制定容器名称  
          resource: requests.cpu
          divisor: 1m

  这样做的理由很明显,因为对于卷的定义是基于pod级的,而不是容器级的。 当我们引用卷定义某一个容器的资源字段时,我们需要明确说明引用的容器的名称。这个规则对于只包含单容器的pod同样适用。

  使用卷的方式来暴露容器的资源请求和使用限制比环境变量的方式稍显复杂, 但好处是如果有必要,可以传递一个容器的资源字段到另一个容器(当然两个容器必须处于同一个pod)。使用环境变量的方式,一个容器只能传递它自身资源申请求和限制的信息。

  何时使用Dowanward API方式

  Downward API方式并不复杂,它使得应用独立于Kubernetes。这一点在处理部分数据己在环境变量中的现有应用时特别有用。 Downward API方式使得我们不必通过修改应用,或者使用shell脚本获取数据再传递给环境变量的方式来暴露数据。

  不过通过Downward API的方式获取的元数据是相当有限的,如果需要获取更多的元数据,需要使用直接访问Kubernetes API服务器的方式。在与KubernetesAPI服务器交互章中讲解。

posted @ 2021-05-21 00:23  小家电维修  阅读(279)  评论(0编辑  收藏  举报