深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

一、特殊的 Volume 

这种特殊的 Volume,叫作 Projected Volume,你可以把它翻译为“投射数据卷”

备注:Projected Volume 是 Kubernetes v1.11 之后的新特性

作用

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 含义

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

Projected Volume种类

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

一、Secret

1、作用

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

2、使用场景

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

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

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

接下来,我们尝试一下创建这个 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

 自动更新

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 注意事项

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 5、生产最佳实践

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

二、ConfigMap

1、与 Secret 的区别

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

2、使用方式

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

比如,一个 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、作用

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

举个例子:

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文件注解

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

$ 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、生产最佳实践

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 4、需要注意

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 5、建议

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

四、Service Account

1、作用

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 2、什么是Service Account

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

3、默认“服务账户

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 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、有趣的容器

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

2、livenessProbe(健康检查)

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

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 状态。这是为什么呢?  

 深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 需要注意的是

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 Pod 恢复机制

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

但一定要强调的是

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 如何出现在其他可用结点上

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

六、容器健康检查和恢复机制

 1、Pod 的恢复策略

1、分类

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

2、合理设计

1、非Always

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

2、Never

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

3、设计原理

1、第一条

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 2、第二条

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

$ 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、相同点

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

2、区别

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

七、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

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

 Spec

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

接下来,我们假定运维人员先创建了这个 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: {}

注意事项

深入剖析Kubernetes学习笔记:Pod基本概念进阶(15)

如果你定义了同时作用于一个 Pod 对象的多个 PodPreset,会发生什么呢?

实际上,Kubernetes 项目会帮你合并(Merge)这两个 PodPreset 要做的修改。而如果它们要做的修改有冲突的话,这些冲突字段就不会被修改。

上一篇:在k8s上部署日志系统elfk


下一篇:基于Kubernetes部署nacos配置中心