• 深入剖析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 '
    
    '; 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 要做的修改。而如果它们要做的修改有冲突的话,这些冲突字段就不会被修改。

  • 相关阅读:
    软件测试 测试路径覆盖
    软件测试Lab Junit&Eclemma
    软件项目管理 名词解释
    使用 async/ await 进行 异步 编程
    基于任务的编程模型TAP
    异步编程(二)基于事件的异步编程模式 (EAP)
    C# 异步编程学习(一)
    C# 设计模式
    C# 读取硬盘信息 ManagementClass类
    C# show和showdialog区别
  • 原文地址:https://www.cnblogs.com/luoahong/p/12355529.html
Copyright © 2020-2023  润新知