在之前的章节中,我们已经了解到如何通过环境变量或者configMap和secret卷向应用传递配置数据。这对于pod调度、运行前预设的数据是可行的。但是对于那些不能预先知道的数据,比如pod的IP、主机名或者是pod自身的名称(当名称被生成,比如当pod通过ReplicaSet或类似的控制器生成时)呢?此外,对于那些已经在别处定义的数据,比如pod的标签和注解呢?我们不想在多个地方重复保留同样的数据。
对于此类问题,可以通过使用Kubernetes Downward API解决。Downward API允许我们通过环境变量或者文件(在downwardAPI卷中)的传递pod的元数据。不要对这个名称产生困惑,Downward API的方式并不像REST endpoint那样需要通过访问的方式获取数据。这种方式主要是将在pod的定义和状态中取得的数据作为环境变量和文件的值。
8.1.1 了解可用的元数据
Downward API可以给在pod中运行的进程暴露pod的元数据。目前我们可以给容器传递以下数据:
- pod的名称
- pod的IP
- pod所在的命名空间
- pod运行节点的名称
- pod运行所归属的服务账户的名称
- 每个容器请求的CPU和内存的使用量
- 每个容器可以使用的CPU和内存的限制
- pod的标签
- pod的注解
这个清单中所列举的大部分项目,除了还没有讲到的服务账户、CPU和内存的请求和限制概念,其他都无须进一步解释。我们将在第12章详细讲解服务账户。现在,你只需了解,服务账户是pod访问API服务器时用来进行身份验证的账户。CPU和内存的请求和限制将在第14章进行说明,它们代表了分配给一个容器的CPU和内存的使用量,以及一个容器可以分配的上限。
列表中的大部分项目既可以通过环境变量也可以通过downwardAPI卷传递给容器,但是标签和注解只可以通过卷暴露。部分数据可以通过其他方式获取(例如,可以直接从操作系统获取),但是Downward API提供了一种更加便捷的方式。
让我们来看一个向容器化的进程传递元数据的例子。
8.1.2 通过环境变量暴露元数据
首先,我们来了解如何通过环境变量的方式将pod和容器的元数据传递到容器中。我们根据如下列出的manifest创建一个简单的单容器。
代码清单8.1 在环境变量中使用downward API: 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: 10Mi
limits:
cpu: 100m
memory: 40Mi
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
valueFrom:
resourceFieldRef:
resource: requests.cpu
divisor: 1m
- name: CONTAINER_MEMORY_LIMIT_KIBIBYTES
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Ki
当我们的进程在运行时,它可以获取所有我们在pod的定义文件中设定的环境变量。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),设定基数为1 Ki(1 Kibibyte),则环境变量 CONTAINER_MEMORY_LIMIT_KIBIBYTES
的值就是4096。
对于CPU资源请求量和使用限制可以被设定为1,也就意味着整颗CPU的计算能力,也可以设定为1m,即千分之一核的计算能力。对于内存的资源请求和使用限制可以设定为1(字节),也可以是1k(kilobute)或1Ki(kibibute),同样也可以设为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
SERVICE_ACCOUNT=default
CONTAINER_CPU_REQUEST_MILLICORES=15
CONTAINER_MEMORY_LIMIT_KIBIBYTES=51200
POD_NAME=downward
POD_NAMESPACE=default
POD_IP=172.18.0.6
NODE_NAME=minikube
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
HOME=/root
所有在这个容器中运行的进程都可以读取并使用它们需要的变量。
8.1.3 通过downwardAPI卷来传递元数据
如果更倾向于使用文件的方式而不是环境变量的方式暴露元数据,可以定义一个downwardAPI卷并挂载到容器中。由于不能通过环境变量暴露,所以必须使用downwardAPI卷来暴露pod标签或注解。我们将摘后将讨论原因。
与环境变量一样,需要显示地指定元器据字段来暴露份进程。下面我们将把前面的示例从使用环境变量修改为使用存储卷,如下面的代码清单所示。
代码清单8.3 一个带有dowanwardAPI卷的pod示例:dowanward-api-volume.yaml
apiVersion: v1
kind: Pod
metadata: #通过downloadApi卷来暴露这些标签和注解
name: downward
labels:
foo: bar
annotations:
key1: value1
key2: |
multi
line
value
spec:
containers:
- name: main
image: busybox
command: ["sleep", "9999999"]
resources:
requests:
cpu: 15m
memory: 10Mi
limits:
cpu: 100m
memory: 40Mi
volumeMounts:
- name: downward
mountPath: /etc/downward #挂载的位置
volumes:
- name: downward #定义downwardAPI卷
downwardAPI:
items:
- path: "podName" #metadata.name字段将被写入到podName文件中
fieldRef:
fieldPath: metadata.name
- path: "podNamespace"
fieldRef:
fieldPath: metadata.namespace
- path: "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 -1L /etc/downward
注意 与configMAp和secret卷一样,可以通过pod定义中downwardAPI卷的defaultMode属性来改变文件的访问权限设置。
每个文件都对应了卷定义中的一项。文件的内容与之前例子中的元数据字段和值,这里不再重复展示。不过由于不能通过环境变量的方式暴露label和annotation,所以看一下我们暴露的这两个文件的代码清单。
代码清单8.5 展示downwardAPI卷中的标签和注解
$ kubectl exec downward -- cat /etc/downward/labels
$ kubectl exec downward -- cat /etc/downward/annotations
正如我们上面看到的,每一个标签和注解都以 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服务器的方式。我们将在接下来的部分了解这种方式。
网友评论