Pod生命周期中的重要行为
- Init容器与普通的容器区别是:
- 1、Init 容器不支持 Readiness,因为它们必须在Pod就绪之前运行完成
- 2、每个Init容器必须运行成功,下一个才能够运行
- 3、如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止,然而,如果Pod对应的restartPolicy值为 Never,它不会重新启动。
Pod 里面可以有一个或者多个容器,部署应用的容器可以称为主容器,在创建Pod时候,Pod 中可以有一个或多个先于主容器启动的Init容器,这个init容器就可以成为初始化容器,初始化容器一旦执行完,它从启动开始到初始化代码执行完就退出了,它不会一直存在,所以在主容器启动之前执行初始化,初始化容器可以有多个,多个初始化容器是要串行执行的,先执行初始化容器1,在执行初始化容器2等,等初始化容器执行完初始化就退出了,然后再执行主容器,主容器一退出,pod就结束了,主容器退出的时间点就是pod的结束点,它俩时间轴是一致的;
Init容器就是做初始化工作的容器。可以有一个或多个,如果多个按照定义的顺序依次执行,只有所有的初始化容器执行完后,主容器才启动。由于一个Pod里的存储卷是共享的,所以Init Container里产生的数据可以被主容器使用到,Init Container可以在多种K8S资源里被使用到,如Deployment、DaemonSet, StatefulSet、Job等,但都是在Pod启动时,在主容器启动前执行,做初始化工作。
初始化容器的官方地址:
https://kubernetes.io/docs/concepts/workloads/pods/init-containers/#init-containers-in-use
[root@xianchaomaster1 init]# cat init.yaml apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: 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"]
[root@xianchaomaster1 init]# kubectl apply -f init.yaml [root@xianchaomaster1 init]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-pod 0/1 Init:0/2 0 2m29s
[root@xianchaomaster1 init]# cat service.yaml --- apiVersion: v1 kind: Service metadata: name: myservice spec: ports: - protocol: TCP port: 80 targetPort: 9376 --- apiVersion: v1 kind: Service metadata: name: mydb spec: ports: - protocol: TCP port: 80 targetPort: 9377
[root@xianchaomaster1 init]# kubectl apply -f service.yaml [root@xianchaomaster1 init]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-pod 1/1 Running 0 3m36s
2)容器钩子
初始化容器启动之后,开始启动主容器,在主容器启动之前有一个post start hook(容器启动后钩子)和pre stop hook(容器结束前钩子),无论启动后还是结束前所做的事我们可以把它放两个钩子,这个钩子就表示用户可以用它来钩住一些命令,来执行它,做开场前的预设,结束前的清理,如awk有begin,end,和这个效果类似;
postStart:该钩子在容器被创建后立刻触发,通知容器它已经被创建。如果该钩子对应的hook handler执行失败,则该容器会被杀死,并根据该容器的重启策略决定是否要重启该容器,这个钩子不需要传递任何参数。
preStop:该钩子在容器被删除前触发,其所对应的hook handler必须在删除该容器的请求发送给Docker daemon之前完成。在该钩子对应的hook handler完成后不论执行的结果如何,Docker daemon会发送一个SGTERN信号量给Docker daemon来删除该容器,这个钩子不需要传递任何参数。
containers: - image: sample:v2 name: war lifecycle: postStart: exec: command: - “cp” - “/sample.war” - “/app” prestop: httpGet: host: monitor.com path: /waring port: 8080 scheme: HTTP
以上示例中,定义了一个Pod,包含一个JAVA的web应用容器,其中设置了PostStart和PreStop回调函数。即在容器创建成功后,复制/sample.war到/app文件夹中。而在容器终止之前,发送HTTP请求到http://monitor.com:8080/waring,即向监控系统发送警告。
优雅的删除资源对象preStop
当用户请求删除含有pod的资源对象时(如RC、deployment等),K8S为了让应用程序优雅关闭(即让应用程序完成正在处理的请求后,再关闭软件),K8S提供两种信息通知:
1)、默认:K8S通知node执行docker stop命令,docker会先向容器中PID为1的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认超时时间(30s),会继续发送SIGKILL的系统信号强行kill掉进程。
2)、使用pod生命周期(利用PreStop回调函数),它执行在发送终止信号之前。
默认情况下,所有的删除操作的优雅退出时间都在30秒以内。kubectl delete命令支持--grace-period=的选项,以运行用户来修改默认值。0表示删除立即执行,并且立即从API中删除pod。在节点上,被设置了立即结束的的pod,仍然会给一个很短的优雅退出时间段,才会开始被强制杀死。如下:
spec: containers: - name: nginx-demo image: centos:nginx lifecycle: preStop: exec: # nginx -s quit gracefully terminate while SIGTERM triggers a quick exit command: ["/usr/local/nginx/sbin/nginx","-s","quit"] ports: - name: http containerPort: 80
3)容器探测
容器探测(container probe)是Pod对象生命周期中的一项重要的日常任务,它是kubelet对容器周期性执行的健康状态诊断,诊断操作由容器的处理器(handler)进行定义。Kubernetes支持三种处理器用于Pod探测:
- ExecAction:在容器内执行指定命令,并根据其返回的状态码进行诊断的操作称为Exec探测,状态码为0表示成功,否则即为不健康状态。【执行命令】
- TCPSocketAction:通过与容器的某TCP端口尝试建立连接进行诊断,端口能够成功打开即为正常,否则为不健康状态。
- HTTPGetAction:通过向容器IP地址的某指定端口的指定path发起HTTP GET请求进行诊断,响应码为2xx或3xx时即为成功,否则为失败。
任何一种探测方式都可能存在三种结果:“Success”(成功)、“Failure”(失败)、“Unknown”(未知),只有success表示成功通过检测。
容器探测分为两种类型:
- 存活性探测(livenessProbe):用于判定容器是否处于“运行”(Running)状态;一旦此类检测未通过,kubelet将杀死容器并根据重启策略(restartPolicy)决定是否将其重启;未定义存活检测的容器的默认状态为“Success”。
- 就绪性探测(readinessProbe):用于判断容器是否准备就绪并可对外提供服务;未通过检测的容器意味着其尚未准备就绪,端点控制器(如Service对象)会将其IP从所有匹配到此Pod对象的Service对象的端点列表中移除;检测通过之后,会再将其IP添加至端点列表中。
- 启动探测startupProbe: 探测容器中的应用是否已经启动。如果提供了启动探测(startup probe),则禁用所有其他探测,直到它成功为止。如果启动探测失败,kubelet 将杀死容器,容器服从其重启策略进行重启。如果容器没有提供启动探测,则默认状态为成功Success。
可以自定义在pod启动时是否执行这些检测,如果不设置,则检测结果均默认为通过,如果设置,则顺序为startupProbe>readinessProbe=livenessProbe。
真正的启动顺序
https://github.com/kubernetes/kubernetes/issues/60647
也就是 Liveness probes并不会等到Readiness probes成功之后才运行
根据上面的官方文档,Liveness 和 readiness 应该是某种并发的关系
什么时候使用存活(liveness)和就绪(readiness)探针?
如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活探针,kubelet将根据Pod的restartPolicy自动执行正确的操作。
如果希望容器在探测失败时被杀死并重新启动,那么请指定一个存活探针,并指定restartPolicy为Always或OnFailure。
如果要仅在探测成功时才开始向Pod发送流量,请指定就绪探针。在这种情况下,就绪探针可能与存活探针相同,但是spec中的就绪探针的存在意味着Pod将在没有接收到任何流量的情况下启动,并且只有在探针探测成功才开始接收流量。
如果希望容器能够自行维护,可以指定一个就绪探针,该探针检查与存活探针不同的端点。
注意:如果只想在Pod被删除时能够排除请求,则不一定需要使用就绪探针;在删除Pod时,Pod会自动将自身置于未完成状态,无论就绪探针是否存在。当等待Pod中的容器停止时,Pod仍处于未完成状态。
探针探测结果有以下值:
1、Success:表示通过检测。
2、Failure:表示未通过检测。
3、Unknown:表示检测没有正常进行。
容器重启的策略
PodSpec中有一个restartPolicy字段,可能的值为Always、OnFailure和Never。默认为Always。restartPolicy适用于Pod中的所有容器。而且它仅用于控制在同一节点上重新启动Pod对象的相关容器。首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长依次为10秒、20秒、40秒... 300秒是最大延迟时长。事实上,一旦绑定到一个节点,Pod对象将永远不会被重新绑定到另一个节点,它要么被重启,要么终止,直到节点发生故障或被删除。
- Always:但凡Pod对象终止就将其重启,默认值
- OnFailure:仅在Pod对象出现错误时方才将其重启
- Never:从不重启
Always:守护进程式,要求持续运行,例如nginx,mysql,redis
(Never,OnFailure):预期终止,批处理,定时任务,执行完就结束
liveness存活检查exec示例:
apiVersion: v1 kind: Pod metadata: name: liveness-exec namespace: default labels: test: kk spec: containers: - name: liveness-exec image: nginx:1.13 imagePullPolicy: IfNotPresent command: ["/bin/sh","-c","touch xx;sleep 10;rm -rf xx;sleep 10"] livenessProbe: exec: command: ["test","-e","xx"] initialDelaySecon
上面的资源清单中定义了一个pod对象,基于nginx镜像启动一个运行“touch xx sleep 10; rm -rf xx; sleep 10"命令的容器,此命令在容器启动时创建了/tmp/healthy文件,并于10秒之后将其删除。存活性探针运行”test -e xx"命令检查xx文件的存在性,若文件存在则返回状态码0,表示成功通过测试。在10秒内使用“kubectl describe pods/liveness-exec-pod”查看其详细信息,其存活性探测不会出现错误。而超过10秒之后,再执行该命令查看详细信息,可以发现存活性探测出现了故障,并且还可通过“kubectl get pods"查看该pod的重启的相关信息。
liveness存活检查httpGet示例:
基于HTTP的探测(HTTPGetAction)向目标容器发起一个HTTP请求,根据其响应码进行结果判定,响应码如2xx或3xx时表示测试通过。通过该命令”# kubectl explainpod.spec.containers.livenessProbe.httpGet“查看httpGet定义的字段
host <string>:请求的主机地址,默认为Pod IP,也可以在httpHeaders中使用“Host:”来定义。
httpHeaders <[]Object>:自定义的请求报文首部
port <string>:请求的端口,必选字段。
path <string>:请求的HTTP资源路径,即URL path。
scheme <string>:建立连接使用的协议,仅可为HTTP或HTTPS,默认为HTTP。
[root@master01 ~]# vim liveness-http.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-http
namespace: default
labels:
test: liveness-http
spec:
containers:
- name: liveness-http-container
image: nginx:1.13
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
lifecycle: #生命周期
postStart:
exec:
command: ["/bin/sh","-c","echo kk > /usr/share/nginx/html/kk"]
livenessProbe:
httpGet:
path: /kk
port: http
scheme: HTTP #方式
[root@master01 ~]# kubectl exec -it -n default liveness-http -c liveness-http-container -- "/bin/bash" root@liveness-http:/# cd /usr/share/nginx/html/ root@liveness-http:/usr/share/nginx/html# ls 50x.html index.html kk root@liveness-http:/usr/share/nginx/html# rm -rf kk root@liveness-http:/usr/share/nginx/html# ls 50x.html index.html
liveness存活检查TCP示例
[root@master01 ~]# cat liveness-tcp.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-tcp
namespace: default
labels:
test: liveness-tcp
spec:
containers:
- name: liveness-tcp-container
image: nginx:1.13
imagePullPolicy: IfNotPresent
ports:
- name: tcp
containerPort: 80
livenessProbe:
tcpSocket:
port: tcp
[root@master01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness-http 1/1 Running 1 41m
liveness-tcp 1/1 Running 0 2m27s
nginx-x 1/1 Running 0 27h
pod-demo 1/1 Running 1 27h
[root@k8s-master ~]# kubectl explain pods.spec.containers.livenessProbe KIND: Pod VERSION: v1 RESOURCE: livenessProbe <Object> exec command 的方式探测,例如 ps 一个进程是否存在 failureThreshold 探测几次失败才算失败,默认是连续三次 initialDelaySeconds 初始化延迟探测,即容器启动多久之后再开始探测,默认为0秒 periodSeconds 每隔多久探测一次,默认是10秒 successThreshold 处于失败状态时,探测操作至少连续多少次的成功才算通过检测,默认为1秒 timeoutSeconds 存活性探测的超时时长,默认为1秒 httpGet http请求探测 tcpSocket 端口探测
Pod就绪性探测示例
Pod对象启动后,容器应用通常需要一段时间才能完成其初始化过程,例如加载配置或数据,甚至有些程序需要运行某类的预热过程,若在这个阶段完成之前即接入客户端的请求,势必会等待太久。因此,这时候就用到了就绪性探测(readinessProbe)。
- 成功了就添加svc,不行就创建
readinessProbe设置HTTP探针示例
[root@master01 ~]# vim readiness-http.yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-http
namespace: default
labels:
test: rediness-http
spec:
containers:
- name: readiness-http-demo
image: nginx:1.13
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
readinessProbe:
httpGet:
path: /index.html
port: http
scheme: HTTP
进行测试查看现象
[root@master01 ~]# kubectl get pods NAME READY STATUS RESTARTS AGE liveness-http 1/1 Running 1 84m liveness-tcp 1/1 Running 0 45m nginx-x 1/1 Running 0 28h pod-demo 1/1 Running 1 28h readiness-http 1/1 Running 0 14m [root@master01 ~]# kubectl exec -it -n default readiness-http -c readiness-http-demo -- "/bin/bash" root@readiness-http:/# cd /usr/share/nginx/html/ root@readiness-http:/usr/share/nginx/html# ls 50x.html index.html root@readiness-http:/usr/share/nginx/html# rm -rf index.html root@readiness-http:/usr/share/nginx/html# ls 50x.html root@readiness-http:/usr/share/nginx/html# exit [root@master01 ~]# kubectl get pods NAME READY STATUS RESTARTS AGE liveness-http 1/1 Running 1 86m liveness-tcp 1/1 Running 0 47m nginx-x 1/1 Running 0 28h pod-demo 1/1 Running 1 28h readiness-http 0/1 Running 0 17m
通过上面测试可以看出,当我们删除了nginx主页文件后,readinessProbe发起的测试就会失败,此时我们再查看pod的状态会发现并不会将pod删除重新启动,只是在READY字段可以看出,当前的Pod处于未就绪状态。
readinessProbe设置exec探针示例
[root@master01 ~]# vim readiness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-exec
namespace: default
labels:
test: rediness-exec
spec:
containers:
- name: readiness-exec-demo
image: nginx:1.13
imagePullPolicy: IfNotPresent
ports:
- name: exec
containerPort: 80
args:
- /bin/sh
- -c
- touch /tmp/healthy;sleep 30;rm -rf /tmp/healthy;sleep 600
readinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
pod启动,创建健康检查文件,这个时候是正常的,30s后删除,ready变成0,但pod没有被删除或者重启
[root@master01 ~]# kubectl get pods NAME READY STATUS RESTARTS AGE liveness-http 1/1 Running 1 139m liveness-tcp 1/1 Running 0 100m nginx-x 1/1 Running 0 29h pod-demo 1/1 Running 1 29h readiness-exec 0/1 Running 0 5m13s readiness-http 0/1 Running 0 69m
[root@master01 ~]# kubectl get pods NAME READY STATUS RESTARTS AGE liveness-http 1/1 Running 1 143m liveness-tcp 1/1 Running 0 104m nginx-x 1/1 Running 0 29h pod-demo 1/1 Running 1 29h readiness-exec 0/1 Running 0 9m7s readiness-http 0/1 Running 0 73m [root@master01 ~]# kubectl exec -it -n default readiness-exec -c readiness-exec-demo -- "/bin/bash" root@readiness-exec:/# cd /tmp root@readiness-exec:/tmp# touch healthy root@readiness-exec:/tmp# ls healthy root@readiness-exec:/tmp# exit [root@master01 ~]# kubectl get pods NAME READY STATUS RESTARTS AGE liveness-http 1/1 Running 1 144m liveness-tcp 1/1 Running 0 105m nginx-x 1/1 Running 0 29h pod-demo 1/1 Running 1 29h readiness-exec 1/1 Running 0 10m readiness-http 0/1 Running 0 74m
readinessProbe设置tcp探针示例
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: default labels: app: nginx spec: selector: matchLabels: app: nginx replicas: 1 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent ports: - containerPort: 80 readinessProbe: tcpSocket: port: 80 initialDelaySeconds: 5 periodSeconds: 3 readinessProbe: tcpSocket: port: 80 initialDelaySeconds: 5 periodSeconds: 3
Pod启动探测startupProbe
为什么要用startupProbe?,
在k8s中,通过控制器管理pod,如果更新pod的时候,会创建新的pod,删除老的pod,但是如果新的pod创建了,pod里的容器还没完成初始化,老的pod就被删除了,会导致访问service或者ingress时候,访问到的pod是有问题的,所以k8s就加入了一些存活性探针:livenessProbe、就绪性探针readinessProbe以及这节课要介绍的启动探针startupProbe。
注意:不要将startupProbe和readinessProbe混淆,区别就是startupProbe探测没有问题的话,启动容器不添加svc,readinessProbe探测成功后会加入svc
什么时候会用startupProbe呢?
正常情况下,我们会在pod template中配置livenessProbe来探测容器是否正常运行,如果异常则会触发restartPolicy重启容器(因为默认情况下restartPolicy设置的是always)。
livenessProbe: httpGet: path: /test prot: 80 failureThreshold: 1 initialDelay:10 periodSeconds: 10
上面配置的意思是容器启动10s后每10s检查一次,允许失败的次数是1次。如果失败次数超过1则会触发restartPolicy。
但是有时候会存在特殊情况,比如服务A启动时间很慢,需要60s。这个时候如果还是用上面的探针就会进入死循环,因为上面的探针10s后就开始探测,这时候我们服务并没有起来,发现探测失败就会触发restartPolicy。这时候有的朋友可能会想到把initialDelay调成60s不就可以了?但是我们并不能保证这个服务每次起来都是60s,假如新的版本起来要70s,甚至更多的时间,我们就不好控制了。有的朋友可能还会想到把失败次数增加,比如下面配置:
livenessProbe: httpGet: path: /test prot: 80 failureThreshold: 5 initialDelay:60 periodSeconds: 10
这在启动的时候是可以解决我们目前的问题,但是如果这个服务挂了呢?如果failureThreshold=1则10s后就会报警通知服务挂了,如果设置了failureThreshold=5,那么就需要5*10s=50s的时间,在现在大家追求快速发现、快速定位、快速响应的时代是不被允许的。
在这时候我们把startupProbe和livenessProbe结合起来使用就可以很大程度上解决我们的问题。
livenessProbe: httpGet: path: /test prot: 80 failureThreshold: 1 initialDelay:10 periodSeconds: 10 startupProbe: httpGet: path: /test prot: 80 failureThreshold: 10 initialDelay:10 periodSeconds: 10
K8s的LivenessProbe 和ReadinessProbe的启动顺序问题
LivenessProbe会导致pod重启,ReadinessProbe只是不提供服务
我们最初的理解是LivenessProbe会在ReadinessProbe成功后开始检查,但事实并非如此。
kubelet 使用存活探测器来知道什么时候要重启容器。 例如,存活探测器可以捕捉到死锁(应用程序在运行,但是无法继续执行后面的步骤)。 这样的情况下重启容器有助于让应用程序在有问题的情况下可用。
kubelet 使用就绪探测器可以知道容器什么时候准备好了并可以开始接受请求流量, 当一个 Pod 内的所有容器都准备好了,才能把这个 Pod 看作就绪了。 这种信号的一个用途就是控制哪个 Pod 作为 Service 的后端。 在 Pod 还没有准备好的时候,会从 Service 的负载均衡器中被剔除的。
kubelet 使用启动探测器(startupProbe)可以知道应用程序容器什么时候启动了。 如果配置了这类探测器,就可以控制容器在启动成功后再进行存活性和就绪检查, 确保这些存活、就绪探测器不会影响应用程序的启动。 这可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。

评论