深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)
一、特殊的 Volume
这种特殊的 Volume,叫作 Projected Volume,你可以把它翻译为“投射数据卷”
备注:Projected Volume 是 Kubernetes v1.11 之后的新特性
作用
含义
Projected Volume种类
一、Secret
1、作用
2、使用场景
Secret 最典型的使用场景,莫过于存放数据库的 Credential 信息,比如下面这个例子:
3、方式一:kubectl create secret
apiVersion: v1 kind: Pod metadata: name: test-projected-volume spec: containers: - name: test-secret-volume image: busybox args: - sleep - "86400" volumeMounts: - name: mysql-cred mountPath: "/projected-volume" readOnly: true volumes: - name: mysql-cred projected: sources: - secret: name: user - secret: name: pass
这里用到的数据库的用户名、密码,正是以 Secret 对象的方式交给 Kubernetes 保存的。完成这个操作的指令,如下所示:
$ cat ./username.txt admin $ cat ./password.txt c1oudc0w! $ kubectl create secret generic user --from-file=./username.txt $ kubectl create secret generic pass --from-file=./password.txt
其中,username.txt 和 password.txt 文件里,存放的就是用户名和密码;而 user 和 pass,则是我为 Secret 对象指定的名字。而我想要查看这些 Secret 对象的话,只要执行一条 kubectl get 命令就可以了:
$ kubectl get secrets NAME TYPE DATA AGE user Opaque 1 51s pass Opaque 1 51s
4、方式二:编写YAML
当然,除了使用 kubectl create secret 指令外,我也可以直接通过编写 YAML 文件的方式来创建这个 Secret 对象,比如:
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: user: YWRtaW4= pass: MWYyZDFlMmU2N2Rm
接下来,我们尝试一下创建这个 Pod:
$ kubectl create -f test-projected-volume.yaml
验证这些Secret对象是不是已经在容器里了
$ kubectl exec -it test-projected-volume -- /bin/sh $ ls /projected-volume/ user pass $ cat /projected-volume/user root $ cat /projected-volume/pass 1f2d1e2e67df
自动更新
注意事项
5、生产最佳实践
二、ConfigMap
1、与 Secret 的区别
2、使用方式
比如,一个 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 ...
备注:kubectl get -o yaml 这样的参数,会将指定的 Pod API 对象以 YAML 的方式展示出来。
三、Downward API
1、作用
举个例子:
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
2、Yaml文件注解
$ kubectl create -f dapi-volume.yaml $ kubectl logs test-downwardapi-volume cluster="test-cluster1" rack="rack-22" zone="us-est-coast"
目前,Downward API 支持的字段已经非常丰富了,比如:
1. 使用 fieldRef 可以声明使用: spec.nodeName - 宿主机名字 status.hostIP - 宿主机 IP metadata.name - Pod 的名字 metadata.namespace - Pod 的 Namespace status.podIP - Pod 的 IP spec.serviceAccountName - Pod 的 Service Account 的名字 metadata.uid - Pod 的 UID metadata.labels['<KEY>'] - 指定 <KEY> 的 Label 值 metadata.annotations['<KEY>'] - 指定 <KEY> 的 Annotation 值 metadata.labels - Pod 的所有 Label metadata.annotations - Pod 的所有 Annotation 2. 使用 resourceFieldRef 可以声明使用: 容器的 CPU limit 容器的 CPU request 容器的 memory limit 容器的 memory request
3、生产最佳实践
4、需要注意
5、建议
四、Service Account
1、作用
2、什么是Service Account
3、默认“服务账户
4、这是如何做到的呢?
当然还是靠 Projected Volume 机制。
如果你查看一下任意一个运行在 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
这个 Secret 类型的 Volume,正是默认 Service Account 对应的 ServiceAccountToken。所以说,Kubernetes 其实在每个 Pod 创建的时候,自动在它的 spec.volumes 部分添加上了默认 ServiceAccountToken 的定义,然后自动给每个容器加上了对应的 volumeMounts 字段。这个过程对于用户来说是完全透明的。
这样,一旦 Pod 创建完成,容器里的应用就可以直接从这个默认 ServiceAccountToken 的挂载目录里访问到授权信息和文件。这个容器内的路径在 Kubernetes 里是固定的,即:/var/run/secrets/kubernetes.io/serviceaccount ,而这个 Secret 类型的 Volume 里面的内容如下所示:
$ ls /var/run/secrets/kubernetes.io/serviceaccount ca.crt namespace token
所以,你的应用程序只要直接加载这些授权文件,就可以访问并操作 Kubernetes API 了。而且,如果你使用的是 Kubernetes 官方的 Client 包(k8s.io/client-go
)的话,
它还可以自动加载这个目录下的文件,你不需要做任何配置或者编码操作。InClusterConfig
当然,考虑到自动挂载默认 ServiceAccountToken 的潜在风险,Kubernetes 允许你设置默认不为 Pod 里的容器自动挂载这个 Volume。
除了这个默认的 Service Account 外,我们很多时候还需要创建一些我们自己定义的 Service Account,来对应不同的权限设置。
这样,我们的 Pod 里的容器就可以通过挂载这些 Service Account 对应的 ServiceAccountToken,来使用这些自定义的授权信息。在后面讲解为 Kubernetes 开发插件的时候,我们将会实践到这个操作。
五、容器健康检查
在 Kubernetes 中,你可以为 Pod 里的容器定义一个健康检查“探针”(Probe)。这样,kubelet 就会根据这个 Probe 的返回值决定这个容器的状态,而不是直接以容器进行是否运行(来自 Docker 返回的信息)作为依据。这种机制,是生产环境中保证应用健康存活的重要手段。
1、容器健康检查
我们一起来看一个 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
1、有趣的容器
2、livenessProbe(健康检查)
2、现在,让我们来具体实践一下这个过程。
1、首先,创建这个 Pod:
$ kubectl create -f test-liveness-exec.yaml
2、然后,查看这个 Pod 的状态:
$ kubectl get pod NAME READY STATUS RESTARTS AGE test-liveness-exec 1/1 Running 0 10s
可以看到,由于已经通过了健康检查,这个 Pod 就进入了 Running 状态。
3、而 30 s 之后,我们再查看一下 Pod 的 Events:
$ kubectl describe pod test-liveness-exec
4、你会发现,这个 Pod 在 Events 报告了一个异常:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 2s 2s 1 {kubelet worker0} spec.containers{liveness} Warning Unhealthy Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
显然,这个健康检查探查到 /tmp/healthy 已经不存在了,所以它报告容器是不健康的。那么接下来会发生什么呢?
3、restartPolicy
我们不妨再次查看一下这个 Pod 的状态:
$ kubectl get pod test-liveness-exec NAME READY STATUS RESTARTS AGE liveness-exec 1/1 Running 1 1m
这时我们发现,Pod 并没有进入 Failed 状态,而是保持了 Running 状态。这是为什么呢?
需要注意的是
Pod 恢复机制
但一定要强调的是
如何出现在其他可用结点上
六、容器健康检查和恢复机制
1、Pod 的恢复策略
1、分类
2、合理设计
1、非Always
2、Never
3、设计原理
1、第一条
2、第二条
$ 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 服务类的应用中非常常用。
readinessProbe和livenessProbe的区别
1、相同点
2、区别
七、Kubernetes 能不能自动给 Pod 填充某些字段呢?
可是,如果运维人员看到了这个 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: {}
selector
Spec
接下来,我们假定运维人员先创建了这个 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 对象的多个 PodPreset,会发生什么呢?
实际上,Kubernetes 项目会帮你合并(Merge)这两个 PodPreset 要做的修改。而如果它们要做的修改有冲突的话,这些冲突字段就不会被修改。