Kubernetes in Action笔记 - (11) 从应用访问pod元数据及其他资源

分享到:

文章目录

对于pod调度、运行前预设的数据,可以通过环境变量或者configMap和secret卷向应用传递配置数据。但是对于那些不能预先知道的数据, 比如pod的IP、 主机名或者是通过ReplicaSet等控制生成的pod名称,该如何获取呢?这种类型的数据,可以通过使用Kubernetes Downward API解决。

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

  • pod的名称
  • pod的IP
  • pod所在的命名空间
  • pod运行节点的名称
  • pod运行所归属的服务账户的名称
  • 每个容器请求的CPU和内存的使用量
  • 每个容器可以使用的CPU和内存的限制
  • pod的标签
  • pod的注解
 1env:
 2- name: POD_NAME
 3  # 引用pod manifest中的元数据名称字段,而不是设定一个具体的值
 4  valueFrom:
 5    fieldRef:
 6      fieldPath: metadata.name
 7- name: POD_NAMESPACE
 8  valueFrom:
 9    fieldRef:
10      fieldPath: metadata.namespace
11- name: POD_IP
12  valueFrom:
13    fieldRef:
14      fieldPath: status.podIP
15- name: NODE_NAME
16  valueFrom:
17    fieldRef:
18      fieldPath: spec.nodeName
19- name: SERVICE_ACCOUNT
20  valueFrom:
21    fieldRef:
22      fieldPath: spec.serviceAccountName
23- name: CONTAINER_CPU_REQUEST_MILLICORES
24  valueFrom:
25    # 容器请求的CPU和内存使用量是使用resourceFieldRef字段而不是feildRef字段
26    resourceFieldRef:
27      resource: requests.cpu
28      divisor: 1m   # 对于资源相关字段,定义一个基数单位,从而生成每一部分的值
29- name: CONTAINER MEMORY LIMIT KIBIBYTES
30  valueFrom:
31    resourceFieldRef:
32      resource: limits.memory
33      divisor: 1Ki
...
yaml

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

在完成创建pod后, 我们可以使用kubectl exec命令来查看容器中的所有环境变量

1kubectl exec downward env
bash

可以定义一个downwardAPI卷,并以文件的方式挂载在容器中。

 1apiVersion: v1
 2kind: Pod
 3metadata:
 4  # 后面会添加定义,通过downwardAPI卷来暴露这些标签
 5  name: downward
 6  labels:
 7    foo: bar
 8  annotations:
 9    key1: value1
10    key2: |
11      multi
12      line
13      value      
14spec:
15  containers:
16  - name: main
17    image: busybox
18    command: ["sleep", "9999999"]
19    resources:
20      requests:
21        cpu: 15m
22        memory: l00Ki
23      limits:
24        cpu: 100m
25        memory: 4Mi
26    volumeMounts:
27    # 挂载卷
28    - name: downward
29      mountPath: /etc/downward
30  volumes:
31  # 定义一个名字为downward的downwardAPI卷
32  - name: downward
33    downwardAPI:
34      items:
35      # Pod名称(来自manifest文件中的metadata.name字段)将被写入podName这个文件
36      - path: "podName"
37        fieldRef:
38          fieldPath: metadata.name
39      - path: "podNamespace"
40        fieldRef:
41          fieldPath: metadata.namespace
42      - path: "labels"
43        fieldRef:
44          fieldPath: metadata.labels
45      - path: "annotations"
46        fieldRef:
47          fieldPath: metadata.annotations
48      - path: "containerCpuRequestMilliCores"
49        resourceFieldRef:
50          containerName: main
51          resource: requests.cpu
52          divisor: 1m
53      - path: "containerMemoryLimitBytes"
54        resourceFieldRef:
55          containerName: main
56          resource: limits.memory
57          divisor: 1
...
yaml

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

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

当暴露容器级的元数据时,如容器可使用的资源限制或者资源请求(使用字段resourceFieldRef), 必须指定引用资源字段对应的容器名称。这是因为对于卷的定义是基于 pod 级的,而不是容器级的。当引用卷定义某一个容器的资源字段时, 需要明确说明引用的容器的名称。这个规则对于只包含单容器的 pod 同样适用。

1resourceFieldRef:
2  containerName: main   #必须指定容器名称
3  resource: limits.memory
4  divisor: 1
yaml

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

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

不过这种方式获取的元数据是相当有限的, 如果需要获取更多的元数据, 需要使用直接访问 k8s API 服务器的方式

kubectl proxy命令启动了一 个代理服务来接收来自你本机的 HTTP 连接并转发至 API 服务器, 同时处理身份认证, 所以不需要每次请求都上传认证凭证。

运行代理很简单, 执行下面的命令

1$ kubectl proxy
2
3Starting to serve on 127.0.0.1:8001
bash

上面的返回结果可以看到已经在本地8001代理端口接受请求。执行下面的命令,服务器会返回一个路径清单,这些路径对应了创建Pod、Service这些资源时定义的API组和版本信息

1curl http://localhost: 8001
bash

通过这些路径可以获取更多的信息,比如下面这个命令可以获取到集群中所有job实例

1curl http://localhost:8001/apis/batch/vl/jobs

每个pod都会被自动挂载上一个默认的secret卷,映射到每个容器的 /var/run/secrets/kubernetes.io/serviceaccount目录下。 这个Secret包含三个条目:ca.crt、namespace与token, 包含了从pod内部安全访问KubernetesAPI服务器所需的全部信息。

在pod中运行的应用如何正确访问k8s的 API:

  • 应用应该验证 API 服务器的证书是否是证书机构所签发, 这个证书是在ca.crt文件中。
  • 应用应该将它在token文件中持有的凭证通过Authorization标头来获得 API 服务器的授权。
  • 当对pod所在命名空间的 API对象进行CRUD操作时, 应该使用namespace文件来传递命名空间信息到 API服务器。

可以使用下面的命令来获取API服务器地址

1$ kubectl get svc
2NAME        CLUSTER-IP  EXTERNAL-IP   PORT(S)   AGE
3kubernetes  10.0.0.1    <none>        443/TCP   46d
bash

可以在容器内通过查询KUBERNETES_SERVICE_HOST 和 KUBERNETES_SERVICE_PORT 这两个环境变量

1$ env | grep KUBERNETES SERVICE
2KUBERNETES_SERVICE _PORT=443
3KUBERNETES_SERVICE_HOST=l0.0.0.1
4KUBERNETES_SERVICE_PORT_HTTPS=443
bash

由于每个服务都获得一个DNS入口,也可以简单地将 curl 指向 https://kubernetes,DNS会自动解析到k8s API服务器

指定证书

1curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt https://kubernetes
2
3或者,先定义成环境变量来简化后续的调用
4export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
5curl https://kubernetes
bash

需要获得 API 服务器的授权, 以便可以读取并进一步修改或删除部署在集群中的 API 对象。

1TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
2curl -H "Authorization: Bearer $TOKEN" https://kubernetes
bash

指定访问命名空间

1NS=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
2curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/vl/namespaces/$NS/pods
bash

使用HTTPS、 证书和授权凭证, 对千开发者来说看上去有点复杂。 在pod中 向代理而不是直接向API服务器发送请求, 通过代理来处理授权、 加密和服务器验证。

如果一个应用需要查询API服务器,可以在主容器运行的同时, 启动一个ambassador容器,并在其中运行kubecctl proxy命令, 通过它来实现与API服务器的交互。在这种模式下, 运行在主容器中的应用不是直接与API服务器进行交互, 而是通过HTTP协议(不是HTTPS协议)与ambassador连接, 并且由ambassador通过HTTPS协议来连接API服务器, 对应用透明地来处理安全问题。 这种方式同样使用了默认凭证Secret卷中的文件。

目前, 存在由 API Machinery special interest group(SIG) 支持的两个版本的Kuberbetes API 客户端库。

  • Galang client
  • Python

k8s社区中还有其他各种不同语言的客户端库


图书资料: