Kubernetes手记(6)- POD 配置清单

六 POD 配置清单

6.1 pods.metadata POD元数据

6.1.1 labels 标签

  • labels 定义标签,键值对组成的标签
  labels:
    app: myapp
    tier: frontend

6.2 pods.spec 规范

6.2.1 nodeName 运行节点

  • 在使用资源清单定义 pod 时候,使用 nodeName 可以直接绑定资源对象在哪个 POD 运行的节点
apiVersion: v1
kind: Pod
metadata:
  name: pod-deme
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  nodeName: node2                           # 直接指定 POD 运行的节点
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    imagePullPolicy: IfNotPresent

6.2.2 nodeSelector 节点选择

  • 在使用资源清单定义 pod 时候,使用 nodeSelector (节点标签选择器)字段,来定义节点的倾向性
apiVersion: v1
kind: Pod
metadata:
  name: pod-deme
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  nodeSelector:                            # 在 spec 中定义这个 POD 的节点倾向性
  	disktype: ssd                         # 这个 POD 最终会运行在拥有 disktype 标签且值为 ssd 的 nodes 上
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    imagePullPolicy: IfNotPresent
    ports:
  • 从文件启动 pod,观察 pod 运行的节点,会发现已经运行在有标签的 node 节点上了
kubectl create -f pod-demo.yaml
kubectl get pods -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-demo   1/1     Running   0          21s   10.244.2.29   node3   <none>           <none>

6.2.3 restartPolicy POD重启策略

Always:一旦容器挂了,那么总是重启它,k8s 每次重启策略为 30 秒的两倍,直到等待 300 秒重启。

OnFailure:只有其状态为错误的时候才去重启它

Never:从来不重启,挂了就挂了

一旦某个 POD 被调度到某个节点上,只要这个节点在,那么它就不会被重新调度,只能被重启,除非 POD 被删除才会被重新调度,或者 node 挂了,才会被重新调度,否则只要 node 在,那么 POD 就不会被重新调度,如果 POD 启动失败,那么将不断的重启 POD。
当需要终止 POD ,k8s 发送 kill -15 信号,让容器平滑的终止,等待 30 秒的宽限期,如果没有终止,那么则发送 kill 信号

6.2.4 hostNetwork 主机网络空间

使用布尔值指定是否让 POD 使用主机的网络名称空间

6.2.5 hostPID 主机PID空间

使用布尔值指定是否让 POD 使用主机的PID名称空间

6.2.6 containers 配置

kubectl explain pods.spec.containers

描述 POD 内所运行容器,语法:containers <[]Object>,表示它的值为数组,数组内使用对象的方式来描述一个容器,对象可以有以下参数:

  • 可用参数
参数 作用
args
command
env 向容器传递环境变量
envFrom
image
imagePullPolicy
lifecycle
livenessProbe
name
ports
readinessProbe
resources
securityContext
stdin
stdinOnce
terminationMessagePath
terminationMessagePolicy
tty
volumeDevices
volumeMounts
workingDir
  • 示例型配置
apiVersion: v1
kind: Pod
metadata:
  name: pod-deme                     # pod 的名称
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
    - name: myapp                      # 运行的容器名称
      image: ikubernetes/myapp:v1      # 容器的镜像
      imagePullPolicy: IfNotPresent    # 从仓库获取镜像的策略
      ports:                           # 定义容器暴漏的端口
    - name: busybox
      image: busybox:latest
      command:
        - "/bin/sh"
        - "-c"
        - "sleep 10"

6.2.6.1 imagePullPolicy下载策略

  • imagePullPolicy 镜像获取的策略,详见:kubectl explain pods.spec.containers
Always            # 总是从仓库下载
Never             # 从不下载,本地有就用,没有就失败
IfNotPresent      # 如果本地存在就直接使用,如果不存在就下载

如果标签是 latest 那么则始终从仓库下载

6.2.6.2 ports 端口信息

  • ports 定义容器保暴露的,详见:kubectl explain pods.spec.containers.ports

在此处暴露的端口可为系统提供有关容器的网络连接的信息,但主要是信息性的,此处没有指定的端口也不会阻止容器暴露该端口,容器中任何侦听 0.0.0.0 地址的端口都可以从网络访问

    ports:                    # 定义两个端口对象一个 http 一个 https
    - name: http              # 定义这个端口的名称,方便别的对象取引用
      containerPort: 80       # 端口号
    - name: https             # 方便引用的名称
      containerPort: 443      # 这个端口号仅仅是起到信息的作用,方便查看和使用名称引用

6.2.6.3 env 传递环境变量

  • Use Pod fields
      env:
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: MY_POD_SERVICE_ACCOUNT
          valueFrom:
            fieldRef:
              fieldPath: spec.serviceAccountName
              
              
  • Use Container fields
      env:
        - name: MY_CPU_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.cpu
        - name: MY_CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.cpu
        - name: MY_MEM_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.memory
        - name: MY_MEM_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.memory
在容器中获取 POD 的信息

可以使用环境变量
可以使用 downwardAPI
https://kubernetes.io/zh/docs/tasks/inject-data-application/downward-api-volume-expose-pod-information/

6.2.6.4 command ENTRYPOINT

  • command 定义容器运行的程序,详见:

https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/

一个 entrypoint array 而 command 启动的程序是不会运行在 Shell 中的,如果想要运行在 Shell 中需要自己填写,如果没有提供这个指令,那么将运行 docker 镜像中的 ENTRYPOINT。

Image Entrypoint Image Cmd Container command Container args Command run
[/ep-1] [foo bar] <not set> <not set> [ep-1 foo bar]
[/ep-1] [foo bar] [/ep-2] <not set> [ep-2]
[/ep-1] [foo bar] <not set> [zoo boo] [ep-1 zoo boo]
[/ep-1] [foo bar] [/ep-2] [zoo boo] [ep-2 zoo boo]

6.2.6.5 args CMD

  • args 向 command 传递参数的

如果你没有定义 args 而镜像中又存在 ENTRYPOINT 指令和 CMD 指令,那么镜像自己的 CMD 将作为参数传递给 ENTRYPOINT。如果手动指定了 args 那么镜像中的 CMD 字段不再作为参数进行传递。

如果在 args 中引用了变量,则需要使用 $(VAR_NAME) 来引用一个变量,如果不想在这里进行命令替换,那么可以 $$(VAR_NAME),转义后在容器内使用。

env:
- name: MESSAGE
  value: "hello kaliarch"
command: ["/bin/echo"]
args: ["$(MESSAGE)"]

6.2.6.6 annotations 注解信息

annotations 与 label 不同的地方在于,它不能用于挑选资源对象,仅为对象提供元数据,它的长度不受限制

apiVersion: v1
kind: Pod
metadata:
  name: pod-deme
  namespace: default
  labels:
    app: myapp
    tier: frontend
  annotations:                                      # 注解关键字
    kaliarch/created-by: "xuel"                     # 添加键值对的资源注解
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    imagePullPolicy: IfNotPresent

6.2.6.7 POD 生命周期

  • 一般状态
Pending:已经创建但是没有适合运行它的节点,已经调度,但是尚未完成
Running:运行状态
Failed: 启动失败
Succeed:成功,这个状态很短
Unkown: 未知的状态,如果 Apiserver 与 kubelet 通信失败则会处于这个状态
  • 创建 POD 阶段

用户的创建请求提交给 apiserver ,而 apiserver 会将请求的目标状态保存在 etcd 中,而后 apiserver 会请求 schedule 进行调度,并且把调度的结果更新在 etcd 的 pod 状态中,随后一旦保存在 etcd 中,并完成 schedule 更新后目标节点的 kubelet 就会从 etcd 的状态变化得知有新任务给自己,所以此时会拿到用户所希望的资源清单目标状态,根据清单在当前节点运行这个 POD,如果创建成功或者失败,则将结果发回给 apiserver ,apiserver 再次保存在 etcd 中。

6.2.6.8 livenessProbe 存活性探测

详细见:kubectl explain pods.spec.containers.livenessProbe

  • livenessProbe / readinessProbe 是 k8s 两个生命周期,这两个生命周期都可以定义探针来探测容器状态做出不同反应
livenessProbe     # 指示容器是否正在运行。如果存活探测失败,则依据 restartPolicy 策略来进行重启
readinessProbe    # 指示容器是否准备好服务请求。如果就绪探测失败端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址
  • livenessProbe / readinessProbe 可用的探针和探针特性,探针只能定义一种类型,例如:HTTPGetAction
exec          # 在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
tcpSocket     # 对指定端口上的容器的 IP 地址进行 TCP 检查。如果端口打开,则诊断被认为是成功的。
httpGet       # HTTP GET 请求指定端口和路径上的容器。如果响应码大于等于200 且小于 400,则诊断被认为是成功的。
failureThreshold    # 探测几次才判定为探测失败,默认为 3 次。
periodSeconds       # 每次探测周期的间隔时长。
timeoutSeconds      # 每次探测发出后等待结果的超时时间,默认为 1 秒。
initalDelaySeconds  # 在容器启动后延迟多久去进行探测,默认为启动容器后立即探测。
  • 使用 exec 探针,实验结果应该为 39 秒后 POD 显示 ERROR ,但不自动重启 POD
apiVersion: v1
kind: Pod
metadata:
  name: execlive
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
    - name: busybox
      image: busybox
      command:
        - "/bin/sh"
        - "-c"
        - "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 3600"    # 创建一个文件等待 30 秒,这个时间探针应该是成功的,30 秒后则失败
      livenessProbe:                                   # 容器的存活性检测,如果失败则按照 restartPolicy 策略来重启 POD
        exec:                                          # exec 类型探针,进入容器执行一条命令
          command: ["test", "-e" ,"/tmp/healthy"]      # 执行的命令为测试文件存在性
        initialDelaySeconds: 2                         # 容器启动后延迟多久进行探测
        periodSeconds: 3                               # 每次探测周期的间隔时长为 3 秒
        failureThreshold: 3                            # 3 次失败后则判定为容器探测存活性失败
  restartPolicy: Never                                 # 当探测到容器失败是否重启 POD
  • 使用 httpGet 探针,实验结果应该大约 40 秒后探测存活性失败,自动重启 POD,第一次重启会立即进行,随后是 30 秒的2倍直到 300 秒。
apiVersion: v1
kind: Pod
metadata:
  name: httpgetlive
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
    - name: nginx
      image: ikubernetes/myapp:v1
      ports:
        - name: http
          containerPort: 80
        - name: https
          containerPort: 443
      livenessProbe:                   # 容器的存活性检测,如果失败则按照 restartPolicy 策略来重启 POD
        httpGet:                       # httpget 探针
          path: /error.html            # 探测的页面,为了效果这个页面不存在
          port: http                   # 探测的端口,使用名称引用容器的端口
          httpHeaders:                 # httpget 时候设置请求头
            - name: X-Custom-Header
              value: Awesome
        initialDelaySeconds: 15        # 容器启动后延迟多久进行探测
        timeoutSeconds: 1              # 每次探测发出等待结果的时长
  restartPolicy: Always                # 当探测到容器失败是否重启 POD

6.2.6.9 readinessProbe 就绪性检测

例如有一个容器运行的是 tomcat ,而 tomcat 展开 war 包,部署完成的时间可能较长,而默认 k8s 会在容器启动就标记为 read 状态,接收 service 的调度请求,但是容器启动不代表 tomcat 已经成功运行,所以需要 readinessProbe 进行就绪性探测,来决定是否可以接入 service 上。

  • livenessProbe / readinessProbe 可用的探针和探针特性基本一样,探针只能定义一种类型,例如:HTTPGetAction
livenessProbe     # 指示容器是否正在运行。如果存活探测失败,则依据 restartPolicy 策略来进行重启
readinessProbe    # 指示容器是否准备好服务请求。如果就绪探测失败端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址
  • 使用 httpGet 探针,实验结果应该大约 40 秒后探测存活性失败,自动重启 POD,第一次重启会立即进行,随后是 30 秒的2倍直到 300 秒。
apiVersion: v1
kind: Pod
metadata:
  name: httpgetread
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
    - name: nginx
      image: ikubernetes/myapp:v1
      ports:
        - name: http
          containerPort: 80
        - name: https
          containerPort: 443
      livenessProbe:                   # 容器的存活性检测,如果失败则按照 restartPolicy 策略来重启 POD
        httpGet:                       # httpget 探针
          path: /error.html            # 探测的页面,为了效果这个页面不存在
          port: http                   # 探测的端口,使用名称引用容器的端口
          httpHeaders:                 # httpget 时候设置请求头
            - name: X-Custom-Header
              value: Awesome
        initialDelaySeconds: 15        # 容器启动后延迟多久进行探测
        timeoutSeconds: 1              # 每次探测发出等待结果的时长
  restartPolicy: Always                # 当探测到容器失败是否重启 POD
  • 手动进入容器,删除 index.html 以触发就绪性探针的检测
kubectl exec -it httpgetread -- /bin/sh
$ rm -f /usr/share/nginx/html/index.html
  • 结果这个 POD 的 READY 状态已经变成非就绪了,此时 service 不会再调度到这个节点了
[root@node1 ~]# kubectl get pods -w
NAME                            READY   STATUS    RESTARTS   AGE
httpgetread                     0/1     Running   0          2m50s
  • 在容器内再创建一个文件,以触发就绪性探针的检测
kubectl exec -it httpgetread -- /bin/sh
$ echo "hello worlld" >>/usr/share/nginx/html/index.html
  • 结果这个 POD 的的 READY 状态已经编程就绪了,此时 service 会调度到这个节点了
[root@node1 ~]# kubectl get pods -w
NAME                            READY   STATUS    RESTARTS   AGE
httpgetread                     1/1     Running   0          8m15s

6.2.6.10 lifecycle 生命周期钩子

详见:kubectl explain pods.spec.containers.lifecycle

postStart           # 在容器启动后立即执行的命令,如果这个操作失败了,那么容器会终止,且根据 restartPolicy 来决定是否重启
preStop             # 在容器终止前立即执行的命令
  • postStart / preStop 的基本使用
apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx

    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]

POD控制器

控制器管理的 POD 可以实现,自动维护 POD 副本数量,它能实现 POD 的扩容和缩容,但是不能实现滚动更新等高级功能。

名称 作用
ReplicationController 原来 k8s 只有这一种控制器,目前已经接近废弃
ReplicaSet 代用户创建指定数量的 POD 副本,还支持扩缩容,被称为新一代的 ReplicationController。主要由 3 个指标,1. 用户希望的 POD 副本,2. 标签选择器,判定 POD 是否归自己管理,3. 如果 POD 副本不够,按照哪个 POD template 创建 POD,但一般我们不直接使用 ReplicaSet。
Deployment Deployment 通过控制 ReplicaSet 来实现功能,除了支持 ReplicaSet 的扩缩容意外,还支持滚动更新和回滚等,还提供了声明式的配置,这个是我们日常使用最多的控制器。它是用来管理无状态的应用。
DaemonSet 用于确保集群内的每个 node 上只运行一个指定的 POD,如果有新增的节点也都会自动运行这个 POD,所以这个控制器无需定义 POD 运行的数量,只需要定义标签选择器和 POD template。所以可以跟根据标签选择器选中的 node 上只运行一个 POD 副本。
Job 执行一个一次性任务,例如数据库备份,任务完成后正常退出,则 POD 不会再被启动了,除非任务异常终止。
CronJob 执行一些周期性任务
StatefulSet 管理有状态的 POD ,但是对每个不同的有状态应用需要自行编写脚本,完成对有状态服务的管理,为了解决 StatefulSet 不方便编写有状态应用管理的问题。k8s 还提供了 helm 这样类似于 yum 的方式,方便用户从 helm 市场来安装一个有状态的应用。

其他

自己将手记发在:https://github.com/redhatxl/awesome-kubernetes-notes
欢迎一键三连

(完)