Kubernetes in Action笔记 - (11) 从应用访问pod元数据及其他资源
通过Downward API传递元数据
对于pod调度、运行前预设的数据,可以通过环境变量或者configMap和secret卷向应用传递配置数据。但是对于那些不能预先知道的数据, 比如pod的IP、 主机名或者是通过ReplicaSet等控制生成的pod名称,该如何获取呢?这种类型的数据,可以通过使用Kubernetes Downward API解决。
Downward API可以给在pod中运行的进程暴露pod的元数据。目前可以给容器传递以下数据:
- pod的名称
- pod的IP
- pod所在的命名空间
- pod运行节点的名称
- pod运行所归属的服务账户的名称
- 每个容器请求的CPU和内存的使用量
- 每个容器可以使用的CPU和内存的限制
- pod的标签
- pod的注解
通过环境变量暴露元数据
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:
# 容器请求的CPU和内存使用量是使用resourceFieldRef字段而不是feildRef字段
resourceFieldRef:
resource: requests.cpu
divisor: 1m # 对于资源相关字段,定义一个基数单位,从而生成每一部分的值
- name: CONTAINER MEMORY LIMIT KIBIBYTES
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Ki
对于暴露资源请 和使用限制的环境变量, 我们会设定一个基数单位。实际的资源请求值和限制值除以这个基数单位, 所得的结果通过环境变量暴露出去。在上面的例子中, 我们设定 CPU 请求的基数为1m (即1 millicore, 也就是千分之一核CPU)。当我们设置资源请求为15m时, 环境变量CONTAINER_CPU_REQUEST_MILLICORES的值就是15
在完成创建pod后, 我们可以使用kubectl exec命令来查看容器中的所有环境变量
kubectl exec downward env
通过卷传递元数据
可以定义一个downwardAPI卷,并以文件的方式挂载在容器中。
apiVersion: v1
kind: Pod
metadata:
# 后面会添加定义,通过downwardAPI卷来暴露这些标签
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: l00Ki
limits:
cpu: 100m
memory: 4Mi
volumeMounts:
# 挂载卷
- name: downward
mountPath: /etc/downward
volumes:
# 定义一个名字为downward的downwardAPI卷
- name: downward
downwardAPI:
items:
# Pod名称(来自manifest文件中的metadata.name字段)将被写入podName这个文件
- path: "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
访问权限设置
与configMap和secret卷一样,可以通过pod定义中downwardAPI卷的defaultMode属性来改变文件的访问权限设置。
修改标签与注解
可以在pod运行时修改标签和注解。当标签和注解被修改后, k8s会更新存有相关信息的文件,从而使pod可以获取最新的数据。这也解释了为什么不能通过环境变量的方式暴露标签和注解,在环境变量方式下,一旦标签和注解被修改,新的值将无法暴露。
在卷的定义中引用容器级的元数据
当暴露容器级的元数据时,如容器可使用的资源限制或者资源请求(使用字段resourceFieldRef), 必须指定引用资源字段对应的容器名称。这是因为对于卷的定义是基于 pod 级的,而不是容器级的。当引用卷定义某一个容器的资源字段时, 需要明确说明引用的容器的名称。这个规则对于只包含单容器的 pod 同样适用。
resourceFieldRef:
containerName: main #必须指定容器名称
resource: limits.memory
divisor: 1
使用卷的方式来暴露容器的资源请求和使用限制比环境变量的方式稍显复杂,但好处是如果有必要, 可以传递一个容器的资源字段到另一个容器(当然两个容器必须处于同一个 pod)。使用环境变量的方式, 一个容器只能传递它自身资源申请求和限制的信息。
Downward API优缺点
DownwardAPI 方式并不复杂, 它使得应用独立于k8s。这一点在处理部分数据已在环境变量中的现有应用时特别有用。它使得我们不必通过修改应用, 或者使用 shell 脚本获取数据再传递给环境变量的方式来暴露数据。
不过这种方式获取的元数据是相当有限的, 如果需要获取更多的元数据, 需要使用直接访问 k8s API 服务器的方式
与Kubernetes API服务器交互
通过 Kubectl proxy 访问 API 服务器
kubectl proxy命令启动了一 个代理服务来接收来自你本机的 HTTP 连接并转发至 API 服务器, 同时处理身份认证, 所以不需要每次请求都上传认证凭证。
运行代理很简单, 执行下面的命令
$ kubectl proxy
Starting to serve on 127.0.0.1:8001
上面的返回结果可以看到已经在本地8001代理端口接受请求。执行下面的命令,服务器会返回一个路径清单,这些路径对应了创建Pod、Service这些资源时定义的API组和版本信息
curl http://localhost: 8001
通过这些路径可以获取更多的信息,比如下面这个命令可以获取到集群中所有job实例
curl http://localhost:8001/apis/batch/vl/jobs
从pod内部与API服务器进行交互
每个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服务器。
(1) 获取API服务器地址
可以使用下面的命令来获取API服务器地址
$ kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.0.0.1 <none> 443/TCP 46d
可以在容器内通过查询KUBERNETES_SERVICE_HOST 和 KUBERNETES_SERVICE_PORT 这两个环境变量
$ env | grep KUBERNETES SERVICE
KUBERNETES_SERVICE _PORT=443
KUBERNETES_SERVICE_HOST=l0.0.0.1
KUBERNETES_SERVICE_PORT_HTTPS=443
由于每个服务都获得一个DNS入口,也可以简单地将 curl 指向 https://kubernetes,DNS会自动解析到k8s API服务器
(2)验证身份
指定证书
curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt https://kubernetes
或者,先定义成环境变量来简化后续的调用
export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
curl https://kubernetes
需要获得 API 服务器的授权, 以便可以读取并进一步修改或删除部署在集群中的 API 对象。
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -H "Authorization: Bearer $TOKEN" https://kubernetes
指定访问命名空间
NS=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/vl/namespaces/$NS/pods
通过 ambassador 容器简化与 API 服务器的交互
使用HTTPS、 证书和授权凭证, 对千开发者来说看上去有点复杂。 在pod中 向代理而不是直接向API服务器发送请求, 通过代理来处理授权、 加密和服务器验证。
如果一个应用需要查询API服务器,可以在主容器运行的同时, 启动一个ambassador容器,并在其中运行kubecctl proxy命令, 通过它来实现与API服务器的交互。在这种模式下, 运行在主容器中的应用不是直接与API服务器进行交互, 而是通过HTTP协议(不是HTTPS协议)与ambassador连接, 并且由ambassador通过HTTPS协议来连接API服务器, 对应用透明地来处理安全问题。 这种方式同样使用了默认凭证Secret卷中的文件。
使用客户端库与API服务器交互
目前, 存在由 API Machinery special interest group(SIG) 支持的两个版本的Kuberbetes API 客户端库。
- Galang client
- Python
k8s社区中还有其他各种不同语言的客户端库
图书资料: