【k8s】五、Pod生命周期(一)

目录

前言

Pod生命周期

Pod 相位 状态值

挂起(Pending)

运行中(Running)

成功(Succeeded)

失败(Failed)

未知(Unknown)

Init Containers

Init Contianers的作用

Init Contianers实验

特殊说明

总结

写在后面


前言

前面我们部署了k8s容器环境,也知道了怎么书写数据自己的Pod,那本节我们就一起来学习Pod的生命周期。这个是重点来的哦,因为在实际运用的过程中,对于Pod生命周期每个阶段的进行检测,进而分析和排错。

Pod生命周期

上图主要描述了在容器环境初始化完成之后,pod从创建到退出,中间这段时间经历的过程;

从大的方向上看,pod生命周期分两个阶段:

  • 第一阶段是初始化容器。
  • 第二阶段是主容器的整个生命周期;

在 Pod 启动时,pause容器总是创建的第一个容器。每个pod中都存在一个pause容器。这个是k8s自动创建的。

对于Init Container来说,一个pod中可以定义多个初始化容器,他们必须是串行执行,只有当所有的初始化容器执行完后,对应的主容器才会启动;如上图所示,会按顺序把1、2、3的InitC都执行完成之后,才进入Main Container阶段

Main Container生命周期又分为三个阶段:

  • 第一阶段是运行post start hook,这个阶段是主容器运行之后立即需要做的事;
  • 第二阶段是主容器正常运行的阶段,在这个阶段中,我们可以定义对容器的健康状态(liveness)检查和就绪状态(readness)检查;
  • 第三阶段是运行pre stop hook,这个阶段主要做容器即将退出前需要做的事;

readness——就绪检测,检测成功后,Pod状态改为Ready

liveness——生存检测,

对于对容器的健康状态检查和就绪状态检查,我们也可以定义开始检查的延迟时长;因为有些容器存在容器显示running状态,但内部程序还没有初始化,如果立即做健康状态检查,可能存在健康状态为不健康,从而导致容器重启的状况;

Pod 相位 状态值

首先在介绍 Pod 的生命周期之前,我们先了解下 Pod 的状态,因为 Pod 状态可以反映出当前我们的 Pod 的具体状态信息,也是我们分析排错的一个必备的方式。

我们可以通过命令kubectl explain pod.status查看到

挂起(Pending)

Pod已被K8s系统接受,但有一个或者多个容器镜像尚未创建,等待时间包括调度Pod的时间和通过网络下载镜像的时间。这可能需要花点时间。

运行中(Running)

该Pod已经绑定到了一个节点上,Pod中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或者重启状态。

成功(Succeeded)

Pod中所有的容器都被成功终止并且不会再重启。

失败(Failed)

Pod中所有容器都已经终止,并且至少有一个容器是因为失败终止。也就是说,至少有一个容器以非0状态退出或者被系统终止。

未知(Unknown)

因为某些原因无法取得Pod的状态,通常是因为与Pod所在主机通信失败。

Init Containers

Init Containers是在Pod的主容器启动之前要运行的容器,主要是做一些主容器的前置工作。

一个Pod能够具有多个容器,应用运行在应用容器里面,但是该Pod中可能有一个或多个先于应用容器启动的Init容器。

Init容器与普通容器非常像,除了以下两点:

  • Init容器总是运行到成功完成为止,如果某个初始化容器运行失败,那么kubernetes需要重启它直至成功完成。
  • Init容器必须按照定义的顺序执行,每个Init容器都必须在下一个Init容器启动之前成功完成

注意:如果Pod的Init容器失败,Kubernetes会不断重启该Pod,知道Init容器成功位置。然而,如果Pod对应的重启策略restartPolicy为Never时,Pod不会重新启动。

Init Contianers的作用

因为Init容器具有与应用程序容器分离的单独镜像,所以它们的启动相关代码具有如下优势:

  • 他们可以包含并运行实用工具,但是出于安全考虑,时不建议在应用程序容器镜像中包含这些实用工具的。
  • 特们可以包含实用工具和定制化代码来安装,但是不能出现在应用程序镜像中。例如,创建镜像没必要FROM另一个镜像,只需要在安装过程中使用类似sed、awk、python、dig这样的工具
  • 应用程序镜像可以分离出创建和部署的角色,而没有必要联合他们构建一个单独的镜像。
  • Init容器使用Linux Namespace,所以相对应用程序容器来说,具有不同的文件系统视图。因此,他们能够具有访问Secret的权限,而应用程序容器则不能。实现分权限来治理。例如,有一个文件夹里面只有一个文件是main容器要用到的,但是又不希望main容器有这个文件夹的访问权限,于是通过init容器来访问该文件,并且把该文件复制到main容器中。
  • 他们必须在应用程序容器启动之前运行完成,而应用程序容器时并行运行的,所以,Init容器能够提供一种简单的阻塞或延迟应用容器启动的方法,直到满足了一系列应用容器启动的先决条件。例如我们有两个容器,一个放数据库,一个放应用服务,服务要在数据库启动之后启动,访问才不会报错,那么此时我们可以在应用服务容器的init containers中设置一个条件,用于检测数据库容器是否完成启动,如果数据库完成启动了,则应用服务容器可以正常启动。如下图所示:

Init Contianers实验

下面我们通过实际例子来进一步认识init containers。

# init-pod.yaml
# author: 攻城狮白玉
apiVersion: v1
kind: Pod
metadata:
  name: baiyu-pod
  namespace: baiyu-learn-k8s
  labels:
    app: baiyu-app
    version: v1
spec:
  containers:
    - name: baiyu-app-container
      image: busybox:1.32
      command: ['sh', '-c', 'echo The app is running && sleep 3600']
  initContainers:
    - name: init-baiyu-service
      image: busybox:1.32
      command: ['sh', '-c','until nslookup baiyu-service; do echo waiting for baiyu-service; sleep 2;done;']
    - name: init-baiyu-db
      image: busybox:1.32
      command: ['sh', '-c', 'until nslookup baiyu-db; do echo waiting for baiyu-db; sleep 2; done;']

上面的yaml,运行了一个pod。里面有一个main container,有两个init container

kubectl create -f init-pod.yaml

然后执行kubectl get pod -n baiyu-learn-k8s -o wide -w查看容器状态

 此时无论多久,上面这个容器都不会变成running状态的。这是因为我们写了两个init container的条件没有满足。

第一个init container里面,我们执行了下面这句命令,表示在我们能解析到有这个baiyu-service名称的网络服务就打破循环。

until nslookup baiyu-service; 
do 
echo waiting for baiyu-service; 
sleep 2;
done;

此处补充个小知识点,便是Linux中until的用法。

until跟while类似,都是一个固定循环。

但是until跟while又有不同,则是它们的条件满足不同。

until是直到条件为真时,打破循环。而while是条件为真时一直循环,条件为假时打破循环。

此时,我们通过命令

kubectl logs baiyu-pod -n baiyu-learn-k8s --container init-baiyu-service

查看pod中的init-baiyu-service容器的日志,可以发现它一直在循环检测服务是否起来

 为了满足我们init-pod中的init container的条件,我们准备了以下脚本实现两个service,用于满足init C的条件。

# init-service.yaml
# author: 攻城狮白玉
kind: Service
apiVersion: v1
metadata:
  name: baiyu-service
  namespace: baiyu-learn-k8s
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
  name: baiyu-db
  namespace: baiyu-learn-k8s
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377

执行命令启动脚本kubectl apply -f init-service.yaml

使用kubectl get pod -n baiyu-learn-k8s -o wide -w查看我们的pod的状态。结果如下图所示,在两个init容器执行完成之后,main 容器进行执行,最终Pod完成启动Pod状态变为Running

特殊说明

  • 在Pod启动过程中,Init容器会按顺序在网络和数据卷初始化(网络和数据卷初始化是在pause中启动的)之后启动。每个容器必须在下一个容器启动之前成功退出。
  • 如果由于运行时或失败退出,将导致容器启动失败,它会根据Pod的restartPolicy指定的策略进行重试。然而,如果Pod的restartPolicy设置为Always,Init容器失败时会使用RestartPolicy策略
  • 在所有的Init容器没有成功之前,Pod将不会变成Ready状态。Init容器的端口将不会再Service中进行聚集。正在初始化中的Pod处于Pending状态,但应该会将Initializing状态设置为true
  • 如果Pod重启,所有Init容器必须重新执行。
  • 对于Init容器spec的修改被限制在容器image字段,修改其他字段都不会生效。更改Init容器的image字段等价于重启该Pod
  • Init容器具有应用容器的所有字段,除了readinessProbe,因为Init容器无法定义不同于完成(completion)的就绪(readiness)之外的其他状态。这会在验证过程中强制执行。
  • 在Pod中的每个app和Init容器的名称必须要唯一;与任何其他容器共享同一个名称,会在验证时抛出错误。
  • Init容器执行的效果应该是幂等的。

总结

本文介绍了k8s整体容器的生命周期以及Init Container的基本概念和作用,并通过一个实战例子进一步加深了各位同学对init Containers的理解。限于篇幅,并没有把容器的生命周期所有知识点写完,下一篇我会介绍容器生命周期的两个探针,readness和liveness。以及启停时的动作钩子

写在后面

如果觉得有用的话,麻烦一键三连支持一下攻城狮白玉,并把本文分享给更多的小伙伴。你的简单支持,我的无限创作动力

猜你喜欢

转载自blog.csdn.net/zhh763984017/article/details/127286358