• 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

  • 相关阅读:
    JS函数
    函数与指针
    函数返回指针类型(strchr函数)
    二维数组求平均值(指针的使用)
    二维数组做函数参数传递
    指针与函数
    Ubuntu安装mysql步骤
    带参程序
    函数返回值是指针
    mystrcat
  • 原文地址:https://www.cnblogs.com/wlbl/p/10694301.html
Copyright © 2020-2023  润新知