Pod生命周期
Pod 生命周期
前面我们已经了解了 Pod 的设计原理,接下来我们来了解下 Pod 的生命周期。下图展示了一个 Pod 的完整生命周期过程,其中包含 Init Container
、Pod Hook
、健康检查
三个主要部分,接下来我们就来分别介绍影响 Pod 生命周期的部分:
首先在介绍 Pod 的生命周期之前,我们先了解下 Pod 的状态,因为 Pod 状态可以反应出当前我们的 Pod 的具体状态信息,也是我们分析排错的一个必备的方式。
目录
[toc]
推荐文章
我的开源项目:
首先先了解下 Pod 的状态值,我们可以通过 kubectl explain pod.status
命令来了解关于 Pod 状态的一些信息,Pod 的状态定义在 PodStatus
对象中,其中有一个 phase
字段,下面是 phase
的可能取值:
[root@master1 ~]#kubectl explain pod.statusphase<string>ThephaseofaPodisasimple,high-levelsummaryofwherethePodisinitslifecycle.Theconditionsarray,thereasonandmessagefields,andtheindividualcontainerstatusarrayscontainmoredetailaboutthepod'sstatus. There are five possible phase values:Pending:The pod has been accepted by the Kubernetes system,but one ormore of the container images has not been created. This includes timebefore being scheduled as well as time spent downloading images over thenetwork,which could take a while. Running:The pod has been bound to anode,and all of the containers have been created. At least one containeris still running,or is in the process of starting or restarting.Succeeded:All containers in the pod have terminated in success,and willnot be restarted. Failed:All containers in the pod have terminated,and atleast one container has terminated in failure. The container either exitedwith non-zero status or was terminated by the system. Unknown:For somereason the state of the pod could not be obtained,typically due to anerror in communicating with the host of the pod.
Pod 状态(5种状态)
挂起(Pending):Pod 信息已经提交给了集群,但是还没有被调度器调度到合适的节点或者 Pod 里的镜像正在下载。
运行中(Running):该 Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或重启状态**。**
成功(Succeeded):Pod 中的所有容器都被成功终止,并且不会再重启
失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非
0
状态退出或者被系统终止。未知(Unknown):因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败导致的。
注意:这些状态都是集群帮我们维护的。
除此之外,PodStatus
对象中还包含一个 PodCondition
的数组,里面包含的属性有:
[root@master1 ~]#kubectl explain pod.status.conditionsFIELDS:lastProbeTime<string>Lasttimeweprobedthecondition.lastTransitionTime<string>Lasttimetheconditiontransitionedfromonestatustoanother.message<string>Human-readablemessageindicatingdetailsaboutlasttransition.reason<string>Unique,one-word,CamelCasereasonforthecondition's last transition.status <string>-required-Status is the status of the condition. Can be True,False,Unknown. Moreinfo:https:type <string>-required-Type is the type of the condition. More info:https:[root@master1 ~]#
lastProbeTime:最后一次探测 Pod Condition 的时间戳。
lastTransitionTime:上次 Condition 从一种状态转换到另一种状态的时间。
message:上次 Condition 状态转换的详细描述。
reason:Condition 最后一次转换的原因。
status:Condition 状态类型,可以为 “True”,“False”,and “Unknown”.
type:Condition 类型,包括以下方面:
- PodScheduled(Pod 已经被调度到其他 node 里)
- Ready(Pod 能够提供服务请求,可以被添加到所有可匹配服务的负载平衡池中)
- Initialized(所有的
init containers
已经启动成功)
- Initialized(所有的
- Unschedulable(调度程序现在无法调度 Pod,例如由于缺乏资源或其他限制)
- ContainersReady(Pod 里的所有容器都是 ready 状态)
注意:这个pod.status.conditions在排错上还是很有用的。
案例:[root@master1 ~]#kubectl get po coredns-7b884d5cb7-6jzn6 -nkube-system -oyaml……status:conditions:-lastProbeTime:nulllastTransitionTime:"2022-12-04T22:53:44Z"status:"True"type:Initialized-lastProbeTime:nulllastTransitionTime:"2022-12-04T22:53:45Z"status:"True"type:Ready-lastProbeTime:nulllastTransitionTime:"2022-12-04T22:53:45Z"status:"True"type:ContainersReady-lastProbeTime:nulllastTransitionTime:"2022-12-04T22:53:44Z"status:"True"type:PodScheduledcontainerStatuses:-containerID:containerd:image:docker.io/coredns/coredns:1.9.3imageID:sha256:5185b96f0becf59032b8e3646e99f84d9655dff3ac9e2605e0dc77f9c441ae4alastState:{}name:corednsready:truerestartCount:0started:truestate:running:startedAt:"2022-12-04T22:53:45Z"hostIP:172.29.9.61phase:RunningpodIP:10.244.0.3podIPs:-ip:10.244.0.3qosClass:BurstablestartTime:"2022-12-04T22:53:44Z"
2、重启策略
Pod对象:重启策略+健康检查(应用自修复)。
我们可以通过配置 restartPolicy
字段来设置 Pod 中所有容器的重启策略,其可能值为 Always
、OnFailure
和 Never
,默认值为 Always
。
重启策略(restartPolicy):
• Always:当容器终止退出后,总是重启容器,默认策略。 #所有的应用服务;守护进程:nginx、tomcat;
• OnFailure:当容器异常退出(退出状态码非0)时,才重启容器。 #定时任务:备份数据库
• Never:当容器终止退出,从不重启容器。 # 临时任务:处理数据库数据
restartPolicy
指通过 kubelet 在同一节点上重新启动容器。通过 kubelet 重新启动的退出容器将以指数增加延迟(10s,20s,40s…)重新启动,上限为 5 分钟,并在成功执行 10 分钟后重置。(这个是针对于**always**
策略)
那么问题来了:
OnFailure策略是容器异常退出后pod只重启一次吗?如果重启多次的话那不是和always一样的效果了?
不同类型的的控制器可以控制 Pod 的重启策略:
Job
:适用于一次性任务如批量计算,任务结束后 Pod 会被此类控制器清除。Job 的重启策略只能是"OnFailure"
或者"Never"
。ReplicaSet
、Deployment
:此类控制器希望 Pod 一直运行下去,它们的重启策略只能是"Always"
。DaemonSet
:每个节点上启动一个 Pod,很明显此类控制器的重启策略也应该是"Always"
。
注意:在k8s中没有重启的概念,删除pod后会被重建。重启策略==重建。
3、初始化容器(InitCongtainer
)
Init Container:顾名思义,用于初始化工作,执行完就结束,可以理解为一次性任务。
支持大部分应用容器配置,但不支持健康检查
优先应用容器执行
了解了 Pod 状态后,首先来了解下 Pod 中最先启动的 Init Container
,也就是我们平时常说的初始化容器。Init Container
就是用来做初始化工作的容器,可以是一个或者多个,如果有多个的话,这些容器会按定义的顺序依次执行。我们知道一个 Pod 里面的所有容器是共享数据卷和 Network Namespace的,所以 Init Container
里面产生的数据可以被主容器使用到。从上面的 Pod 生命周期的图中可以看出初始化容器是独立与主容器之外的,只有所有的`初始化容器执行完之后,主容器才会被启动。
那么初始化容器有哪些应用场景呢:
环境检查(等待其他模块 Ready) 例如确保应用容器依赖的服务启动后再启动应用容器。这个可以用来解决服务之间的依赖问题,比如我们有一个 Web 服务,该服务又依赖于另外一个数据库服务,但是在我们启动这个 Web 服务的时候我们并不能保证依赖的这个数据库服务就已经启动起来了,所以可能会出现一段时间内 Web 服务连接数据库异常。要解决这个问题的话我们就可以在 Web 服务的 Pod 中使用一个
InitContainer
,在这个初始化容器中去检查数据库是否已经准备好了,准备好了过后初始化容器就结束退出,然后我们主容器的 Web 服务才被启动起来,这个时候去连接数据库就不会有问题了。初始化配置 例如给应用容器准备配置文件。比如集群里检测所有已经存在的成员节点,为主容器准备好集群的配置信息,这样主容器起来后就能用这个配置信息加入集群。
其它场景:如将 Pod 注册到一个中央数据库、配置中心等。
日志采集也不能放到init container里去,可以放到一个pod里去。
==💘 实战:初始化容器测试-2022.12.9(成功测试)==
- 实验环境
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,2个master节点,1个node节点
k8s version:v1.20
CONTAINER-RUNTIME:docker://20.10.7
实验软件(无)
创建pod资源清单
比如现在我们来实现一个功能,在 Nginx Pod 启动之前去重新初始化首页内容,如下所示的资源清单:
#init-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
volumes:
- name: workdir
emptyDir: {} #注意:emptyDir{},这个是一个临时的目录,数据会保存在 kubelet 的工作目录下面,生命周期等同于 Pod 的生命周期。
initContainers:
- name: install
image: busybox
command:
- wget
- "-O"
- "/work-dir/index.html"
- http://www.baidu.com # https
volumeMounts:
- name: workdir
mountPath: "/work-dir"
containers:
- name: web
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html # nginx容器默认的首页index.html就位于/usr/share/nginx/html
上面的资源清单中我们首先在 Pod 顶层声明了一个名为 workdir 的 Volume
,前面我们用了 hostPath 的模式,这里我们使用的是 emptyDir{}
,这个是一个临时的目录,数据会保存在 kubelet 的工作目录下面,生命周期等同于 Pod 的生命周期。
然后我们定义了一个初始化容器,该容器会下载一个 html 文件到 /work-dir
目录下面,但是由于我们又将该目录声明挂载到了全局的 Volume,同样的主容器 nginx 也将目录 /usr/share/nginx/html
声明挂载到了全局的 Volume,所以在主容器的该目录下面会同步初始化容器中创建的 index.html
文件。
这里需要注意一点的是:
init和main 2个容器挂载到同一个volume,main container原来的文件会默认被init container覆盖掉的。
- 部署并测试
直接创建上面的 Pod:
[root@master1 ~]#kubectl apply -f init-demo.yaml
pod/init-demo created
创建完成后可以查看该 Pod 的状态:
[root@master1 ~]#kubectl get po -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
init-demo 0/1 Init:0/1 0 11s <none> node1 <none> <none>
可以发现 Pod 现在的状态处于 Init:0/1
状态,意思就是现在第一个初始化容器还在执行过程中,此时我们可以查看 Pod 的详细信息:
[root@master1 ~]#kubectl describe pod init-demo
Name: init-demo
Namespace: default
Priority: 0
Node: node1/172.29.9.52
Start Time: Sat, 06 Nov 2021 17:36:21 +0800
Labels: <none>
Annotations: <none>
Status: Running
IP: 10.244.1.6
IPs:
IP: 10.244.1.6
Init Containers:
install:
Container ID: containerd://7b8ae516ae003458e439f9efe9d31c9e33b44593e827b2518007a7dbf103f69e
Image: busybox
Image ID: docker.io/library/busybox@sha256:15e927f78df2cc772b70713543d6b651e3cd8370abf86b2ea4644a9fba21107f
Port: <none>
Host Port: <none>
Command:
wget
-O
/work-dir/index.html
http://www.baidu.com
State: Terminated
Reason: Completed
Exit Code: 0
Started: Sat, 06 Nov 2021 17:36:39 +0800
Finished: Sat, 06 Nov 2021 17:36:39 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-wzm78 (ro)
/work-dir from workdir (rw)
Containers:
web:
Container ID: containerd://194aaf1d7c003029d1d89bafd946de1d919efa15f0e1032184fc4b95d4e05ae8
Image: nginx
Image ID: docker.io/library/nginx@sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Sat, 06 Nov 2021 17:36:55 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/usr/share/nginx/html from workdir (rw)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-wzm78 (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
workdir:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
SizeLimit: <unset>
kube-api-access-wzm78:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 119s default-scheduler Successfully assigned default/init-demo to node1
Normal Pulling 118s kubelet Pulling image "busybox"
Normal Pulled 101s kubelet Successfully pulled image "busybox" in 16.310146437s
Normal Created 101s kubelet Created container install
Normal Started 101s kubelet Started container install
Normal Pulling 100s kubelet Pulling image "nginx"
Normal Pulled 85s kubelet Successfully pulled image "nginx" in 15.212796527s
Normal Created 85s kubelet Created container web
Normal Started 85s kubelet Started container web
[root@master1 ~]#
从上面的描述信息里面可以看到初始化容器已经启动了,现在处于 Running
状态,所以还需要稍等,到初始化容器执行完成后退出初始化容器会变成 Complete状态,然后才会启动主容器。待到主容器也启动完成后,Pod 就会变成
Running` 状态,然后我们去访问下 Pod 主页,验证下是否有我们初始化容器中下载的页面信息:
- 访问init-demo pod ip测试:
[root@master1 ~]#kubectl get po -owideNAMEREADYSTATUSRESTARTSAGEIPNODENOMINATEDNODEREADINESSGATESinit-demo1/1Running03m3s10.244.1.6node1<none><none>[root@master1 ~]#curl 10.244.1.6<!DOCTYPEhtml><!--STATUSOK--><html><head><metahttp-equiv=content-typecontent=text/html;charset=utf-8><metahttp-equiv=X-UA-Compatiblecontent=IE=Edge><metacontent=alwaysname=referrer><linkrel=stylesheettype=text/csshref=http:[root@master1~]#
可以看到,这里访问到的是百度首页内容,符合预期。
实验结束。😘
4、Pod Hook
我们知道 Pod 是 Kubernetes 集群中的最小单元,而 Pod 是由容器组成的,所以在讨论 Pod 的生命周期的时候我们可以先来讨论下容器的生命周期。实际上 Kubernetes 为我们的容器提供了生命周期的钩子,就是我们说的 Pod Hook
。Pod Hook 是由 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。我们可以同时为 Pod 中的所有容器都配置 hook。
Kubernetes 为我们提供了两种钩子函数:
PostStart
:Kubernetes 在容器创建后立即发送 postStart 事件,然而 postStart 处理函数的调用不保证早于容器的入口点(entrypoint) 的执行。 postStart 处理函数与容器的代码是异步执行的,但 Kubernetes的容器管理逻辑会一直阻塞等待 postStart 处理函数执行完毕。只有 postStart 处理函数执行完毕,容器的状态才会变成 RUNNING。PreStop
:这个钩子在容器终止之前立即被调用。它是阻塞的,所以它必须在删除容器的调用发出之前完成。主要用于优雅关闭应用程序、通知其他系统等。
注意:PostStart用的不是很多,而PreStop用的相对很多;
所以我们应该让钩子函数尽可能的轻量。当然有些情况下,长时间运行命令是合理的, 比如在停止容器之前预先保存状态。
Kubernetes 只有在 Pod 结束(Terminated)的时候才会发送 preStop 事件,这意味着在 Pod 完成(Completed)时 preStop 的事件处理逻辑不会被触发,可以查看 issue #55087 https:2、k8s集群:3台centos7.61810虚机,2个master节点,1个node节点k8sversion:v1.20CONTAINER-RUNTIME:docker:apiVersion:v1kind:Podmetadata:name:hook-demo1spec:containers:- name:hook-demo1image:nginxlifecycle:postStart:exec:command:["/bin/sh","-c","echo Hello from the postStart handler >/usr/share/message"]
- 部署
[root@master1 ~]#kubectl apply -f pod-postStart.yamlpod/hook-demo1created[root@master1 ~]#kubectl describe pod hook-demo1Events:TypeReasonAgeFromMessage-------------------------NormalScheduled19sdefault-schedulerSuccessfullyassigneddefault/hook-demo1tonode2NormalPulling18skubeletPullingimage"nginx"NormalPulled3skubeletSuccessfullypulledimage"nginx"in15.461233038sNormalCreated3skubeletCreatedcontainerhook-demo1NormalStarted3skubeletStartedcontainerhook-demo1[root@master1 ~]#kubectl get poNAMEREADYSTATUSRESTARTSAGEhook-demo11/1Running027s
- 测试
创建成功后可以查看容器中 /usr/share/message
文件是否内容正确:
[root@master1 ~]#kubectl exec -it hook-demo1 -- cat /usr/share/messageHelloformpostStarthandler
测试完成。😘
2.PreStop
💘 实战:PreStop钩子函数-2022.12.9(成功测试)
- 实验环境
1、win10,vmwrokstation虚机;2、k8s集群:3台centos7.61810虚机,2个master节点,1个node节点k8sversion:v1.20CONTAINER-RUNTIME:docker:apiVersion:v1kind:Podmetadata:name:hook-demo2spec:containers:- name:hook-demo2image:nginxlifecycle:preStop:exec:command:["/usr/sbin/nginx","-s","quit"] # 优雅退出,注意,如果程序有这个优雅退出的命令,应用才可能使用这个preStop hook功能;---apiVersion:v1kind:Podmetadata:name:hook-demo3#这边使用这个demo来测试验证下preStop hook的效果spec:volumes:- name:messagehostPath:path:/tmpcontainers:- name:hook-demo2image:nginxports:- containerPort:80volumeMounts:- name:messagemountPath:/usr/share/lifecycle:preStop:exec:command:['/bin/sh','-c','echo Hello from the preStop Handler >/usr/share/message']
上面定义的两个 Pod,一个是利用 preStop
来进行优雅删除,另外一个是利用 preStop
来做一些信息记录的事情。
- 部署pod
同样直接创建上面的 Pod:
[root@master1 ~]#kubectl apply -f pod-prestop.yamlpod/hook-demo2createdpod/hook-demo3created[root@master1 ~]#kubectl get po -owideNAMEREADYSTATUSRESTARTSAGEIPNODENOMINATEDNODEREADINESSGATEShook-demo21/1Running023s10.244.1.7node1<none><none>hook-demo31/1Running023s10.244.2.7node2<none><none>
- 测试
创建完成后,我们可以直接删除 hook-demo2 这个 Pod,在容器删除之前会执行 preStop 里面的优雅关闭命令,这个用法在后面我们的滚动更新的时候用来保证我们的应用零宕机非常有用。
第二个 Pod 我们声明了一个 hostPath 类型的 Volume,在容器里面声明挂载到了这个 Volume,所以当我们删除 Pod,退出容器之前,在容器里面输出的信息也会同样的保存到宿主机(一定要是 Pod 被调度到的目标节点)的 /tmp
目录下面,我们可以查看 hook-demo3 这个 Pod 被调度的节点:
[root@master1 ~]#kubectl describe pod hook-demo3Name:hook-demo3Namespace:defaultPriority:0Node:node2/172.29.9.53
可以看到这个 Pod 被调度到了 node2
这个节点上,我们可以先到该节点上查看 /tmp
目录下面目前没有任何内容:
[root@node2 ~]#ls /tmp/systemd-private-f1bc2b719ba648949a0f4f4f95c61219-chronyd.service-fuobuq vmware-root_6166-1002616931[root@node2 ~]#
现在我们来删除 hook-demo3 这个 Pod,安装我们的设定在容器退出之前会执行 preStop
里面的命令,也就是会往 message 文件中输出一些信息:
[root@master1 ~]#kubectl delete pod hook-demo3pod"hook-demo3"deleted[root@master1 ~]#kubectl get poNAMEREADYSTATUSRESTARTSAGEhook-demo11/1Running022mhook-demo21/1Running03m13sinit-demo1/1Running014h[root@master1 ~]#[root@node2 ~]#ls /tmp/messagesystemd-private-f1bc2b719ba648949a0f4f4f95c61219-chronyd.service-fuobuqvmware-root_6166-1002616931[root@node2 ~]#cat /tmp/messageHellofromthepreStopHandler[root@node2 ~]#
实验结束。😘
另外 Hook 调用的日志没有暴露给 Pod,如果处理程序由于某种原因失败,它将产生一个事件。对于 PostStart,这是FailedPostStartHook 事件,对于 PreStop,是 FailedPreStopHook 事件,比如我们修改下面的 lifecycleevents.yaml 文件,将 postStart 命令更改为 badcommand 并应用它。
vim lifecycle-events.yaml
#lifecycle-events.yamlapiVersion:v1kind:Podmetadata:name:lifecycle-demospec:containers:- name:lifecycle-demo-containerimage:nginxlifecycle:postStart:exec:command:["/bin/sh","-c","echo Hello from the postStart handler >/usr/share/message"]preStop:exec:command:["/usr/sbin/nginx","-s","quit"] # 优雅退出,注意,如果程序有这个优雅退出的命令,应用才可能使用这个preStop hook功能;
应用后我们可以通过运行 kubectl describe pod lifecycle-demo 后来查看一些结果事件的示例输出:
5、Pod 健康检查
Pod对象:重启策略+健康检查(应用自修复)。
健康检查是一种让系统知道应用程序实例是否正常运行的简单方法。如果你的应用程序实例未正常运行,其他服务不会访问它或向它发送请求。相反,会将请求发送到另一个准备就绪的实例,或者你应该重试发送请求。
系统应该能够使你的应用程序处于健康状态。默认情况下,pod内的所有容器都已启动后,Kubernetes会开始向该pod发送流量。容器崩溃时,Kubernetes会重启容器。这个默认行为应该足以开始上手。由于Kubernetes有助于创建自定义健康检查,提高部署稳健性就变得比较简单。
任何分布式系统都需要检查健康,Kubernetes也不例外。使用健康检查为你的Kubernetes服务提供稳固的基础、更高的可靠性和更长的正常运行时间。
在Kubernetes中,探针(Probes)是Kubelet用来定期诊断容器健康状况的工具。
1.探针类型
现在在 Pod 的整个生命周期中,能影响到 Pod 的就只剩下健康检查这一部分了。在 Kubernetes 集群当中,我们可以通过配置 liveness probe(存活探针)、readiness probe(就绪探针) 以及 startupProbe(启动探针) 来影响容器的生命周期。
- Liveness Probe(存活探针):用来确定容器是否正在运行。如果存活探针失败,kubelet会杀死容器,并且容器将根据其重启策略进行重启。---(通俗点将就是是否还活着。)
- Readiness Probe(就绪探针):用来确定容器是否准备好为请求提供服务。如果就绪探针失败,Endpoint控制器将从与Pod关联的所有Service的Endpoints中移除该Pod的IP地址。--(通俗点讲就是说是是否准否准备好了)
- Startup Probe(启动探针):用来确定应用程序在容器内是否已经启动完毕。如果启动探针失败,kubelet会杀死容器,容器将根据其重启策略进行重启。启动探针的主要目的是为了处理启动时间较长的应用程序。当设置了启动探针时,其他探针(存活和就绪探针)将不会开始,直到启动探针成功为止。(如果容器没有提供启动探测,则默认状态为 Success。)(这是Kubernetes 1.16版本引入的特性)
🚩 各探针使用场景
==(1)存活探针 使用场景:==
不妨看另一种场景:你的应用程序因代码错误而崩溃(可能是极端情况),并且无限期挂起,停止为请求提供服务。由于你的进程默认继续运行,Kubernetes会将流量发送到坏掉的pod。使用存活探针,Kubernetes将检测到应用程序不再为请求提供服务,默认情况下重启出故障的pod。
==(2)就绪探针 使用场景:==
不妨以实际场景为例:你的应用程序需要一些时间来预热,或从GitHub等某个外部源下载应用程序内容。除非完全准备好,否则你的应用程序不会接受流量。默认情况下,一旦容器内的进程启动,Kubernetes就会开始发送流量。使用就绪探针,Kubernetes会等到应用程序完全启动后,才允许服务将流量发送到新副本。
请注意,如果你只是想在 Pod 被删除时能够排空请求,则不一定需要使用就绪态探针;在删除 Pod 时,Pod 会自动将自身置于未就绪状态,无论就绪态探针是否存在。 等待 Pod 中的容器停止期间,Pod 会一直处于未就绪状态。
例如,应用程序可能需要在启动期间加载大量数据或配置文件。
==(3)启动探针 使用场景:==
如果你的容器需要在启动期间加载大型数据、配置文件等操作,那么这个时候我们可以使用启动探针。
有时候,会有一些现有的应用在启动时需要较长的初始化时间,前面我们提到了探针里面有一个 initialDelaySeconds的属性,可以来配置第一次执行探针的等待时间,对于启动非常慢的应用这个参数非常有用,比如 Jenkins、 Gitlab这类应用,但是如何设置一个合适的初始延迟时间呢?这个就和应用具体的环境有关系了,所以这个值往往不是通用的。
这样的话可能就会导致一个问题,我们的资源清单在别的环境下可能就会健康检查失败了。
这个时候我们就可以使用startupProbe(启动探针),该探针将推迟所有其他探针,直到 Pod 完成启动为止,使用方法和存活探针一样。技巧就是使用相同的命令来设置启动探测。
针对 HTTP 或 TCP 检测,可以通过将 failureThreshold *periodSeconds
参数设置为足够长的时间来应对糟糕情况下的启动时间。
如果你的容器启动时间通常超出 initialDelaySeconds + failureThreshold × periodSeconds
总值,你应该设置一个启动探针,对存活态探针所使用的同一端点执行检查。 periodSeconds 的默认值是 10 秒,还应该将其failureThreshold 设置得足够高,以便容器有充足的时间完成启动,并且避免更改存活态探针所使用的默认值。 这一设置有助于减少死锁状况的发生。
ports:- name:liveness-portcontainerPort:8080hostPort:8080livenessProbe:httpGet:path:/healthzport:liveness-portfailureThreshold:1periodSeconds:10startupProbe:httpGet:path:/healthzport:liveness-portfailureThreshold:30# 尽量设置大点periodSeconds:10
比如上面这里的配置表示我们的慢速容器最多可以有 5 分钟(30 个检查 *10 秒=300s)来完成启动。
🚩
就绪探针的配置和存活探针的配置相似,唯一区别就是要使用 readinessProbe 字段,而不是 livenessProbe 字段。
就绪探针和存活探针是同步的;
存活探针和就绪探针如果同时使用的话就可以确保流量不会到达还未准备好的容器,准备好过后,如果应用程序出现了错误,则会重新启动容器。
对于一般的应用提供一个健康检查的 URL 会更好。
一般情况下,我们的livenessProbe和readinessProbe都是会去配置;
线上业务,存活探针可以不一定加,但是就绪探针应该尽可能地加上;
为什么呢?这里其实是考虑到线上错误排查的一个问题,如果当我们的应用出现了问题,然后就自动重启去掩盖错误的话,可能这个错误就会被永远忽略掉了,所以其实这是一个折衷的做法。不使用存活性探针,而是结合监控报警,保留错误现场,方便错误排查,但是可读写探针是一定需要添加的:
2.探测方式
probe 是由 kubelet 对容器执行的定期诊断,要执行诊断,kubelet 既可以在容器内执行代码,也可以发出一个网络请求。
使用探针来检查容器有四种不同的方法,每个探针都必须准确定义为这四种机制中的一种:
- exec:在容器内执行指定命令,如果命令退出时返回码为 0, 则认为诊断成功。
- grpc:使用 gRPC 执行一个远程过程调用,目标应该实现 gRPC 健康检查。如果响应的状态是 SERVING,则认为诊断成功。不过需要注意 gRPC 探针是一个 Alpha 特性,只有在启用了 GRPCContainerProbe特性门时才能使用。
- httpGet:对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求,如果响应的状态码大于等于 200 且小于400,则诊断被认为是成功的。
- tcpSocket:使用此配置,kubelet 将尝试在指定端口上打开容器的套接字。如果可以建立连接,容器被认为是健康的,如果不能就认为是失败的。(实际上就是检查端口)
每次探测都将获得以下三种结果之一:
- Success(成功):容器通过了诊断。
- Failure(失败):容器未通过诊断。
- Unknown(未知):诊断失败,因此不会采取任何行动。
==1、命令探针(exec命令)==
• 命令探针(exec命令):在命令探针情况下,Kubernetes将在你的容器内运行命令。如果命令返回退出代码0,容器将被标记为健康。否则,将被标记为不健康。当你不能或不想运行HTTP服务器时,这种类型的探针很有用,但你可以运行命令来检查应用程序是否健康。在下面示例中,我们检查文件/tmp/healthy是否存在;如果命令返回退出代码0,容器将被标记为健康;否则,它将被标记为不健康。
livenessProbe:exec:command:- cat- /tmp/healthy
==2、HTTP探针(httpGet)==
• HTTP探针(httpGet):这是最常见的探针类型。即使你的应用程序不是HTTP服务器,通常也可以在应用程序内创建轻量级 HTTP服务器以响应存活探针。Kubernetes将在特定端口(本例中端口8080)测式ping路径(比如/healthz)。如果它收到200或300范围内的HTTP响应,将被标记为健康。(想进一步了解HTTP响应代码,请参阅该链接https:httpGet:path:/healthzport:8080