6-2 Kubernetes Pod 的生命周期和容器探针

更新时间:2023 年 3 月

Pod 生命周期

Pod 的调度流程

  1. 用户创建一个 Pod 对象,并将其提交到 Kubernetes API Server
  2. Kubernetes Scheduler 组件定期检查新提交的 Pod 对象,并为其选择一个节点
  3. 调度器使用一组调度策略来选择节点,例如负载均衡、资源利用率、亲和性和反亲和性等。它会选择一个满足 Pod 所需资源、节点亲和性和反亲和性等要求的节点
  4. 调度器为 Pod 创建一个绑定(Binding)对象,将 Pod 和节点进行绑定,并将该绑定保存到 Kubernetes API Server 中
  5. 节点代理(kubelet)定期检查 Kubernetes API Server 上的新 Pod 绑定对象,并获取它们的定义和镜像
  6. 节点代理会下载 Pod 所需的镜像,并创建容器
  7. 节点代理将 Pod 所需的所有容器启动起来,并将它们加入到 Pod 中
  8. 当所有容器都成功启动之后,Kubernetes API Server 中的 Pod 对象的状态被更新为 Running
  9. 如果 Pod 对象的调度策略发生变化,例如节点故障或调度器的策略变更,则调度器将重新选择一个节点并更新 Pod 的绑定对象。

调度流程图

在这里插入图片描述

Pod 阶段与状况

参考:Pod 的生命周期 | Kubernetes

参考:Pod异常问题排查 (aliyun.com)

Pod 遵循预定义的生命周期,起始于 Pending 阶段, 如果至少其中有一个主要容器正常启动,则进入 Running,之后取决于 Pod 中是否有容器以失败状态结束而进入 Succeeded 或者 Failed 阶段。详细的 Pod 的阶段参考:Pod 的生命周期 - Pod 阶段 | Kubernetes

在 Kubernetes API 中,Pod 包含规约部分和实际状态部分。 Pod 对象的状态包含了一组 Pod 状况(Conditions)。 如果应用需要的话,也可以向其中注入自定义的就绪态信息

Pod 在其生命周期中只会被调度一次。 一旦 Pod 被调度(分派)到某个节点,Pod 会一直在该节点运行,直到 Pod 停止或者被终止

Pod 阶段及描述

取值描述
Pending
(悬决)
Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行
(此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时)
Running
(运行中)
Pod 已经绑定到了某个节点,Pod 中所有的容器都已被创建。
至少有一个容器仍在运行,或者正处于启动或重启状态
Succeeded
(成功)
Pod 中的所有容器都已成功终止,并且不会再重启
Failed
(失败)
Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。
(容器以非 0 状态退出或者被系统终止)
Unknown(未知)因为某些原因无法取得 Pod 的状态。
这种情况通常是因为与 Pod 所在主机通信失败

Pod 状况

Pod 有一个 PodStatus 对象,其中包含一个 PodConditions 数组。Pod 可能通过也可能未通过其中的一些状况测试(若未通过测试,则会在 status 中显示未通过的项)。Kubelet 管理以下 PodCondition:

  • PodScheduled:Pod 已经被调度到某节点
  • PodHasNetwork:Pod 沙箱被成功创建并且配置了网络(Alpha 特性,必须被显式启用
  • ContainersReady:Pod 中所有容器都已就绪
  • Initialized:所有的 Init 容器都已成功完成
  • Ready:Pod 可以为请求提供服务,并且应该被添加到对应服务的负载均衡池中

Pod Status 总结

结合 Pod 阶段和 Pod 状况,常见的 Pod Status 如下表

取值描述
Pending正在创建 Pod;若 Pod 依赖的存储异常则会一直卡在该状态
RunningPod 中所有的容器都已被创建
至少有一个容器仍在运行,或者正处于启动或重启状态
SucceededPod 中的所有容器都已成功终止,并且不会再重启
FailedPod 中的所有容器都已终止,并且至少有一个容器是因为失败终止
Unknown因为某些原因无法取得 Pod 的状态
UnschedulablePod 不能被调度,即kube-scheduler 没有匹配到合适的 node 节点
PodScheduledPod 正在调度中,即 kube-scheduler 刚开始调度
NetworkPluginNotReady网络插件异常
InvalidImageName无法解析镜像名称导致镜像无法下载
RegistryUnavailableRegistry 不可用,无法与 Registry 正常建立连接
ErrImageNeverPull策略禁止拉去镜像,可能镜像中心权限不足导致
ErrImagePull镜像拉取出错,超时或下载被强制终止
ImagePullBackOff下载镜像失败
ImageInspectError无法校验镜像,可能镜像不完整导致
PodInitializingPod 正在初始化中
InitializedPod 中的初始化容器已完成
CreateContainerConfigError不能创建 kubelet 使用的容器配置
CreateContainerError创建容器失败
RunContainerErrorPod 运行失败,容器中没有初始化 PID 为 1 的守护进程
ContainerNotInitializedPod 没有初始化完成
ContainerNotReadyPod 没有准备完毕(未达到就绪探针要求)
ContainerCreatingPod 正在创建中
DockerDaemonNotReadyNode 节点的容器运行时没有启动
TerminatingPod 正在被销毁
ErrorPod 启动过程发生错误
NodeLostPod 所在节点失联
WaitingPod 等待启动
CrashLoopBackOffPod 启动失败,正在重启

Pod 中特殊的容器

pause 容器

Pause 容器,又叫 Infra 容器,是 Pod 中的基础容器,体积非常小,主要用于向 Pod 中所有容器共享相同的 Net Namespace(网络1命名空间)

生命周期

创建 Pod 时, Kubelet 会调用 CRI 接口 RuntimeService.RunPodSandbox 来创建一个沙箱环境,为 Pod 设置网络(例如:分配 IP)基础运行环境。当 Pod 沙箱(Pod Sandbox)建立起来后,Kubelet 就可以在里面创建其余的用户容器。当到删除 Pod 时,Kubelet 会停止里面的所有容器,再移除 Pod Sandbox

在目前的 CRI 规范中,Pod Sandbox 其实就是 pause 容器。Kubelet 代码引用的 defaultSandboxImage 其实就是官方提供的 pause 镜像

init 容器

参考:Init 容器 | Kubernetes

init 容器(Init Container),它是 Pod 中第一个启动的容器,而其他容器将在 init 容器成功执行后启动。init 容器常用于在主应用程序容器运行之前完成某些预备工作,例如初始化数据库、创建存储卷或下载配置文件等

init 容器与普通容器一样,都有自己的镜像、命令和环境变量。但与普通容器不同的是,init 容器只有在成功完成其任务后才会退出。如果 init 容器失败,则 Kubernetes 将重新启动 Pod,直到 init 容器成功完成其任务

init 容器的作用

  • 为业务容器提前准备好运行环境,例如

    • 将业务容器需要的配置文件提前生成并放在指定位置(挂载的卷中)

    • 检查数据权限或完整性,检查软件版本等

  • 为业务容器提前准备好业务数据,比如从网络中下载,或者从其他位置 Copy 到挂载的卷中

  • 检查依赖的服务是否能够正常访问

init 容器的特点

参考:Init 容器 - 与普通容器的不同之处 | Kubernetes

  • 一个 Pod 可以有多个 init 容器和多个业务容器,每个容器的运行环境相互之间隔离(网络不隔离,但无意义)
  • init 容器比业务容器先启动,多个 init 容器会依次启动
  • 当所有 init 容器运行成功之后,才会开始运行业务容器
  • init 容器不支持探针检查(运行完成后即退出)

init 容器示例

示例一

检查等待某些服务启动

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app.kubernetes.io/name: MyApp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

示例二

为业务容器提前准备好业务数据

apiVersion: v1
kind: Deployment
metadata:
  name: init-test-deploy
  labels:
    app.kubernetes.io/name: init-test-deploy
kind: Deployment 
spec:
  replicas: 1 
  selector:
    matchLabels:
      app: init-test
  template:
    metadata:
      labels:
        app: init-test
    spec:
      containers:
        - name: nginx
          image: nginx:1.22.1 
          #imagePullPolicy: Always
          volumeMounts:
          - mountPath: "/usr/share/nginx/html/myserver"
            name: data
      initContainers:
        - name: init-web-data
          image: centos:7.9.2009
          command: ['/bin/bash','-c',"for i in `seq 1 10`;do echo '<h1>'$i web page at $(date +%Y%m%d%H%M%S) '<h1>' >> /data/nginx/html/myserver/index.html;sleep 1;done"]
          volumeMounts:
          - mountPath: "/data/nginx/html/myserver"
            name: data
        - name: change-data-owner
          image: busybox:1.28
          command: ['/bin/sh','-c',"/bin/chmod 644 /data/nginx/html/myserver/* -R"]
          volumeMounts:
          - mountPath: "/data/nginx/html/myserver"
            name: myserver-data
      volumes:
      - name: data
        hostPath:
          path: /tmp/data/html

---    

容器状态与重启策略

容器状态

Kubernetes 会跟踪 Pod 中每个容器的状态。 并且可以使用容器生命周期回调来在容器生命周期中的特定时间点触发事件

一旦 kube-scheduler 将 Pod 分配给某个节点,kubelet 就通过 Container Runtime 开始为 Pod 创建容器。容器的状态有三种:Waiting(等待)、Running(运行中)和 Terminated(已终止)。要检查 Pod 中容器的状态,你可以使用 kubectl describe pod <pod 名称>。 其输出中包含 Pod 中每个容器的状态

Waiting (等待)

处于 Waiting 状态的容器表示在准备启动所需要的操作:例如, 从某个容器镜像仓库拉取容器镜像,或者向容器应用 Secret 数据等等。 当你使用 kubectl 来查询包含 Waiting 状态的容器的 Pod 时,你也会看到一个 Reason 字段,其中给出了容器处于等待状态的原因

Running(运行中)

Running 状态表明容器正在执行状态并且没有问题发生。 如果配置了 postStart 回调,那么该回调已经执行且已完成。 如果你使用 kubectl 来查询包含 Running 状态的容器的 Pod 时, 你也会看到关于容器进入 Running 状态的信息。

Terminated(已终止)

处于 Terminated 状态,表示容器已经执行并正常结束,或者因为某些原因失败。 使用 kubectl 来查询包含 Terminated 状态的容器的 Pod 时, 可以查看容器进入此状态的原因、退出代码以及容器执行期间的起止时间

如果容器配置了 preStop 回调,则该回调会在容器进入 Terminated 状态之前执行

重启策略

Pod 的 spec 中包含一个 restartPolicy 字段,其代表了容器在异常时的重启策略,其可能取值包括以下三个

  • Always(默认)

    当容器结束时(正常或异常),自动重启该容器

  • OnFailure

    当容器失败时,(容器停止运行且瑞出码不为 0),自动重启该容器

  • Never

    不重启容器

注:restartPolicy 适用于 Pod 中的所有容器

当 Pod 中的容器退出时,kubelet 会按指数回退方式计算重启的延迟(10s、20s、40s、…),其最长延迟为 5 分钟。 一旦某容器执行了 10 分钟并且没有出现问题,kubelet 对该容器的重启回退计时器执行重置操作。

容器探针(probe)

容器探针(probe) 是由 kubelet 对容器执行的定期诊断。kubelet 执行诊断的方式很多,既可以在容器内执行代码,也可以发出一个网络请求

检查机制

使用探针来检查容器有四种不同的方法。 每个探针必须定义为这四种机制中的一种

  • exec

    在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功

  • grpc(1.26 版本尚为 Alpha 特性)

    使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC 健康检查。 如果响应的状态是 SERVING,则认为诊断成功

  • httpGet

    对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求。如果响应的状态码大于等于 200 且小于 400,则认为诊断成功

  • tcpSocket

    对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则认为诊断成功。 即使远程系统(容器)在打开连接后立即将其关闭,这也算作是健康的

探测结果

每次探测都将获得以下三种结果之一:

  • Success(成功)

    容器通过了诊断

  • Failure(失败)

    容器未通过诊断

  • Unknown(未知)

    诊断失败,因此不会采取任何行动

探针类型

针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:

  • startupProbe(启动探针)

    判断容器中的应用是否已经启动。如果配置了启动探针,则所有其他探针都会被禁用,直到探针成功为止。如果启动探测失败,kubelet 将杀死容器, 而容器将根据 重启策略 进行重启。 如果容器没有提供启动探测,则默认状态为 Success

  • livenessProbe(存活探针)

    判断容器是否正在运行。如果存活探针探测失败,则 kubelet 会杀死容器, 并且容器将根据其 重启策略 决定未来。如果容器不提供存活探针, 则默认状态为 Success

  • readinessProbe(就绪探针)

    判断容器是否准备好为请求提供服务。如果就绪探针探测失败, 端点控制器(endpoints controller)将从与 Pod 匹配的所有 Service 的 Endpoints 列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success

注:

探针的配置参数

参考:配置存活、就绪和启动探针 - 配置探针 | Kubernetes

通用配置参数

探针(Probe)有很多配置字段,可以使用这些字段精确地控制启动、存活和就绪检测的行为

  • initialDelaySeconds

    容器启动后要等待多少秒后才启动启动、存活和就绪探针, 默认是 0 秒,最小值是 0。

  • periodSeconds

    执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。

  • timeoutSeconds

    探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。

  • successThreshold

    探针在失败后,被视为成功的最小连续成功数。默认值是 1。 存活探针和启动探针的这个值必须是 1。最小值是 1。

  • failureThreshold

    探针连续失败了 failureThreshold 次之后, Kubernetes 认为总体上检查已失败:容器状态未就绪、不健康、不活跃。

    对于启动探针或存活探针而言,如果至少有 failureThreshold 个探针已失败, Kubernetes 会将容器视为不健康并为这个特定的容器触发重启操作。 kubelet 会考虑该容器的 terminationGracePeriodSeconds 设置

    对于失败的就绪探针,kubelet 继续运行检查失败的容器,并继续运行更多探针; 因为检查失败,kubelet 将 Pod 的 Ready 状况设置为 false

  • terminationGracePeriodSeconds

    为 kubelet 配置从为失败的容器触发终止操作到强制容器运行时停止该容器之前等待的宽限时长。 默认值是继承 Pod 级别的 terminationGracePeriodSeconds 值(如果不设置则为 30 秒),最小值为 1。 更多细节请参见探针级别 terminationGracePeriodSeconds

HTTP 探针特有的配置参数

HTTP 探针支持 httpGet 配置额外的字段:

  • host:连接使用的主机名,默认是 Pod 的 IP。也可以在 HTTP 头中设置 Host 来代替
  • scheme :用于设置连接主机的方式(HTTP 还是 HTTPS)。默认是 HTTP
  • path:访问 HTTP 服务的路径。默认值为 /
  • httpHeaders:设置请求中自定义的 HTTP 头。HTTP 头字段允许重复
  • port:访问容器的端口号或者端口名。端口号必须在 1~65535 之间。

注意事项

  • 如果 scheme 字段设置为了 HTTPS,kubelet 会跳过证书验证发送 HTTPS 请求
  • 大多数情况下,不需要设置 host 字段。 这里有个需要设置 host 字段的场景,假设容器监听 127.0.0.1,并且 Pod 的 hostNetwork 字段设置为了 true。那么 httpGet 中的 host 字段应该设置为 127.0.0.1。 可能更常见的情况是如果 Pod 依赖虚拟主机,你不应该设置 host 字段,而是应该在 httpHeaders 中设置 Host
  • 针对 HTTP 探针,kubelet 除了必需的 Host 头部之外还发送两个请求头部字段: User-Agent (默认值分别是 kube-probe/{{ skew currentVersion >}} ,例如 kubernets 1.26 版本中,该值为 kubelet 的版本号)和 Accept(默认值是 */*)。可以通过为探针设置 .httpHeaders 来重载默认的头部字段值

示例

HTTP 探针-就绪探针示例

创建应用

创建一个 Nginx,使用 HTTP 探针探测 /index.html

$ vim http-probe.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-nginx
spec:
  replicas: 1
  selector:
    matchLabels: 
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.22.1
        ports:
        - containerPort: 80
        readinessProbe:
          # HTTP 探针
          httpGet:
            path: /index.html
            port: 80
          # 容器启动 5 秒后开始探测
          initialDelaySeconds: 5
          # 每隔 5 秒探测一次
          periodSeconds: 5
          # 超时时间
          timeoutSeconds: 5
          # 最小连续成功数
          successThreshold: 1
          # 最大连续失败数
          failureThreshold: 3

---
apiVersion: v1
kind: Service
metadata:
  name: svc-nginx
spec:
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
  selector:
    app: nginx

应用声明文件

$ kubectl apply -f http-probe.yaml

测试

查看 Service 的 Endpoint 列表

kubectl describe svc svc-nginx
Name:              svc-nginx
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.68.183.143
IPs:               10.68.183.143
Port:              http  80/TCP
TargetPort:        80/TCP
Endpoints:         172.20.195.48:80
Session Affinity:  None
Events:            <none>

删除 index.html

$ kubectl exec -it deploy-nginx-6fdc5776c8-rdchk bash  
root@deploy-nginx-6fdc5776c8-rdchk:/# mv /usr/share/nginx/html/index.html /usr/share/nginx/html/index.html.bak

查看 Pod 信息

$ kubectl get pod
NAME                                      READY   STATUS    RESTARTS      AGE
deploy-nginx-6fdc5776c8-rdchk             0/1     Running   0             27m


$ kubectl describe pod deploy-nginx-6fdc5776c8-rdchk
Events:
  Type     Reason     Age                From               Message
......
  Warning  Unhealthy  4s (x15 over 69s)  kubelet            Readiness probe failed: HTTP probe failed with statuscode: 404

查看 Service 的 Endpoint 列表

# Pod 被从 Service 的 Endpoint 列表中移除
$ kubectl describe svc svc-nginx
Name:              svc-nginx
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.68.183.143
IPs:               10.68.183.143
Port:              http  80/TCP
TargetPort:        80/TCP
Endpoints:         
Session Affinity:  None
Events:            <none>

恢复 index.html

$ kubectl exec -it deploy-nginx-6fdc5776c8-rdchk bash  
root@deploy-nginx-6fdc5776c8-rdchk:/# mv /usr/share/nginx/html/index.html.bak /usr/share/nginx/html/index.html

查看 Service 的 Endpoint 列表

# Pod 从新被添加到 Service 的 Endpoint 列表
$ kubectl describe svc svc-nginx
Name:              svc-nginx
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.68.183.143
IPs:               10.68.183.143
Port:              http  80/TCP
TargetPort:        80/TCP
Endpoints:         172.20.195.48:80
Session Affinity:  None
Events:            <none>

TCP 探针-存活探针示例

创建应用

$ vim tcp-probe.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-mysql
spec:
  replicas: 1
  selector:
    matchLabels: 
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0.32
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: 520qwe..
        ports:
        - containerPort: 3306
        livenessProbe:
          tcpSocket:
            port: 3306
          initialDelaySeconds: 60
          periodSeconds: 5
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3

---
apiVersion: v1
kind: Service
metadata:
  name: svc-mysql
spec:
  ports:
  - name: mysql
    port: 3306
    targetPort: 3306
    protocol: TCP
  selector:
    app: mysql

应用声明

$ kubectl apply -f tcp-probe.yaml

查看 Pod

$ kubectl get pods
NAME                                      READY   STATUS    RESTARTS   AGE
mysql-57d6db4686-6fk88                    1/1     Running   0          76s


$ kubectl describe pod mysql-57d6db4686-6fk88
......
    Liveness:       tcp-socket :3306 delay=60s timeout=5s period=5s #success=1 #failure=3
......
exec 探针示例
$ vim exec-probe.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-redis
spec:
  replicas: 1
  selector:
    matchLabels: 
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7
        ports:
        - containerPort: 6379
        livenessProbe:
        #readinessProbe:
          exec:
            command:
            - /usr/local/bin/redis-cli
            - quit
          initialDelaySeconds: 60
          periodSeconds: 5
          timeoutSeconds: 3
          successThreshold: 1
          failureThreshold: 3
      
---
apiVersion: v1
kind: Service
metadata:
  name: svc-redis
spec:
  ports:
  - name: redis-cli
    port: 6379
    targetPort: 6379
    protocol: TCP
  selector:
    app: redis

应用声明

$ kubectl apply -f exec-probe.yaml

查看 Pod

$ kubectl describe pod deploy-redis-859bf44554-h7rwh
......
    Liveness:       exec [/usr/local/bin/redis-cli quit] delay=60s timeout=3s period=5s #success=1 #failure=3
......
三种探针类型整合示例

注:此处使用到了一个结合了 SpringBoot 的 Java 程序 demo-0.0.1-SNAPSHOT.jar,该程序通过整合 spring-boot-starter-actuator,在路径 /actuator/health/readiness 实现了就绪探针接口,在路径 /actuator/health/liveness 实现了存活探针接口

构建 JRE 镜像

通过 JDK-17 生成 JRE,减少镜像大小。Dockerfile 如下

# 一阶段,生成 jre
FROM openjdk:17.0.2-slim-buster as builder

MAINTAINER nemo "sky.nemo@outlook.com"

RUN ["jlink", "--add-modules", "ALL-MODULE-PATH", "--output", "/usr/local/openjdk-17-jre" ]



# 二阶段
FROM debian:10-slim

MAINTAINER nemo "sky.nemo@outlook.com"

ENV TZ=Asia/Shanghai

#  JAVA_HOME
ENV JAVA_HOME=/usr/local/openjdk-17-jre

# 重写 PATH
ENV PATH=/usr/local/openjdk-17-jre/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

ENV LANG=C.UTF-8

ENV JAVA_VERSION=17.0.2

# copy 一阶段的 jre
COPY --from=builder /usr/local/openjdk-17-jre /usr/local/openjdk-17-jre

构建 JRE 镜像

$ docker build -t registry.cn-hangzhou.aliyuncs.com/kmust/openjdk:17.0.2-jre-slim-buster .

构建应用镜像

应用 Dockerfile 如下

#FROM openjdk:17.0.2-jre-slim-buster
FROM registry.cn-hangzhou.aliyuncs.com/kmust/openjdk:17.0.2-jre-slim-buster


MAINTAINER nemo "sky.nemo@outlook.com"

ENV TZ=Asia/Shanghai

WORKDIR /demo-java

ADD config ./
ADD demo-0.0.1-SNAPSHOT.jar ./

EXPOSE 8080/tcp 5000/tcp

ENTRYPOINT ["java","-jar","/demo-java/demo-0.0.1-SNAPSHOT.jar"]

构建镜像

$ docker build -t registry.cn-hangzhou.aliyuncs.com/kmust/demo-java:0.0.1  .

创建 Pod

$ vim probe.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-demo-app
spec:
  replicas: 1
  selector:
    matchLabels: 
      app.kubernetes.io/name: demo-app
  template:
    metadata:
      labels:
        app.kubernetes.io/name: demo-app
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: demo-app
        image: registry.cn-hangzhou.aliyuncs.com/kmust/demo-java:0.0.1
        ports:
        - name: app-port
          containerPort: 8080
        - name: healthy
          containerPort: 5000
        #
        startupProbe:
          httpGet:
            path: /app
            port: 8080
          # 首次检测延迟5s
          initialDelaySeconds: 5
          # 连续失败次数
          failureThreshold: 3
          # 探测间隔周期
          periodSeconds: 3 
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 5000
          initialDelaySeconds: 60
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 5000
          initialDelaySeconds: 60
          periodSeconds: 3
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
  name: svc-demo-app
spec:
  ports:
  - name: http
    port: 8080
    targetPort: 8080
    protocol: TCP
  selector:
    app.kubernetes.io/name: demo-app

应用声明

$ kubectl apply -f probe.yaml

查看探针情况

$ kubectl describe pod deploy-demo-app-767cfb49d8-d7lfq
......
    Liveness:       http-get http://:5000/actuator/health/liveness delay=5s timeout=5s period=3s #success=1 #failure=3
    Readiness:      http-get http://:5000/actuator/health/readiness delay=5s timeout=5s period=3s #success=1 #failure=3
    Startup:        http-get http://:8080/app delay=5s timeout=1s period=3s #success=1 #failure=3
......

容器生命周期回调

参考:容器生命周期回调 | Kubernetes

参考:为容器的生命周期事件设置处理函数 | Kubernetes

Kubernetes 支持在容器生命周期内通过事件(Event)来触发调用函数,目前支持两个事件

  • PostStart

    PostStart在容器创建之后立即执行调用。和容器入口点(ENTRYPOINT)异步触发。如果回调运行或挂起的时间太长,则容器无法达到 running 状态

  • PreStop

    PreStop在容器因 API 请求或者管理事件(例如存活探针失败、启动探针失败、资源抢占、资源竞争等) 而被终止之前调用。无论回调函数的执行结果如何,容器最终都会在 Pod 的终止宽限期内被终止

    PreStop 回调并不会与停止容器的信号处理程序异步执行,回调必须在可以发送信号之前完成执行。如果 PreStop 回调在执行期间停滞不前,Pod 的阶段会变成 Terminating 并一直处于该状态, 直到其 terminationGracePeriodSeconds 耗尽, Pod 被杀死

回调处理程序

容器通过实现和注册该回调的处理程序来访问该回调。 针对容器,有两种类型的回调处理程序可供实现:

  • exec:在容器的 cgroups 和 namespace 中执行特定的命令。 命令所消耗的资源计入容器的资源消耗
  • httpGet:对容器上的特定端点执行 HTTP 请求

注:tcpSocket 也支持,但目前无太大用处

示例

$ vim deploy-nginx-lifecycle
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-nginx-lifecycle
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: nginx
        image: nginx:1.22.1 
        lifecycle:
          postStart:
            exec:
              command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
          preStop:
            exec:
              command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]
        ports:
          - name: http
            containerPort: 8080

Pod 的终止流程

参考 Pod 的生命周期 - Pod 的终止 | Kubernetes

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值