Kubernetes之(八)Pod的生命周期

Kubernetes之(八)Pod的生命周期

理解Pod

Pod是kubernetes中你可以创建和部署的最⼩也是最简的单位。 ⼀个Pod代表着集群中运⾏的⼀个进程。
Pod中封装着应⽤的容器(有的情况下是好⼏个容器) , 存储、 独⽴的⽹络IP, 管理容器如何运⾏的策略选项。 Pod代
表着部署的⼀个单位: kubernetes中应⽤的⼀个实例, 可能由⼀个或者多个容器组合在⼀起共享资源。
在Kubrenetes集群中Pod有如下两种使⽤⽅式:

  • ⼀个Pod中运⾏⼀个容器。 “每个Pod中⼀个容器”的模式是最常⻅的⽤法; 在这种使⽤⽅式中, 你可以把Pod想象成是单个容器的封装, kuberentes管理的是Pod⽽不是直接管理容器。
  • 在⼀个Pod中同时运⾏多个容器。 ⼀个Pod中也可以同时封装⼏个需要紧密耦合互相协作的容器, 它们之间共享资源。 这些在同⼀个Pod中的容器可以互相协作成为⼀个service单位——⼀个容器共享⽂件, 另⼀个“sidecar”容器来更新这些⽂件。 Pod将这些容器的存储资源作为⼀个实体来管理。

每个Pod都是应⽤的⼀个实例。 如果你想平⾏扩展应⽤的话(运⾏多个实例) , 你应该运⾏多个Pod, 每个Pod都是⼀个应⽤实例。 在Kubernetes中, 这通常被称为replication。

Pod内如何管理多个容器

Pod中可以同时运行多个进程(作为容器运行)协同工作。同一个Pod中的容器会自动的分配到同一个 node 上。同一个Pod中的容器共享资源、网络环境和依赖,它们总是被同时调度。
注意在一个Pod中同时运行多个容器是一种比较高级的用法。只有当你的容器需要紧密配合协作的时候才考虑用这种模式。例如,你有一个容器作为web服务器运行,需要用到共享的volume,有另一个“sidecar”容器来从远端获取资源更新这些文件,如下图所示:

(图片来源于网络)
Pod中可以共享两种资源:网络和存储。

  • 网络
    每个Pod都会被分配一个唯一的IP地址。Pod中的所有容器共享网络空间,包括IP地址和端口。Pod内部的容器可以使用localhost互相通信。Pod中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)。
  • 存储
    可以Pod指定多个共享的Volume。Pod中的所有容器都可以访问共享的volume。Volume也可以用来持久化Pod中的存储资源,以防容器重启后文件丢失。

Pod的使用

Pod也可以⽤于垂直应⽤栈(例如LAMP) , 这样使⽤的主要动机是为了⽀持共同调度和协调管理应⽤程序, 例如:

  • 内容管理系统、 ⽂件和数据加载器、 本地换群管理器等。
  • ⽇志和检查点备份、 压缩、 旋转、 快照等。
  • 数据变更观察者、 ⽇志和监控适配器、 活动发布者等。
  • 代理、 桥接和适配器等。
  • 控制器、 管理器、 配置器、 更新器等。
    通常单个pod中不会同时运⾏⼀个应⽤的多个实例。

其他替代选择

为什么不直接在⼀个容器中运⾏多个应⽤程序呢?
1、透明,让Pod中的容器对基础设施可⻅, 以便基础设施能够为这些容器提供服务, 例如进程管理和资源监控。 这可以为⽤户带来极⼤的便利。
2、解耦软件依赖。 每个容器都可以进⾏版本管理, 独⽴的编译和发布。 未来kubernetes甚⾄可能⽀持单个容器的在线升级。
3、使⽤⽅便。 ⽤户不必运⾏⾃⼰的进程管理器, 还要担⼼错误信号传播等。
4、效率。 因为由基础架构提供更多的职责, 所以容器可以变得更加轻量级。

Pod的持久性

Pod在设计⽀持就不是作为持久化实体的。 在调度失败、 节点故障、 缺少资源或者节点维护的状态下都会死掉会被驱逐。
通常, ⽤户不需要⼿动直接创建Pod, ⽽是应该使⽤controller(例如Deployments) , 即使是在创建单个Pod的情况下。 Controller可以提供集群级别的⾃愈功能、 复制和升级管理。
Pod原语有利于:

  • 调度程序和控制器可插拔性
  • 支持pod级操作,无需通过控尸气API代理他们
  • 将pod生命周期与控制器生命周期分离
  • 控制器与服务的分离,端点控制器只是监视pod
  • 将集群集功能与kubelet及共鞥的清晰组合
  • 高可用性应用程序,他们可以在终止前及在删除前更换pod

Pod的终止

因为Pod作为在集群的节点上运⾏的进程, 所以在不再需要的时候能够优雅的终⽌掉是⼗分必要的(⽐起使⽤发送KILL信号这种暴⼒的⽅式) 。 ⽤户需要能够放松删除请求, 并且知道它们何时会被终⽌, 是否被正确的删除。 ⽤户想终⽌程序时发送删除pod的请求, 在pod可以被强制删除前会有⼀个宽限期, 会发送⼀个TERM请求到每个容器的主进程。 ⼀旦超时, 将向主进程发送KILL信号并从API server中删除。 如果kubelet或者container manager在等待进程终⽌的过程中重启, 在重启后仍然会重试完整的宽限期。
示例流程如下:

  1. 用户发送删除pod的命令,默认宽限期30秒
  2. 、在Pod超过该宽限期后API server就会更新Pod的状态为dead
  3. 在客户端命令行上显示的Pod的状态为为terminating;
  4. 跟上一部同时,当kubelet发现pod被标记为terminating时,开始停止pod进程:
  • 如果在pod中定义了preStop hook,在停止pod前会被调用。如果宽限期后 preStop hook仍然在运行,第二部会增加2秒宽限期;
  • 向Pod中的进程发送TERM信号
  1. 跟第三部同时,该Pod将从该service的端点列表中删除,不再是replication controller中的一部分,关闭的慢的pod将继续处理load balancer转发的流量
  2. 过了宽限期后,将向Pod中易安存在的进程发SIGKILL信号杀掉进程。
  3. kubelet会在APIserver中完成Pod的删除,通过将优雅周期设置为0(立即删除)Pod在API中小时,并且客户端也不可见。

删除宽限期默认是30秒。 kubectl delete 命令⽀持 —grace-period= 选项, 允许⽤户设置⾃⼰的宽限期。 如果设置为0将强制删除pod。 在kubectl>=1.5版本的命令中, 你必须同时使⽤ --force 和 --grace-period=0 来强制删除pod。

Init容器

Pod能够具有多个容器,应用运行在容器里面,但是它也可能有一个或多个咸鱼应用容器启动的Init容器。
Init容器与普通容器很像,除了一下亮点:

  • Init容器总是u以女性到成功完成为止。
  • 每个Init容器都必要在下一个Init容器启动之前完成。

如果 Pod 的 Init 容器失败, Kubernetes 会不断地重启该 Pod, 直到 Init 容器成功为⽌。 然⽽, 如果 Pod 对应的restartPolicy 为 Never, 它不会重新启动。

Pause容器

Pause容器,又叫Infra容器。我们检查node节点的时候会发现每个node上都运行了很多的pause容器,例如如下。

[root@node02 ~]# docker ps |grep pause
db1f4da98626        registry.aliyuncs.com/google_containers/pause:3.1   "/pause"                 24 hours ago        Up 24 hours                             k8s_POD_myapp-9b4987d5-djdr9_default_995067e0-5124-11e9-80a7-000c295ec349_0
e344da31cee8        registry.aliyuncs.com/google_containers/pause:3.1   "/pause"                 24 hours ago        Up 24 hours                             k8s_POD_client-f5cdb799f-pklmc_default_25f4bf9b-5121-11e9-80a7-000c295ec349_0
de5e811fa5fa        registry.aliyuncs.com/google_containers/pause:3.1   "/pause"                 28 hours ago        Up 28 hours                             k8s_POD_nginx-deploy-84cbfc56b6-tcssz_default_10003a4a-5104-11e9-80a7-000c295ec349_0
3f7d179b79b9        registry.aliyuncs.com/google_containers/pause:3.1   "/pause"                 30 hours ago        Up 30 hours                             k8s_POD_kube-flannel-ds-amd64-gwbql_kube-system_0f2de1c5-506c-11e9-80a7-000c295ec349_1
cc07e6411d32        registry.aliyuncs.com/google_containers/pause:3.1   "/pause"                 30 hours ago        Up 30 hours                             k8s_POD_kube-proxy-cz2rf_kube-system_1f58e488-5068-11e9-80a7-000c295ec349_1

kubernetes中的pause容器主要为每个业务容器提供以下功能:

  • 在pod中担任Linux命名空间共享的基础;
  • 启用pid命名空间,开启init进程。
    pause容器的作⽤可以从这个例⼦中看出, ⾸先⻅下
    图:

    (图片来源于网络)
    我们⾸先在节点上运⾏⼀个pause容器。
[root@node02 ~]# docker run --name pause -d -p 8880:80 xiaobai20201/pause:3.1
Unable to find image 'xiaobai20201/pause:3.1' locally
3.1: Pulling from xiaobai20201/pause
Digest: sha256:59eec8837a4d942cc19a52b8c09ea75121acc38114a2c68b98983ce9356b8610
Status: Downloaded newer image for xiaobai20201/pause:3.1
d4078dc99bec81167c8d288c2b44485d407780913eee88458986d5bb3750c509

然后再运⾏⼀个nginx容器, nginx将为 localhost:2368 创建⼀个代理。

[root@node02 ~]# vim nginx.conf
error_log stderr;
events { worker_connections 1024; }
http {
        access_log /dev/stdout combined;
        server {
                listen 80 default_server;
                server_name example.com www.example.com;
                location / {
                        proxy_pass http://127.0.0.1:2368;
                }
        }
}
[root@node02 ~]#  docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause nginx

然后为ghost创建一个容器应用,这是一款博客软件。

[root@node02 ~]# docker run -d --name ghost --net=container:pause --ipc=container:pause --pid=container:pause ghost

现在访问http://10.0.0.12:8880/ 就可以看到ghost博客的界面了

解析
pause容器将内部的80端⼝映射到宿主机的8880端⼝, pause容器在宿主机上设置好了⽹络namespace后, nginx容器加⼊到该⽹络namespace中, 我们看到nginx容器启动的时候指定了 --net=container:pause , ghost容器同样加⼊到了该⽹络namespace中, 这样三个容器就共享了⽹络, 互相之间就可以使⽤ localhost 直接通信, --ipc=contianer:pause --pid=container:pause 就是三个容器处于同⼀个namespace中, init进程为 pause , 这时我们进⼊到ghost容器中查看进程情况。

Pod的生命周期

Pod的phase

Pod的status信息保存在PodStatus中定义,有一个phase字段
Pod的相位(phase)是Pod在其生命周期中的简单宏观概述,该阶段并不是对容器或Pod的总和汇总,也不是为了作为综合状态机。
Pod香味的数量和含义是严格指定的。除了文档中列举的状态外,不应该再假定Pod有其他phase值。
下面是phase可能的值:

  • 挂起 Pending:Pod已经被Kubernetes系统接受,但是有一个或多个容器镜像尚未创建,等待时间包括调度Pod的时间和通过网络下载镜像的时间。
  • 运行中 Running:该Pod已经绑定到一个节点上,Pod中所有的容器都已经被创建,至少有一个容器正在运行或者处于启动或者重启状态。
  • 成功 Succeeded:Pod中的所有容器都被成功终止,并且不会在重启。
  • 失败 Failed:Pod中的所有容器已经终止,并且至少有一个容器是因为失败终止,也就是说,容器以非0状态退出或者被系统终止。
  • 位置 Unknown:因为某些议无法获取Pod的状态,哦那个厂是因为与Pod所在的节点通信失败。

Pod的状态

Pod 有一个 PodStatus 对象,其中包含一个 PodCondition 数组。 PodCondition 数组的每个元素都有一个 type 字段和一个 status 字段。type 字段是字符串,可能的值有 PodScheduled、Ready、Initialized 和 Unschedulable。status 字段是一个字符串,可能的值有 True、False 和 Unknown。

容器探针

在pod生命周期中可以做的一些事情。主容器启动前可以完成初始化容器,初始化容器可以有多个,他们是串行执行的,执行完成后就推出了,在主程序刚刚启动的时候可以指定一个post start 主程序启动开始后执行一些操作,在主程序结束前可以指定一个 pre stop 表示主程序结束前执行的一些操作。在程序启动后可以做两类检测 liveness probe(存活性探测) 和 readness probe(就绪性探测)

探针是由 kubelet 对容器执⾏的定期诊断。 要执⾏诊断, kubelet 调⽤由容器实现的 Handler。 有三种类型的处理程序:

  • ExecAction: 在容器内执⾏指定命令。 如果命令退出时返回码为 0 则认为诊断成功。
  • TCPSocketAction: 对指定端⼝上的容器的 IP 地址进⾏ TCP 检查。 如果端⼝打开, 则诊断被认为是成功的
  • HTTPGetAction: 对指定的端⼝和路径上的容器的 IP 地址执⾏ HTTP Get 请求。 如果响应的状态码⼤于等于200且⼩于 400, 则诊断被认为是成功的。

Kubelet 可以选择是否执行在容器上运行的两种探针执行和做出反应:

  • livenessProbe:指示容器是否正在运行。如果存活探测失败,则 kubelet 会杀死容器,并且容器将受到其 重启策略 的影响。如果容器不提供存活探针,则默认状态为 Success。
  • readinessProbe:指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址。初始延迟之前的就绪状态默认为 Failure。如果容器不提供就绪探针,则默认状态为 Success。

存活性探测 livenessProbe

解析


[root@master ~]# kubectl explain pods.spec.containers.livenessProbe.
KIND:     Pod
VERSION:  v1
   exec <Object> #命令式探针
   failureThreshold     <integer> #探测失败次数,超过该次数才算失败,默认是3
   periodSeconds        <integer> #探测间隔时间,默认10s
   successThreshold     <integer> #探测成功的最少次数,默认是1,即探测成功1次就算是成功 
   tcpSocket    <Object> #检测端口的探测
   initialDelaySeconds  #初始化延迟探测,第一次探测的时候,因为主程序未必启动完成
   timeoutSeconds       <integer> # 探测超时的秒数 默认1s
   httpGet <Object> #http请求探测

exec探针举例

[root@master manifests]# vim liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec-pod
  namespace: default
spec:
  containers:
  - name: liveness-exec-container
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","touch /tmp/healthy;sleep 30;rm -f /tmp/healthy;sleep 600"]
    livenessProbe:
      exec:
        command: ["test","-e","/tmp/healthy"]
      initialDelaySecond: 1
      periodSecond: 3
#运行并查看pod
[root@master manifests]# kubectl apply -f liveness-exec.yaml 
pod/liveness-exec-pod created
[root@master manifests]# kubectl get pods -w
NAME                            READY   STATUS    RESTARTS   AGE
client-f5cdb799f-pklmc          1/1     Running   0          25h
liveness-exec-pod               1/1     Running   0          5s
myapp-9b4987d5-47sjj            1/1     Running   0          25h
myapp-9b4987d5-684q9            1/1     Running   0          25h
myapp-9b4987d5-djdr9            1/1     Running   0          25h
nginx-deploy-84cbfc56b6-tcssz   1/1     Running   0          29h
pod-demo                        2/2     Running   4          4h33m
liveness-exec-pod   1/1   Running   1     87s
#重启次数变成1

上面的资源清单中定义了一个Pod对象,基于busybox镜像启动一个运行“touch /tmp/healthy;sleep 30;rm -f /tmp/healthy;sleep 600”命令的容器,此命令在容器启动时创建/tmp/healthy文件,并于60秒之后将其删除。存活性探针运行“test -e /tmp/healthy”命令检查/tmp/healthy文件的存在性,若文件存在则返回状态码0,表示成功通过测试。

httpGet探测举例

[root@master manifests]# vim liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-httpget-pod
  namespace: default
spec:
  containers:
  - name: liveness-httpget-container
    image: ikubernetes/myapp:v1
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    livenessProbe:
      httpGet:
        port: http
        path: /index.html

#创建Pod并查看
[root@master manifests]# kubectl create -f liveness-httpget.yaml 
pod/liveness-httpget-pod created
[root@master manifests]# kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
client-f5cdb799f-pklmc          1/1     Running   0          26h
liveness-exec-pod               1/1     Running   8          20m
liveness-httpget-pod            1/1     Running   0          8s
myapp-9b4987d5-47sjj            1/1     Running   0          25h
myapp-9b4987d5-684q9            1/1     Running   0          25h
myapp-9b4987d5-djdr9            1/1     Running   1          25h
nginx-deploy-84cbfc56b6-tcssz   1/1     Running   0          29h
pod-demo                        2/2     Running   4          4h53m

#手动进入容器删除index.html文件,再监控pod状态
[root@master manifests]# kubectl exec -it  liveness-httpget-pod -- /bin/sh
/ # rm -f /usr/share/nginx/html/index.html 
#查看pod
^C[root@master manifests]# kubectl get pods -w
NAME                            READY   STATUS             RESTARTS   AGE
client-f5cdb799f-pklmc          1/1     Running            0          26h
liveness-exec-pod               0/1     CrashLoopBackOff   9          24m
liveness-httpget-pod            1/1     Running            1          3m41s
myapp-9b4987d5-47sjj            1/1     Running            0          25h
myapp-9b4987d5-684q9            1/1     Running            0          25h
myapp-9b4987d5-djdr9            1/1     Running            1          25h
nginx-deploy-84cbfc56b6-tcssz   1/1     Running            0          29h
pod-demo                        2/2     Running            4          4h57m
#发现liveness-httpget-pod已经重启了一次

TCP探测举例

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness-tcp
  name: liveness-tcp
spec:
  containers:
  - name: liveness-tcp-demo
    image: nginx:1.12-alpine
    ports:
    - name: http
      containerPort: 80
    livenessProbe:
      tcpSocket:
        port: http

就绪性探测 readnessProbe

[root@master manifests]# vim readness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
  name: readiness-httpget-pod
  namespace: default
spec:
  containers:
  - name: readiness-httpget-container
    image: ikubernetes/myapp:v1
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    readinessProbe:
      httpGet:
        port: http
        path: /index.html
      initialDelaySecond: 1
      periodSeconds: 3

此时pod运行正常,进入容器删除首页文件后观察pod状态

[root@master manifests]# kubectl exec -it readiness-httpget-pod -- /bin/sh
/ # rm -f /usr/share/nginx/html/index.html 

[root@master manifests]# kubectl get pods -w
NAME                            READY   STATUS    RESTARTS   AGE
client-f5cdb799f-pklmc          1/1     Running   0          26h
myapp-9b4987d5-47sjj            1/1     Running   0          26h
myapp-9b4987d5-684q9            1/1     Running   0          26h
myapp-9b4987d5-djdr9            1/1     Running   1          26h
nginx-deploy-84cbfc56b6-tcssz   1/1     Running   0          30h
pod-demo                        2/2     Running   5          5h27m
readiness-httpget-pod           0/1     Running   0          115s

重写写入index.html文件后继续观察pod

/ # echo ad >/usr/share/nginx/html/index.html 
^C[root@master manifests]# kubectl get pods -w
NAME                            READY   STATUS    RESTARTS   AGE
client-f5cdb799f-pklmc          1/1     Running   0          26h
myapp-9b4987d5-47sjj            1/1     Running   0          26h
myapp-9b4987d5-684q9            1/1     Running   0          26h
myapp-9b4987d5-djdr9            1/1     Running   1          26h
nginx-deploy-84cbfc56b6-tcssz   1/1     Running   0          30h
pod-demo                        2/2     Running   5          5h29m
readiness-httpget-pod           1/1     Running   0          3m54s

livenessProbe和readinessProbe使用场景

如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活探针; kubelet 将根据 Pod 的restartPolicy 自动执行正确的操作。

如果希望容器在探测失败时被杀死并重新启动,那么请指定一个存活探针,并指定restartPolicy 为 Always 或 OnFailure。

如果要仅在探测成功时才开始向 Pod 发送流量,请指定就绪探针。在这种情况下,就绪探针可能与存活探针相同,但是 spec 中的就绪探针的存在意味着 Pod 将在没有接收到任何流量的情况下启动,并且只有在探针探测成功后才开始接收流量。

如果您希望容器能够自行维护,您可以指定一个就绪探针,该探针检查与存活探针不同的端点。

请注意,如果您只想在 Pod 被删除时能够排除请求,则不一定需要使用就绪探针;在删除 Pod 时,Pod 会自动将自身置于未完成状态,无论就绪探针是否存在。当等待 Pod 中的容器停止时,Pod 仍处于未完成状态。

lifecycle

定义容器启动后和终止前立即执行的动作
解析

[root@master ~]# kubectl explain pods.spec.containers.lifecycle.
postStart    <Object> #启动后
- exec
- httpGet
- tcpSocket

preStop      <Object> #终止前
- exec
- httpGet
- tcpSocket

postStart举例

[root@master manifests]# vim poststart-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: poststart-pod
  namespace: default
spec:
  containers:
  - name: busybox-httpd
    image: busybox
    imagePullPolicy: IfNotPresent
    lifecycle:
      postStart:
        exec:
          command: ["mkdir","-p","/tmp/share"]
    command: ["/bin/sh","-c","sleep 3600"]
#进入容器查看是否有/tmp/share目录
[root@master manifests]# kubectl exec -it poststart-pod  -- /bin/sh
/ # ls -l /tmp
total 0
drwxr-xr-x    2 root     root             6 Mar 29 09:19 share
参考资料

https://www.cnblogs.com/linuxk
马永亮. Kubernetes进阶实战 (云计算与虚拟化技术丛书)
Kubernetes-handbook-jimmysong-20181218

posted @ 2019-04-12 10:06  微落不落  阅读(1747)  评论(0编辑  收藏  举报