K8S 快速入门(十一)实战篇:资源清单运作原理解析&Pod的生命周期&探针

一、资源控制器

官方文档 工作负载资源

1. 什么是控制器?

Kubernetes中内建了很多controller (控制器),这些相当于一个状态机,用来控制Pod的具体状态和行为

RS、Deployment控制器用来控制pod的,还有一些其他控制器控制其他的资源

Pod 和控制器
你可以使用工作负载资源来创建和管理多个 Pod。 资源的控制器能够处理副本的管理、上线,并在 Pod 失效时提供自愈能力。 例如,如果一个节点失败,控制器注意到该节点上的 Pod 已经停止工作, 就可以创建替换性的 Pod。调度器会将替身 Pod 调度到一个健康的节点执行。

Pod 的分类

  • 自主式 Pod:Pod 退出了,此类型的 Pod 不会被创建
  • 控制器管理的 Pod:在控制器的生命周期里,始终要维持 Pod 的副本数目

2. 常用控制器

  • ReplicationController(旧版本)
  • ReplicaSet
  • Deployment
  • DaemonSet
  • Job/Cronjob

3. 自主式pod

# 自主式的pod: 单独定义一个pod,这个没有副本控制器管理,也没有对应deployment
# self-pod.yaml
apiVersion: v1
kind: Pod
metadata:
 name: init-pod
 labels:
   app: myapp
spec:
 containers:
 - name: myapp
   image: hub.kaikeba.com/java12/myapp:v1 # pod内部运行的容器

注意事项/总结:
k8s资源对象(所有的k8s管理的资源,都叫做资源对象),都可以独立存在,但是需要根据相应原理,需求结合使用。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

可以通过命令kubectl describe pod podName 查看创建过程详情
kubectl get pod -o wide #查看pod状态详情

自主式pod没有rs、deployment,一但删掉不会重新创建
在这里插入图片描述

4. RC&RS

ReplicationController (RC)用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收;

在新版本的Kubernetes中建议使用Replicaset来取代ReplicationController. ReplicaSet跟 ReplicationController没有本质的不同,只是名字不一样,并且ReplicaSet支持集合式的selector;

apiVersion: extensions/v1beta1 
kind: ReplicaSet
metadata:
  name: frontend 
spec:
  replicas: 3 #3个副本
  selector:
    matchLabels: 
      tier: frontend 
  template: # 下面配置的是pod
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
        - name: java-nginx #首字母不能大写
          image: hub.kaikeba.com/java12/myapp:v1
          env:
            - name: GET_HOSTS_FROM 
              value: dns
          ports:
            - containerPort: 80
            

要支持滚动更新,还需要Deployment,这里主要说明K8S的资源对象都是可以单独部署的

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

修改其中一个pod的标签,这样该pod就脱离原来的RS管理了,而RS会再次创建一个pod,保持副本数量:
在这里插入图片描述

由此可见RS副本控制器是通过标签维护pod的

不受RS控制的pod就是自主式pod,删除后不再重建:
在这里插入图片描述

5. Deployment

5.1 创建Deployment

Deployment为Pod和ReplicaSet提供了一个声明式定义(declarative)方法,用来替代以前的 ReplicationController来方便的管理应用。典型的应用场景包括;

  • 定义Deployment来创建Pod和ReplicaSet
  • 滚动升级和回滚应用
  • 扩容和缩容
  • 暂停和继续Deployment
#1)、部署一简单的Nginx应用
apiVersion: extensions/v1beta1 
kind: Deployment 
metadata:
  name: nginx-deployment 
spec:
  replicas: 3 #副本控制器
  template:
    metadata:
      labels:
        app: nginx 
    spec:
      containers: 
        - name: nginx 
          image: hub.kaikeba.com/java12/myapp:v1
          ports:
            - containerPort: 80

在这里插入图片描述

在这里插入图片描述

创建资源,同时把之前创建的资源删掉,避免干扰
在这里插入图片描述

退出结束,只剩下nginx的:
在这里插入图片描述

5.2 根据cpu利用率自动扩容

根据cpu利用率自动扩容:

 kubectl autoscale deployment [deployment Name] --min=[最小pod数量] --max=[最大pod数量] --cpu-percent=[根据cpu利用率]

在这里插入图片描述

设置了最小pod数为6,最大为15,需要等待一会儿才会变:
在这里插入图片描述

为什么会出现扩容效果,因为刚才的命令创建了hpa组件资源
通过hpa对象监控rs,实现pod自动扩容
在这里插入图片描述

5.3 滚动更新

演示滚动升级,为Deployment设置新版本镜像:

set image deployment [deploymentName] [containerName]=[新版本镜像路径]

在这里插入图片描述

在这里插入图片描述
我们知道pod的命名规则是 [deploymentName]-[ReplicaSetName]-[PodName]
图中可以看到升级后是新的RS,滚动更新的原理就是创建新的RS
回滚也是同理,回滚到之前的RS

5.4 回滚

演示回滚:

kubectl rollout undo deployment [deploymentName]

在这里插入图片描述
留意一下回滚后的RS名字,和上面的对比一下。

测试完成,清空环境:
在这里插入图片描述

Deployment更新策略

  • Deployment可以保证在升级时只有一定数量的Pod是down的。默认的,它会确保至少有比期望的Pod数量少一个是up状态(最多一个不可用)
  • Deployment同时也可以确保只创建出超过期望数量的一定数量的Pod,默认的,它会确保最多比期望的Pod数量多一个的Pod是up的(最多1个surge )

未来的Kuberentes版本中,将从1-1变成25%-25%

 kubect1 describe deployments

6. DaemonSet

#确保只运行一个副本,运行在集群中每一个节点上。(也可以部分节点上只运行一个且只有一个pod副本,如监控ssd硬盘)
# kubectl explain ds
# vim daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata: #元数据信息,描述DaemonSet
  name: my-deamon
  namespace: default
  labels: 
    app: daemonset
spec: # RS副本控制器
  selector:
    matchLabels:
      app: my-daemonset
  template: #描述pod
    metadata:
      labels:
        app: my-daemonset
    spec:
      containers: #描述容器
      - name: daemon-app
        image: hub.kaikeba.com/java12/myapp:v1

在这里插入图片描述

在这里插入图片描述

看到DaemonSet会在每个node节点上会运行一个pod
在这里插入图片描述

删除其中一个pod也会自动重新创建一个:
在这里插入图片描述

7. Job

Job负责处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。而CronJob则就是在Job上加上了时间调度。官方文档Jobs

# 我们用Job这个资源对象来创建一个任务,我们定一个Job来执行一个倒计时的任务,定义YAML文件:
apiVersion: batch/v1
kind: Job
metadata:
  name: job-demo
spec:
  template: #描述pod
    metadata:
      name: job-demo
    spec:
      restartPolicy: Never #pod重启策略
      containers:
      - name: counter
        image: busybox #指定镜像,busybox 是网络上的镜像
        command: #容器启动后执行的命令
        - "bin/sh"
        - "-c"
        - "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"
# 创建
kubectl apply -f job.yaml
# 查询日志
kubectl logs 

注意JobRestartPolicy(重启策略)仅支持NeverOnFailure两种,不支持Always,我们知道Job就相当于来执行一个批处理任务,执行完就结束了,如果支持Always的话是不是就陷入了死循环了?

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

看到多了一个job的pod,状态是Completed,READY是0/1,说明已经执行结束了
PS:执行完成后是不会主动删除pod的,但是pod中的容器已经结束了
还有一个job任务处理控制器,查看该job的日志:
在这里插入图片描述

更多查看日志命令
kubectl logs --help

8. cronJob

CronJob其实就是在Job的基础上加上了时间调度,我们可以:在给定的时间点运行一个任务,也可以周期性地在给定时间点运行。这个实际上和我们Linux中的crontab就非常类似了。官方文档:CronJob

一个CronJob对象其实就对应crontab文件中的一行,它根据配置的时间格式周期性地运行一个Job,格式和crontab也是一样的。

crontab的格式如下:

分 时 日 月 星期 要运行的命令
第1列分钟 0~59
第2列小时 0~23)
第3列日 1~31
第4列月 1~12
第5列星期 0~7(0和7表示星期天)
第6列要运行的命令

Linux crontab 命令 | 菜鸟教程

# 现在,我们用CronJob来管理我们上面的Job任务
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cronjob-demo
spec:
  schedule: "*/1 * * * *" #调度规则 */1 表示每分钟调度一次 
  jobTemplate: #任务模板
    spec:
      template: #描述pod
        spec:
          restartPolicy: OnFailure #pod重启策略
          containers: #描述容器
          - name: hello
            image: busybox
            args:
            - "bin/sh"
            - "-c"
            - "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"
# 创建cronjob
kubctl apply -f cron-job.yaml
# 查询cronjob
kubectl get cronjob
# 查询jon ,cronjon会循环多个job
kubectl get job 
# 实时监控查询job 
kubectl get job -w

我们这里的KindCronJob了,要注意的是spec.schedule字段是必须填写的,用来指定任务运行的周期,格式就和crontab一样,另外一个字段是spec.jobTemplate, 用来指定需要运行的任务,格式当然和Job是一致的。还有一些值得我们关注的字段spec.successfulJobsHistoryLimit

在这里插入图片描述

使用镜像仓库的镜像:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

创建完cronjob后查看pod是没有的,因为策略是每分钟执行一次,还没到1分钟
在这里插入图片描述

等待1分钟后,查看job、pod:
在这里插入图片描述

到了指定时间后会创建pod,在pod内部进行调度

使用kubectl job -w(watche)进行监控,可以看到调用多次了
在这里插入图片描述

二、Pod’s lifecycle

1. 再次理解Pod

Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。Pods官方文档

Pod (就像在鲸鱼荚或者豌豆荚中)是一组(一个或多个) 容器; 这些容器共享存储、网络、以及怎样运行这些容器的声明。 Pod 中的内容总是并置(colocated)的并且一同调度,在共享的上下文中运行。 Pod 所建模的是特定于应用的“逻辑主机”,其中包含一个或多个应用容器, 这些容器是相对紧密的耦合在一起的。 在非云环境中,在相同的物理机或虚拟机上运行的应用类似于 在同一逻辑主机上运行的云应用。

除了应用容器,Pod 还可以包含在 Pod 启动期间运行的 Init 容器。 你也可以在集群中支持临时性容器 的情况外,为调试的目的注入临时性容器。

1.1 什么是 Pod?

除了 Docker 之外,Kubernetes 支持 很多其他容器运行时, Docker 是最有名的运行时, 使用 Docker 的术语来描述 Pod 会很有帮助。

Pod 的共享上下文包括一组 Linux 名字空间、控制组(cgroup)和可能一些其他的隔离 方面,即用来隔离 Docker 容器的技术。 在 Pod 的上下文中,每个独立的应用可能会进一步实施隔离。

就 Docker 概念的术语而言,Pod 类似于共享名字空间和文件系统卷的一组 Docker 容器。

1.2 使用 Pod

通常你不需要直接创建 Pod,甚至单实例 Pod。 相反,你会使用诸如 Deployment 或 Job 这类工作负载资源 来创建 Pod。如果 Pod 需要跟踪状态, 可以考虑 StatefulSet 资源。

这是因为 Pod 被设计成了相对临时性的、用后即抛的一次性实体。 当 Pod 由你或者间接地由 控制器 创建时,它被调度在集群中的节点上运行。 Pod 会保持在该节点上运行,直到 Pod 结束执行、Pod 对象被删除、Pod 因资源不足而被 驱逐 或者节点失效为止。

注意:重启 Pod 中的容器不应与重启 Pod 混淆。 Pod 不是进程,而是容器运行的环境。Pod 天生地为其成员容器提供了两种共享资源:网络 和 存储。 在被删除之前,Pod 会一直存在。

Kubernetes 集群中的 Pod 主要有两种用法:

  • 运行单个容器的 Pod。“每个 Pod 一个容器”模型是最常见的 Kubernetes 用例; 在这种情况下,可以将 Pod 看作单个容器的包装器,并且 Kubernetes 直接管理 Pod,而不是容器。
  • 运行多个协同工作的容器的 Pod。 Pod 可能封装由多个紧密耦合且需要共享资源的共处容器组成的应用程序。 这些位于同一位置的容器可能形成单个内聚的服务单元 —— 一个容器将文件从共享卷提供给公众, 而另一个单独的“挂斗”(sidecar)容器则刷新或更新这些文件。 Pod 将这些容器和存储资源打包为一个可管理的实体。

每个 Pod 都旨在运行给定应用程序的单个实例。如果希望横向扩展应用程序(例如,运行多个实例 以提供更多的资源),则应该使用多个 Pod,每个实例使用一个 Pod。 在 Kubernetes 中,这通常被称为 副本(Replication)。 通常使用一种工作负载资源及其控制器 来创建和管理一组 Pod 副本。

服务如何部署:

  • 1、建议一个pod中部署一个容器(通常情况下都是这样的)
  • 2、有一些业务上紧密耦合的服务,可以部署在一个容器,通信效率比较高。

2. Pod Phase

Pod 的生命周期 官方文档

Pod 的 status 属性是一个 PodStatus 对象,拥有一个 phase 字段。它简单描述了 Pod 在其生命周期的阶段。

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

如果某节点死掉或者与集群中其他节点失联,Kubernetes 会实施一种策略,将失去的节点上运行的所有 Pod 的 phase 设置为 Failed。

3. 容器状态

Kubernetes 会跟踪 Pod 中每个容器的状态,就像它跟踪 Pod 总体上的阶段一样。 你可以使用容器生命周期回调 来在容器生命周期中的特定时间点触发事件。

一旦调度器将 Pod 分派给某个节点,kubelet 就通过 容器运行时 开始为 Pod 创建容器。 容器的状态有三种:Waiting(等待)、Running(运行中)和 Terminated(已终止)。

要检查 Pod 中容器的状态,可以使用如下命令,其输出中包含 Pod 中每个容器的状态:

kubectl describe pod <pod 名称>
  • Waiting (等待)
    如果容器并不处在 Running 或 Terminated 状态之一,它就处在 Waiting 状态。 处于 Waiting 状态的容器仍在运行它完成启动所需要的操作:例如,从某个容器镜像 仓库拉取容器镜像,或者向容器应用 Secret 数据等等。 当你使用 kubectl 来查询包含 Waiting 状态的容器的 Pod 时,你也会看到一个 Reason 字段,其中给出了容器处于等待状态的原因。

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

  • Terminated(已终止)
    处于 Terminated 状态的容器已经开始执行并且或者正常结束或者因为某些原因失败。 如果你使用 kubectl 来查询包含 Terminated 状态的容器的 Pod 时,你会看到 容器进入此状态的原因、退出代码以及容器执行期间的起止时间。
    如果容器配置了 preStop 回调,则该回调会在容器进入 Terminated 状态之前执行。

4. 容器重启策略

Pod 的 spec 中包含一个 restartPolicy 字段,其可能取值包括 Always、OnFailure 和 Never。默认值是 Always。

  • Always : 容器失效时,kubelet 自动重启该容器;
  • OnFailure : 容器终止运行且退出码不为0时重启;
  • Never : 不论状态为何, kubelet 都不重启该容器。

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

5. 生命周期详解

pod生命周期示意图(初始化容器,post start,main container…,pre stop):
在这里插入图片描述

主容器,就是我们指定的镜像构建的容器

  • 说明:

    • 初始化容器阶段初始化pod中每一个init容器,他们是串行执行的,执行完成后就退出了
    • 启动主容器main container
    • 在main container刚刚启动之后可以执行post start命令
    • 在整个main container执行的过程中可以做两类探测:liveness probe(存活探测)readiness probe(就绪探测)
    • 在main container结束前可以执行pre stop命令
  • 配置启动后钩子(post start)和终止前钩子(pre stop)

    • post start:容器创建之后立即执行,如果失败了就会按照重启策略重启容器
    • pre stop:容器终止前立即执行,执行完成之后容器将成功终止
  • 可以使用以下命令查看post start和pre stop的设置格式:

    • kubectl explain pod.spec.containers.lifecycle.preStop
    • kubectl explain pod.spec.containers.lifecycle.postStart

6. pod init

理解 Init 容器 官方文档

6.1 理解init容器(初始化容器)

每个 Pod 中可以包含多个容器, 应用运行在这些容器里面,同时 Pod 也可以有一个或多个先于应用容器启动的 Init 容器。

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

  • 它们总是运行到完成。
  • 每个都必须在下一个启动之前成功完成。(初始化容器是串行化过程)

如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止。 然而,如果 Pod 对应的 restartPolicy 值为 “Never”,Kubernetes 不会重新启动 Pod。

在这里插入图片描述
图画错了,就绪探针不会重启

为 Pod 设置 Init 容器需要在 Pod 的 spec 中添加 initContainers 字段, 该字段以 Container 类型对象数组的形式组织,和应用的 containers 数组同级相邻。 Init 容器的状态在 status.initContainerStatuse 字段中以容器状态数组的格式返回 (类似 status.containerStatuses 字段)

6.2 init作用

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

  • Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。 例如,没有必要仅为了在安装过程中使用类似 sed、awk、python 或 dig 这样的工具而去 FROM 一个镜像来生成一个新的镜像。
  • Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低。
  • 应用镜像的创建者和部署者可以各自独立工作,而没有必要联合构建一个单独的应用镜像。
  • Init 容器能以不同于 Pod 内应用容器的文件系统视图运行。因此,Init 容器可以访问 应用容器不能访问的 Secret 的权限。
  • 由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器 提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。 一旦前置条件满足,Pod 内的所有的应用容器会并行启动。

示例,下面是一些如何使用 Init 容器的想法:

  • 等待一个 Service 完成创建,通过类似如下 shell 命令:
    for i in {
         1..100}; do sleep 1; if dig myservice; then exit 0; fi; exit 1
    
  • 注册这个 Pod 到远程服务器,通过在命令中调用 API,类似如下:
    curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register \
      -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
    
  • 在启动应用容器之前等一段时间,使用类似命令:
  • 克隆 Git 仓库到卷中。
  • 将配置值放到配置文件中,运行模板工具为主应用容器动态地生成配置文件。 例如,在配置文件中存放 POD_IP 值,并使用 Jinja 生成主应用配置文件。

6.3 特殊说明

  • 在 Pod 启动过程中,每个 Init 容器会在网络和数据卷初始化之后按顺序启动。 kubelet 运行依据 Init 容器在 Pod 规约中的出现顺序依次运行之。
  • 每个 Init 容器成功退出后才会启动下一个 Init 容器。 如果某容器因为容器运行时的原因无法启动,或以错误状态退出,kubelet 会根据 Pod 的 restartPolicy 策略进行重试。 然而,如果 Pod 的 restartPolicy 设置为 “Always”,Init 容器失败时会使用 restartPolicy 的 “OnFailure” 策略。
  • 在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态。 Init 容器的端口将不会在 Service 中进行聚集。正在初始化中的 Pod 处于 Pending 状态, 但会将状况 Initializing 设置为 true。
  • 如果 Pod 重启,所有 Init 容器必须重新执行。
  • 对 Init 容器规约的修改仅限于容器的 image 字段。 更改 Init 容器的 image 字段,等同于重启该 Pod。

    (第一次创建以后后期修改初始化容器,spec的相关配置都不起作用,只能改image字段)

  • 因为 Init 容器可能会被重启、重试或者重新执行,所以 Init 容器的代码应该是幂等的。 特别地,基于 emptyDirs 写文件的代码,应该对输出文件可能已经存在做好准备。
  • Init 容器具有应用容器的所有字段。然而 Kubernetes 禁止使用 readinessProbe, 因为 Init 容器不能定义不同于完成态(Completion)的就绪态(Readiness)。 Kubernetes 会在校验时强制执行此检查。

    (Init容器只有完成状态,所以不需要、也没有就绪检测、存活检测)

  • 在 Pod 上使用 activeDeadlineSeconds 和在容器上使用 livenessProbe 可以避免 Init 容器一直重复失败。activeDeadlineSeconds 时间包含了 Init 容器启动的时间。
  • 在 Pod 中的每个应用容器和 Init 容器的名称必须唯一; 与任何其它容器共享同一个名称,会在校验时抛出错误。

6.4 案例演示

官网更好的例子

#init-pod.yaml
apiVersion: v1
kind: Pod
metadata:
 name: init-pod
 labels:
   app: myapp
spec:
 containers:
 - name: myapp
   image: hub.kaikeba.com/java12/busybox:v1
   command: ['sh', '-c', 'echo -n "running at " && date +%T && sleep 600']
 initContainers: # 这里指定的就是初始化容器!!!
 - name: init-mydb
   image: hub.kaikeba.com/java12/busybox:v1
   command: ['sh', '-c', 'until nslookup init-db; do echo waiting for init-db;date +%T; sleep 2;echo; done;'] 
   # 这里执行的指令 nslookup 是查找init-db服务,until 是循环直到找到

清空cronjob的环境:
在这里插入图片描述

新建一个目录lifecicle:
在这里插入图片描述

在这里插入图片描述

初始化容器会先于主容器运行,如果初始化容器运行有问题主容器也不会运行成功:

在这里插入图片描述

可以看到状态一直是init状态,查看详情:
kubectl describe pod [podName]
在这里插入图片描述

在这里插入图片描述

看不出什么,看下容器里面日志:
kubectl logs [pod名字] -c [pod中指定容器名字]
在这里插入图片描述

在这里插入图片描述

#init-svc.yaml
kind: Service
apiVersion: v1
metadata:
 name: init-db
spec:
 ports:
   - protocol: TCP
     port: 80
     targetPort: 3366

现在我们创建init-db服务:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

等待一会儿后
在这里插入图片描述

7. 容器探针

7.1 理解容器探针

容器探针 官方文档

Probe 是由 kubelet 对容器执行的定期诊断。 要执行诊断,kubelet 调用由容器实现的 Handler (处理程序)。有三种类型的处理程序:

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

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

  • Success(成功):容器通过了诊断。
  • Failure(失败):容器未通过诊断。
  • Unknown(未知):诊断失败,因此不会采取任何行动。

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

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

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

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

    注意Pod和容器的关系

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

    何时该使用就绪态探针?
    如果要仅在探测成功时才开始向 Pod 发送请求流量,请指定就绪态探针。 在这种情况下,就绪态探针可能与存活态探针相同,但是规约中的就绪态探针的存在意味着 Pod 将在启动阶段不接收任何数据,并且只有在探针探测成功后才开始接收数据。

    如果你的容器需要加载大规模的数据、配置文件或者在启动期间执行迁移操作,可以添加一个 就绪态探针。

    如果你希望容器能够自行进入维护状态,也可以指定一个就绪态探针,检查某个特定于 就绪态的因此不同于存活态探测的端点。

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

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

    何时该使用启动探针?
    对于所包含的容器需要较长时间才能启动就绪的 Pod 而言,启动探针是有用的。 你不再需要配置一个较长的存活态探测时间间隔,只需要设置另一个独立的配置选定, 对启动期间的容器执行探测,从而允许使用远远超出存活态时间间隔所允许的时长。

    如果你的容器启动时间通常超出 initialDelaySeconds + failureThreshold × periodSeconds 总值,你应该设置一个启动探测,对存活态探针所使用的同一端点执行检查。 periodSeconds 的默认值是 30 秒。你应该将其 failureThreshold 设置得足够高, 以便容器有充足的时间完成启动,并且避免更改存活态探针所使用的默认值。 这一设置有助于减少死锁状况的发生。

7.2 案例演示 – 就绪检测

官方更详细的例子

  • readinessProbe-httpget
    #readinessProbe-httpget
    apiVersion: v1 
    kind: Pod 
    metadata:
      name: readiness-httpget-pod 
      namespace: default
    spec:
      containers:
      - name: readiness-httpget-container 
        image: hub.kaikeba.com/java12/myapp:v1 #myapp是一个nginx镜像
        imagePullPolicy: IfNotPresent
        readinessProbe: # 关键在这,就绪检测
          httpGet: #使用http 协议get请求
            port: 80 #访问端口
            path: /index1.html #访问路径,如果是微服务架构就可以写 健康检查的路径/health/xx
          initialDelaySeconds: 1 #第一次探测前应该等待的时间
          periodSeconds: 3 #每隔3秒检测一次
    

    在这里插入图片描述

    在这里插入图片描述

    清除之前的测试环境:
    在这里插入图片描述

    创建
    在这里插入图片描述

    可以看到ready是0/1,只有一个容器,但是没有跑起来,这个就是僵尸程序
    僵尸程序:pod状态是running,但是ready中显示0/1,总共一个容器,但是没有一个容器是ready状态,即没有跑起来,这样的情况下pod是不能对外服务的,所以就认为这个pod内部程序是僵尸程序
    当然也要分情况,有的时候需求就是需要暂时不能对外服务。

    kubectl describe pod [podName]查看状态:
    在这里插入图片描述

    在这里插入图片描述

    看到就绪检测得到的响应是404

    登录当前pod的容器(里面只有一个容器,不用指定容器名):
    在容器中的nginx目录下创建index1.html页面,再次查看pod状态
    在这里插入图片描述

7.3 案例演示 – 存活检测

  • livenessProbe-exec方式

    apiVersion: v1 
    kind: Pod 
    metadata:
      name: liveness-exec-pod
      namespace: default 
    spec:
      containers: 
      - name: liveness-exec-container 
        image: hub.kaikeba.cn/java12/myapp:v1
        imagePullPolicy: IfNotPresent  # 镜像拉取策略,本地有从本地拉取,没有再从镜像仓库拉取
        #容器内部运行指令,创建/tmp/live文件、睡眠60毫秒、删除/tmp/live文件、睡眠3600毫秒
        command: ["/bin/sh","-c","touch /tmp/live;sleep 60;rm -rf /tmp/live;sleep 3600"] 
        livenessProbe:                       #存活检测!
          exec:                              # exec方式
            command: ["test","-e","/tmp/live"] # 检测文件存不存在
          initialDelaySeconds: 1 #1秒后再检测
          periodSeconds: 3 #每3秒检测一次
          
    

    在这里插入图片描述

    在这里插入图片描述

    删除之前的测试环境,创建新的,对pod进行监控
    在这里插入图片描述

    等待一定时间后看到pod重启了:
    在这里插入图片描述

    因为检测的时候发现文件不见了,意味着pod出问题了就会将pod重启
    在这里插入图片描述

  • livenessProbe-Httpget方式

    apiVersion: v1 
    kind: Pod 
    metadata:
      name: liveness-httpget-pod 
      namespace: default 
    spec:
      containers: 
      - name: liveness-httpget-container 
        image: hub.kaikeba.com/java12/myapp:v1
        imagePullPolicy: IfNotPresent 
        ports:
        - name: http 
          containerPort: 80 
        livenessProbe: #存活检测!
          httpGet: #http get方式
            port: http  
            path: /index.html  # nginx中index.html默认是存在的
          initialDelaySeconds: 1
          periodSeconds: 3
          timeoutSeconds: 10 # 检测超时时间
    

    在这里插入图片描述

    在这里插入图片描述

    此时没问题,因为检测的时候,这个文件是在的
    在这里插入图片描述

    进入pod中的容器删除该文件:
    在这里插入图片描述

    只会重启一次,因为重启是创建新的pod,文件又存在了

  • livenessProbe-Tcp方式

    apiVersion: v1 
    kind: Pod 
    metadata:
      name: probe-tcp 
    spec:
      containers:
      - name: nginx
        image: hub.kaikeba.com/java12/myapp:v1 
        livenessProbe: #存活检测!
          initialDelaySeconds: 5 # 5秒后在开始检测
          timeoutSeconds: 1 # 检测超时时间
          tcpSocket:
            port: 80 #通过tcp直接访问80端口检测
          periodSeconds: 3 # 每3秒检测一次
    

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    如果关闭了80端口就会重启

7.4 案例演示 – 存活、就绪检测都使用

apiVersion: v1 
kind: Pod 
metadata:
  name: liveness-httpget-pod 
  namespace: default 
spec:
  containers: 
  - name: liveness-httpget-container 
    image: hub.kaikeba.com/java12/myapp:v1 
    imagePullPolicy: IfNotPresent 
    ports:
    - name: http 
      containerPort: 80 
    readinessProbe: # 就绪检测
      httpGet: 
        port: 80
        path: /index1.html # 就绪检测还是访问index1.html
      initialDelaySeconds: 1 
      periodSeconds: 3 
    livenessProbe: # 存活检测
      httpGet:
        port: http 
        path: /index.html  # 存活检测也用http方式检测index.html
      initialDelaySeconds: 1
      periodSeconds: 3
      timeoutSeconds: 10
 

在这里插入图片描述

在这里插入图片描述

清空刚才的测试环境:
在这里插入图片描述

看到pod是running,但是里面的容器没有ready,因为就绪检测失败了:
kubectl describe pod [podName]查看状态:
在这里插入图片描述

登录容器
在这里插入图片描述

看到就绪检测成功

再次登录容器直接执行删除index.html,校验存活检测:
在这里插入图片描述

等个几秒中后看到重启了,新的pod里面的文件又恢复了
在这里插入图片描述

8. 启动及退出动作(start/stop钩子函数)

容器生命周期回调 官方文档
有点类似于Spring Bean生命周期的init和destory函数

apiVersion: v1 
kind: Pod 
metadata:
  name: lifecycle-startstop 
spec:
  containers:
  - name: lifecycle-container
    image: nginx
    lifecycle: #生命周期
      postStart: #start
        exec:
          # 容器启动的时候自动执行start钩子函数,打印一段话输出到/usr/share/message
          command: ["/bin/sh","-c","echo Hello from the postStart handler > /usr/share/message"]
      preStop: #stop,退出的时候自动执行,退出看不了,因为pod没了
        exec:
          command: ["/bin/sh","-c","echo Hello container stop"]

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

张贴在2