本文链接 https://www.jianshu.com/p/134092c8e105
configMap
ConfigMap 的用法几乎与 Secret 完全相同:你可以使用 kubectl create configmap 从文件或者目录创建 ConfigMap,也可以直接编写 ConfigMap 对象的 YAML 文件。
比如,一个 Java 应用所需的配置文件(.properties 文件),就可以通过下面这样的方式保存在 ConfigMap 里:
# .properties文件的内容
$ cat example/ui.properties
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice
# 从.properties文件创建ConfigMap
$ kubectl create configmap ui-config --from-file=example/ui.properties
# 查看这个ConfigMap里保存的信息(data)
$ kubectl get configmaps ui-config -o yaml
apiVersion: v1
data:
ui.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice
kind: ConfigMap
metadata:
name: ui-config
...
Downward API
它的作用是:让 Pod 里的容器能够直接获取到这个 Pod API 对象本身的信息。
举个例子
apiVersion: v1
kind: Pod
metadata:
name: test-downwardapi-volume
labels:
zone: us-est-coast
cluster: test-cluster1
rack: rack-22
spec:
containers:
- name: client-container
image: k8s.gcr.io/busybox
command: ["sh", "-c"]
args:
- while true; do
if [[ -e /etc/podinfo/labels ]]; then
echo -en '\n\n'; cat /etc/podinfo/labels; fi;
sleep 5;
done;
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
readOnly: false
volumes:
- name: podinfo
projected:
sources:
- downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
创建了这个 Pod 之后,就可以通过 kubectl logs 指令,查看到这些 Labels 字段被打印出来,如下所示:
$ kubectl create -f dapi-volume.yaml
$ kubectl logs test-downwardapi-volume
cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"
不过,需要注意的是,Downward API 能够获取到的信息,一定是 Pod 里的容器进程启动之前就能够确定下来的信息。而如果你想要获取 Pod 容器运行后才会出现的信息,比如,容器进程的 PID,那就肯定不能使用 Downward API 了,而应该考虑在 Pod 里定义一个 sidecar 容器。
Service Account
Service Account 对象的作用,就是 Kubernetes 系统内置的一种“服务账户”,它是 Kubernetes 进行权限分配的对象。比如,Service Account A,可以只被允许对 Kubernetes API 进行 GET 操作,而 Service Account B,则可以有 Kubernetes API 的所有操作的权限。
像这样的 Service Account 的授权信息和文件,实际上保存在它所绑定的一个特殊的 Secret 对象里的。这个特殊的 Secret 对象,就叫作 ServiceAccountToken。任何运行在 Kubernetes 集群上的应用,都必须使用这个 ServiceAccountToken 里保存的授权信息,也就是 Token,才可以合法地访问 API Server。
为了方便使用,Kubernetes 已经为你提供了一个的默认“服务账户”(default Service Account)。并且,任何一个运行在 Kubernetes 里的 Pod,都可以直接使用这个默认的 Service Account,而无需显示地声明挂载它。
查看一下任意一个运行在 Kubernetes 集群里的 Pod,就会发现,每一个 Pod,都已经自动声明一个类型是 Secret、名为 default-token-xxxx 的 Volume,然后 自动挂载在每个容器的一个固定目录上。比如:
$ kubectl describe pod nginx-deployment-5c678cfb6d-lg9lw
Containers:
...
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-s8rbq (ro)
Volumes:
default-token-s8rbq:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-s8rbq
Optional: false
一旦 Pod 创建完成,容器里的应用就可以直接从这个默认 ServiceAccountToken 的挂载目录里访问到授权信息和文件。
这个文件在容器内的路径是固定的,即:/var/run/secrets/kubernetes.io/serviceaccount ,而这个 Secret 类型的 Volume 里面的内容如下所示:
~ # ls -l /var/run/secrets/kubernetes.io/serviceaccount
total 0
lrwxrwxrwx 1 root root 13 Dec 9 10:03 ca.crt -> ..data/ca.crt
lrwxrwxrwx 1 root root 16 Dec 9 10:03 namespace -> ..data/namespace
lrwxrwxrwx 1 root root 12 Dec 9 10:03 token -> ..data/token
这种把 Kubernetes 客户端以容器的方式运行在集群里,然后使用 default Service Account 自动授权的方式,被称作“InClusterConfig”,也是最推荐的进行 Kubernetes API 编程的授权方式。
容器健康检查和恢复机制
在 Kubernetes 中,你可以为 Pod 里的容器定义一个健康检查“探针”(Probe)。这样,kubelet 就会根据这个 Probe 的返回值决定这个容器的状态,而不是直接以容器进行是否运行(来自 Docker 返回的信息)作为依据。这种机制,是生产环境中保证应用健康存活的重要手段。
我们一起来看一个 Kubernetes 文档中的例子。
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: test-liveness-exec
spec:
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
记住如下两个基本的设计原理即可:
- 只要 Pod 的 restartPolicy 指定的策略允许重启异常的容器(比如:Always),那么这个 Pod 就会保持 Running 状态,并进行容器重启。否则,Pod 就会进入 Failed 状态 。
- 对于包含多个容器的 Pod,只有它里面所有的容器都进入异常状态后,Pod 才会进入 Failed 状态。在此之前,Pod 都是 Running 状态。此时,Pod 的 READY 字段会显示正常容器的个数,比如:
$ kubectl get pod test-liveness-exec
NAME READY STATUS RESTARTS AGE
liveness-exec 0/1 Running 1 1m
现在,我们一起回到前面提到的 livenessProbe 上来。除了在容器中执行命令外,livenessProbe 也可以定义为发起 HTTP 或者 TCP 请求的方式,定义格式如下:
...
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
...
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
所以,你的 Pod 其实可以暴露一个健康检查 URL(比如 /healthz),或者直接让健康检查去检测应用的监听端口。这两种配置方法,在 Web 服务类的应用中非常常用。
预设
Pod 的字段这么多,我又不可能全记住,Kubernetes 能不能自动给 Pod 填充某些字段呢?
这个需求实际上非常实用。比如,开发人员只需要提交一个基本的、非常简单的 Pod YAML,Kubernetes 就可以自动给对应的 Pod 对象加上其他必要的信息,比如 labels,annotations,volumes 等等。而这些信息,可以是运维人员事先定义好的。这么一来,开发人员编写 Pod YAML 的门槛,就被大大降低了。所以,这个叫作 PodPreset(Pod 预设置)的功能 已经出现在了 v1.11 版本的 Kubernetes 中。举个例子,现在开发人员编写了如下一个 pod.yaml 文件:
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
spec:
containers:
- name: website
image: nginx
ports:
- containerPort: 80
如果运维人员看到了这个 Pod,他一定会连连摇头:这种 Pod 在生产环境里根本不能用啊!
运维人员就可以定义一个 PodPreset 对象。在这个对象中,凡是他想在开发人员编写的 Pod 里追加的字段,都可以预先定义好。比如这个 preset.yaml:
apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
name: allow-database
spec:
selector:
matchLabels:
role: frontend
env:
- name: DB_PORT
value: "6379"
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
在这个 PodPreset 的定义中,首先是一个 selector。这就意味着后面这些追加的定义,只会作用于 selector 所定义的、带有“role: frontend”标签的 Pod 对象,这就可以防止“误伤”
然后,我们定义了一组 Pod 的 Spec 里的标准字段,以及对应的值。比如,env 里定义了 DB_PORT 这个环境变量,volumeMounts 定义了容器 Volume 的挂载目录,volumes 定义了一个 emptyDir 的 Volume。
接下来,我们假定运维人员先创建了这个 PodPreset,然后开发人员才创建 Pod:
$ kubectl create -f preset.yaml
$ kubectl create -f pod.yaml
这时,Pod 运行起来之后,我们查看一下这个 Pod 的 API 对象
$ kubectl get pod website -o yaml
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
annotations:
podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
spec:
containers:
- name: website
image: nginx
volumeMounts:
- mountPath: /cache
name: cache-volume
ports:
- containerPort: 80
env:
- name: DB_PORT
value: "6379"
volumes:
- name: cache-volume
emptyDir: {}
这个时候,我们就可以清楚地看到,这个 Pod 里多了新添加的 labels、env、volumes 和 volumeMount 的定义,它们的配置跟 PodPreset 的内容一样。此外,这个 Pod 还被自动加上了一个 annotation 表示这个 Pod 对象被 PodPreset 改动过。
需要说明的是,PodPreset 里定义的内容,只会在 Pod API 对象被创建之前追加在这个对象本身上,而不会影响任何 Pod 的控制器的定义。
比如,我们现在提交的是一个 nginx-deployment,那么这个 Deployment 对象本身是永远不会被 PodPreset 改变的,被修改的只是这个 Deployment 创建出来的所有 Pod。这一点请务必区分清楚。这里有一个问题:如果你定义了同时作用于一个 Pod 对象的多个 PodPreset,会发生什么呢?实际上,Kubernetes 项目会帮你合并(Merge)这两个 PodPreset 要做的修改。而如果它们要做的修改有冲突的话,这些冲突字段就不会被修改。