kubernetes学习(五)pod生命周期
1、Pod的创建到死亡

从上图开始梳理,从左往右
- pause:
从时间轴看pod的启动第一个容器pause,这个容器是直接创建在节点上的,用kubectl describe无法查询创建过程 。这个容器作用提到过,初始化网络、挂载可能有的存储卷,并且要和其他容器共享network、PID、IPC(进程间通信)和回收僵尸进程,创建结束后就会创建另一个容器类型initC - initC(初始化容器):
从时间轴来看这类容器只出现在我们pod生命周期的前面一部分。在Pod初始化期间完成运行的容器。非必须,看需求添加- initc会按照定义的顺序依次执行。
- 一个initc完成后,下一个initc才会启动。
- 只有所有initc成功完成后,主容器才会启动。阻塞特性
- 利用阻塞特性可以实现类似启动前检测的机制比如,在主容器运行之前,检查外部依赖服务(例如数据库、缓存服务、API 网关等)是否可用。如果依赖服务未就绪,initc会阻塞 pod的启动,直到依赖服务正常运行
- mainC(或叫主容器):
从时间轴来看这类容器出现在我们pod生命周期的后面部分。在Pod初始化结束后运行的容器mainC可以有很多,在创建容器过程中,它们是并发执行的- mainC两个特性:钩子和探测
- 探测;分为启动探测(Startup Probe)、就绪探测(Readiness Probe)、存活探测(Liveness Probe)
- StartupProbe:
确保容器能够成功启动。如果探针检测失败,容器会被杀死并重启,在容器启动前生效 - ReadinessProbe:
确保容器能够接收流量。如果探针检测失败,容器会被标记为未就绪,并从服务的负载均衡中移除,容器启动后立即生效 - LivenessProbe:
确保容器处于“存活”状态。如果探针检测到容器未存活(例如进程卡死或进入死循环),Kubernetes 会重启该容器,容器启动后立即生效
- StartupProbe:
- 探测;分为启动探测(Startup Probe)、就绪探测(Readiness Probe)、存活探测(Liveness Probe)
- 钩子:分为容器启动后钩子(PostStart Hook)和容器停止前钩子(PreStop Hook),统称为生命周期钩子(Lifecycle Hook)
- PostStart Hook:
- 图中mainC有个初始化过程,这是容器启动后执行的操作,也就是分配资源、设置网络、挂载存储等操作
- 在容器ENTRYPOINT执行后立即触发。这是容器初始化结束后,执行的第一条命令,如docker run --name centos centos sleep 3600
- 不等待PostStart完成,容器就算启动。就是说钩子和容器启动是异步执行,容器状态变为Running和Poststart是否完成无关,这是官方设计
-
如果PostStart失败,容器会被杀死。很奇怪是不是?和上面的冲突了。举个栗子:一家餐厅宣布开业(Pod Running状态);同时厨师开始做第一道菜(PostStart);宣布开业不等于第一道菜做完(钩子和容器启动异步执行);菜做的难吃,餐厅关门(杀死容器);菜做好了,财源滚滚。
- PreStop Hook:
- 容器停止前执行的操作
-
在发送TERM信号之前执行
-
必须完成才能继续停止流程
- PostStart Hook:
- mainC两个特性:钩子和探测
以上就是pod启动的过程详解,每个mainC启动都是这些机制。可自定义在资源清单中使用某些机制,除mainC以外,像initC、钩子,探针,按需选择,可全用,部分使用甚至一个不用
2、init容器
针对initc特性官方有举例,这是一个很直观的yaml,就是创建一个mainC,在mainC之前进行一个初始化的操作,已知initC拥有阻塞特性,所以在没达到条件之前mainC是永远不可能启动的,开始实验
apiVersion: v1
kind: Pod
metadata:
name: initc
labels:
app: initC
spec:
containers:
- name: mainc
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/busybox:1.28
command:
- "sh"
- "-c"
- "echo The app is running! && sleep 3600"
initContainers:
- name: initc-1
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/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: initc-2
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/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"
各位同志。busybox有些版本在使用循环时候,即使nolookup无法解析域名,返回值也会是0,导致退出循环,所以做实验的时候可以问问AI那些版本合适,并且往后的实验也不加注释了。各位同志熟练使用AI工具
创建资源对象后,会发现主容器READY(就绪状态)一直无法启动,STATUS状态也显示initC也没有一个启动

查看pod创建过程,可以看到只有init-myservice这个容器被启动,只有init-myservice这个容器顺利结束进程后死亡,init-mydb才会建立。这里就可以体现出initc的阻塞特性了,只有第一个以0的返回码退出后才会继续创建第二个

看下init-myservice日志,myservice这个容器一直在循环解析域名,但是一直解析不到,进入了死循环,导致了容器无法结束进程顺利死亡

创建一个域名(不知道啥意思,还没学到),然后kubectl describe po initc-1看下,可以发现init-mydb容器被创建,再看容器日志可以发现已经跳出了循环
kubectl create svc clusterip myservice --tcp=80:80

但是主容器还是没启动,因为第二个intic还没有结束循环


在创建一个mydb域名, kubectl delete po initc-1查看创建流程,init-mydb容器也已经被创建
kubectl create svc clusterip mydb --tcp=80:80

再看pod容器和日志


ps:关于initc返回码为0的问题,这个还没学到,可以从网上找找案例自己做测试
3、探测
探测是由当前pod所在节点的kubectl对容器执行的定期诊断。要执行诊断,kubectl调用由容器实现的Handler。有三种类型的探测方式
- HTTPGetAction:
对指定的端口和路径上的容器的ip地址执行http get请求。返回状态码在2xx/3xx则诊断成功 - ExecAction:
在容器内执行指定命令。如果命令退出时返回码为0则代表诊断成功 - TCPSocketAction:
对指定端口上的容器的IP地址进行tcp检查。如果端口打开,则诊断成功
在三种探测方式中,HTTP使用最广;EXEC中等;TCP需要结合场景使用,有一定局限性。
3.1就绪探测
readinessProbe 就绪探测
通过添加就绪探测,解决尤其是在扩容时保证提供给用户的服务是可用的,如果pod内部的C不添加就绪探测,默认C处于就绪状态。如果添加了就绪探测,只有就绪探测通过后,才标记修改为就绪状态。当前pod内的所有C都就绪,才标记当前pod就绪。每次探测都将获得以下三种结果之一
- 成功:容器通过了诊断,将当前的C标记为就绪
- 失败:容器未通过诊断,静默,不采取任何行动
- 未知:这种情况比如说一个C在将要返回状态码时死了,探测收不到返回码了。不知所措了,因此会保持静默,不会采取任何行动
就绪探测字段说明,在某些字段不配置的情况下,pod会按照默认数值执行。
- intiaIDelaySeconds:
容器启动后要等待多少秒后探针开始工作,单位“秒”,默认0,最小0 - periodSeconds:
执行探测时间间隔,单位“秒”,默认10,最小1 - timeoutSeconds:
探针执行检测请求后,探测的超时后等待多少秒。默认值是1 。最小值是1 -
successThreshold:
探针在失败后,被视为成功的最小连续成功数。默认值是 1。 -
failureThreshold:
探测失败的重试次数,重试一定次数后将认为失败,默认值3,最小值1
做个示例方便理解。创建两个nginxPod提供给用户访问,初始版修改默认主页内容为1,v1版修改默认主页内容为2。
镜像版本尽量和我保持一致,因为有的版本不支持内部修改。而且k8s也是极度不推荐在容器内部修改配置的,但是因为要做实验嘛,就凑合下
#初始版
apiVersion: v1
kind: Pod
metadata:
name: pod-1
labels:
app: nginx
spec:
containers:
- name: nginx-1
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent
#v1版
apiVersion: v1
kind: Pod
metadata:
name: pod-2
labels:
app: nginx
spec:
containers:
- name: nginx-2
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent
这时候分别访问都是没问题的,并且两个Pod IP也不一样。众所周知nginx要做负载均衡,因此在k8s中提供了更简化的负载均衡方式service(后面会学)。
创建这个service资源时会指定标签选择器(labels)比如标签选择器的值为app=myapp。这时Service会做匹配,选中符合标签的Pod做负载均衡,并赋予这个Service一个IP,这个IP可以理解为当前负载均衡的vip,只要访问这个IP就可以访问服务

访问试一下,可以看到负载均衡成功了的

假设用户太多,负载均衡需要扩容,又用deployment(pod控制器,后面会学)创建了一个nginxPod,这个Pod标签也是app=myapp,这个标签也会被service匹配到。
Service标签匹配要满足两个条件
- 满足子集匹配
- pod必须为就绪状态
关于子集匹配。假设你有一个大的商品清单A和小的商品清单B
- 清单A:此清单包括很多商品如苹果、香蕉、橙子、葡萄、桃子。
- 清单B:只有部分商品如苹果、香蕉。
子集匹配的任务就是:检查清单B的商品,是否都能在清单A中找到
问题来了,如果新pod内nginx还没启动用户就已经连接进来了,不就访问错误了么?这时候我们可以在扩容的nginxPod上,加一个就绪探测。通过探测pod状态,来决定是否将扩容的nginx加入负载均衡集群中
HTTPGetAction
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
namespace: default
labels:。
app: myapp
env: test
spec:
containers:
- name: readiness-httpget-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent # 定义镜像拉取策略:如果本地没有该镜像才拉取。
readinessProbe: # 定义容器的就绪探针,用于检查容器是否可接收流量。
httpGet: # 使用 HTTP GET 方法探测。
port: 80 # 探测的端口号,这里是容器内的80端口。有的是映射在8080
path: /index1.html # 探测的路径,这里是 `/index1.html`。
initialDelaySeconds: 1 # 在容器启动后,延迟 1 秒开始进行探测。
periodSeconds: 3 # 探测的时间间隔,每 3 秒进行一次探测。
启动pod可以看到pod在运行,确认标签是否被service匹配到以及是否能加入到负载均衡集群中

用curl访问service的ip可以看到这个pod并没有加载到集群中,并且一直处于未就绪状态
前面提过Service匹配要满足两个条件:满足子集匹配和容器必须为就绪状态。
用命令看下创建Pod过程中是否记录了未就绪原因,可以发现报错404,找不到index1.html这个主页

就绪探测应该是一个自然而然的状态。比如一个服务启动需要3秒,3秒后启动成功了这就是正常情况。
上面的例子为了方便理解所以写了一个无法恢复的情况。想让探测通过就要手动创建index1.html文件。进入容器内部,到nginx主页下创建一个index1.html文件。退出容器再看pod已经准备就绪了,

测试是否加入了负载均衡集群

出现了1 2 和扩容pod的默认nginx主页,所以只要在创建或扩容新的pod时,添加一个就绪探测,只要能达到就绪状态,就会自动添加到负载均衡集群,这就是真正就绪探测的意义
ExecAction
apiVersion: v1
kind: Pod
metadata:
name: readiness-exec-pod
labels:
env: test
app: nginx
spec:
containers:
- name: readiness-exec-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent
readinessProbe:
exec: # 使用执行命令的方法进行探测。
command: # 探测命令:检查文件是否存在。
- "test"
- "-e"
- "/usr/share/nginx/html/index1.html"
#命令写法有两种,实验中的,另一种是command: [“命令”,“参数”,“执行对象”],每部分需用逗号隔开
initialDelaySeconds: 1
periodSeconds: 3
还有另一种实时观测的办法
kubectl get pod -w #实时查看pod状态
apiVersion: v1
kind: Pod
metadata:
name: readiness-exec2-pod
labels:
env: test
app: nginx
spec:
containers:
- name: readiness-exec-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/busybox:1.28
imagePullPolicy: IfNotPresent
command:
- "/bin/sh"
- "-c"
- | # 用于定义保留换行符的多行字符串。通俗的说,加了这个,下面的命令就可以顺序执行
sleep 10
touch /tmp/test.txt
sleep 3600
readinessProbe:
exec:
command:
- "test"
- "-e"
- "/tmp/test.txt"
initialDelaySeconds: 1
periodSeconds: 3

从上图看执行结果,容器创建后先是延迟1秒进行探测,执行休眠10秒的命令,期间间隔探测也在继续,10秒后创建文件,探测立马成功,Pod状态Running
TCPSocketAction
这种探测方式多用于数据库,消息队列,缓存等服务
apiVersion: v1
kind: Pod
metadata:
name: readiness-tcpsocket
labels:
app: nginx
spec:
containers:
- name: readiness-tcpsocket-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent
command:
- "sh"
- "-c"
- |
sleep 30
nginx -g 'daemon off;' #容器内nginx启动命令,正常启动命令无法容器内使用
readinessProbe:
tcpSocket:
port: 80
periodSeconds: 3 #3秒探测一次
failureThreshold: 10 #探测失败的重试次数,重试10次后将认为失败
#比较简单的测试方法,这个更容易理解
apiVersion: v1
kind: Pod
metadata:
name: readiness-tcpsocket
labels:
app: nginx
spec:
containers:
- name: readiness-tcpsocket-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/busybox:1.28
imagePullPolicy: IfNotPresent
command:
- "sh"
- "-c"
- |
sleep 20
nc -l -p 80 -k -e echo "READY" & #通过nc命令打开80端口持续监听
sleep 3600
readinessProbe:
tcpSocket:
port: 80
periodSeconds: 3 #3秒探测一次
failureThreshold: 10 #探测失败的重试次数,重试10次后将认为失败

看pod创建过程,nginx默认启动命令我放在了休眠后,所以在容器创建时立马就进行了探测,探测3秒一次,看时间8m8s容器创建成功,7m38s到创建成功正好30秒,并且30秒内探测失败了12次。嗯?12次?是不是不太对?根据字段设置重试10次探测就认为失败,为什么会有12次?这是因为k8s设计哲学:容器是可能自我恢复的。
当探测10次后标记容器不健康,但是!探测还是在继续的!举个例子
公司规定:连续3天体温>38° → 回家隔离(failureThreshold: 3)
实际情况:
第1天:38.5° → 记录1
第2天:38.8° → 记录2
第3天:39.0° → ❗达到阈值,回家隔离
但是检测继续:
第4天:还在测体温(万一降到37°就能回来)
第5天:还在测体温
就是这个道理
3.2存活探测
livenessProbe 存活探测
通过添加存活探测,解决虽然活着但是已经死了的问题。如果pod内部不指定存活探测,可能会发生容器运行但是无法提供服务的情况,每次探测都将获得以下三种结果之一
- 成功:静默
- 失败:根据重启的策略进行重启的动作(本质上是重建,谁死了就把它鲨了,创建新的对应容器)
- 未知:静默
存活探测字段说明,在某些字段不配置的情况下,他会按照默认数值执行。
-
intiaIDelaySeconds:容器启动后要等待多少秒后探针开始工作,单位“秒”,默认0,最小0
-
periodSeconds:执行探测时间间隔,单位“秒”,默认10,最小1
-
timeoutSeconds:探针执行检测请求后,探测的超时后等待多少秒。默认值是1 。最小值是1
-
successThreshold:探针在失败后,被视为成功的最小连续成功数。默认值是 1。 存活探测的这个值必须是1。最小值是1(就是说设置探测成功几次才算真正的成功)
-
failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,默认值3,最小值1
HTTPGetAction
apiVersion: v1
kind: Pod
metadata:
name: liveness-http
labels:
app: nginx
spec:
containers:
- name: liveness-http-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
port: 80
path: /index.html
initialDelaySeconds: 1
#通过GET http://<PodIP>:port/index.html(这个在应用层协议(HTTP)的请求头中),探测nginx默认主页返回http状态码。只有2xx/3xx状态码才算成功

kubectl exec -it liveness-httpget-pod -c liveness-httpget-container -- /bin/bash进入容器给主页改个名字,可以看到改完名字后就退出容器了,在我们的理解中,它变成了一种即存在但又无法访问的情况,所以要把这个容器杀死。但是pod重启策略又是Always总是重启,所以会创建新的容器取代它

再次进入容器,看看是否为新的容器,会发现新的index.html主页刚才改名的不存在了

还可以用另一种方式查看是否为重建的新容器
kubectl get po -o wide #找到pod所在节点

docker ps -a |grep <pod-name>

k8s_<container-name>_<pod-name>_<namespaces>_唯一标识符(UUID)_重启次数
探测失败后发现个问题,失败的容器没有被删除,新的就被创建了。经过测试发现探测失败的容器会保留重建容器的前一个,比如有容器0和1,pod新建了容器2,那么失败的1会被保留,0被删除
ExecAction
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
namespace: default
spec:
containers:
- name: liveness-exec-container
image: busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","touch /tmp/live ;sleep 60 ;rm -fr /tmp/live ;sleep 3600"]
livenessProbe:
exec:
command: ["test", "-e", "/tmp/live"]
initialDelaySeconds: 1
periodSeconds: 3
kubectl get po -w监控这个pod的状态,可以看到pod被重启(重建)了两次

为什么会被不停的重建呢?这就要提到探测失败后的重启策略了,先查一下kubectl get po liveness-exec-pod -o yaml(liveness-exec-pod 的 Pod 的详细信息,以 YAML 格式输出),其中有这么一行,这是默认的重启重启策略,Always总是,也有Nerver策略

TCPSocketAction
此探测方式使用场景比较有局限性。使用较少,作为了解即可
因为他的探测就相当于执行了nc -zv 127.0.0.1 80这个操作,只能检查tcp链接是否建立,无法检查服务是否健康。典型的就是web应用,探测到tcp链接,但是返回码500这种情况。所以tcp探测一般作为补充,通常http就能满足我们的绝大部分需求
apiVersion: v1
kind: Pod
medadata:
name: liveness-tcp-pod
spec:
container:
- name: liveness-tcp-container
image: nginx
imagePullPolicy: IfNotPresent
livenessProbe:
initialDelaySeconds: 5
timeoutSeconds: 1
tcpSocket:
port: 80
3.3启动探测
startupProbe 启动探测
k8s在1.16版本以后官方新增功能。保障存活探针在执行的时候,不会因为时间设定问题导致无限死亡或着延迟很长的情况,启动探针专门为启动时间长或初始化过程复杂的容器设计,它的功能是 确保容器完全启动后再开始执行存活和就绪探针。启动探针可以避免容器在启动过程中因延迟或未完成初始化而被过早杀死
每次探测都将获得以下三种结果之一
- 成功:开始允许存活探测或就绪探测开始执行
- 失败:静默
- 未知:静默
以下是启动探测字段说明
-
intiaIDelaySeconds:容器启动后要等待多少秒后探针开始工作,单位“秒”,默认0,最小0
-
periodSeconds:执行探测时间间隔,单位“秒”,默认10,最小1
-
timeoutSeconds:探针执行检测请求后,探测的超时后等待多少秒。默认值是1 。最小值是1
-
successThreshold:探针在失败后,被视为成功的最小连续成功数。默认值是 1。 启动探测的这个值必须是1。最小值是1(就是说设置探测成功几次才算真正的成功)
-
failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,默认值3,最小值1
httpGetAction
apiVersion: v1
kind: Pod
metadata:
name: start-http-pod
labels:
app: myapp
namespace: default
spec:
containers:
- name: start-http-nginx
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent
startupProbe:
httpGet:
path: /index2.html
port: 80
failureThreshold: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /index1.html
port: 80
initialDelaySeconds: 3
periodSeconds: 3
应用程序有最多5分钟failureThreshold*periodSeconds(30*10=300s)的时间来完成其启动过程
启动探测是优先于存活或就绪探测的,同志们可以先创建index1.html观察pod就绪状况,再创建index2.html就可以确定探测顺序了
ExecAction
apiVersion: v1
kind: Pod
metadata:
name: start-exec-pod
labels:
app: myapp
namespace: default
spec:
containers:
- name: start-exec-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent
startupProbe:
exec:
command:
- "sh"
- "-c"
- "[ -f /tmp/ready.txt ]"
initialDelaySeconds: 20
periodSeconds: 3
failureThreshold: 10
我做实验的栗子都是简单的,同志们可以自己加难度,比如检查服务文件啥的
TCPSocketAction
apiVersion: v1
kind: Pod
metadata:
name: start-tcp-pod
labels:
app: myapp
namespace: default
spec:
containers:
- name: start-tcp-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent
startupProbe:
tcpSocket:
port: 80
initialDelaySeconds: 0
periodSeconds: 5
failureThreshold: 60
4、钩子
其实钩子可以做的事情不是很多,按需使用。
钩子的使用场合仅在于pod生命周期的边界,即“容器启动后立即执行和容器停止前立即执行”,通俗来说“启动后钩子”适用于初始化、注册等操作;“停止前钩子”适用于清理、注销等操作。且尽量避免在钩子上执行长时间运行的任务,钩子分为两种:
-
PostStart(启动后钩子):这个回调在容器被创建之后立即被执行。 但是,不能保证回调会在容器入口点(ENTRYPOINT)之前执行。 没有参数传递给处理程序。 -
PreStop(结束前钩子):在容器因 API 请求或者管理事件(诸如存活态探针、启动探针失败、资源抢占、资源竞争等) 而被终止之前,此回调会被调用。 如果容器已经处于已终止或者已完成状态,则对 preStop 回调的调用将失败。 在用来停止容器的 TERM 信号被发出之前,回调必须执行结束。 Pod 的终止宽限周期在PreStop回调被执行之前即开始计数, 所以无论回调函数的执行结果如何,容器最终都会在 Pod 的终止宽限期内被终止。 没有参数会被传递给处理程序
钩子执行方式:exec
apiVersion: v1
kind: Pod
metadata:
name: postart
labels:
lifecycle: hook
namespace: default
spec:
containers:
- name: poststart-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent
lifecycle: # 定义容器生命周期的钩子
postStart: # postStart 钩子:在容器启动后执行的命令
exec:
command: #容器运行后执行的命令
- "sh"
- "-c"
- |
echo poststart > /usr/share/message
preStop: # preStop 钩子:在容器停止前执行的命令
exec:
command: #容器停止前执行的命令
- "sh"
- "-c"
- |
echo prestop > /usr/share/message
创建对象后进入容器内查看文件内容如下,证明了启动后钩子执行没有问题
kubectl exec -it <podName> -c <containerName> -- /bin/bash
#-c是可选选项,pod内只有单一容器的话忽略此选项

现在来验证停止前钩子执行是否可行,在容器内写个死循环不停查看文件
while true ;do cat /usr/share/message ;done


从上图可以观测到容器停止前执行了停止前钩子的命令。
需要注意的是,如果用此方式进行结束语打印的话,pod删除后是不会显示的,结束语指挥打印在容器内部
钩子执行方式:HTTP
随便一个节点前台先启动一个web服务,并创建对象pod是否有发起访问请求

apiVersion: v1
kind: Pod
metadata:
name: lifecycle-http
labels:
app: myapp
spec:
containers:
- name: lifecycle-http-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/busybox:1.28
imagePullPolicy: IfNotPresent
command:
- "sh"
- "-c"
- "sleep 3600"
lifecycle:
postStart:
httpGet:
host: 192.168.100.102
port: 1234
path: /index.html
preStop:
httpGet:
host: 192.168.100.102
port: 1234
path: /index.html
启动pod后再看前台访问记录,会发现有一条访问记录,这是启动后钩子poststart起作用了

紧接着删除pod会发现新增了一条访问记录,这是关闭前钩子起作用了

从实验结果来看两个钩子的执行是没有任何问题的,
钩子的延伸说明
- postStart的成败和容器主进程是并行执行的,postStart 永远不会阻塞容器主进程的启动
-
postStart 永远不阻塞容器主进程的启动,这是设计决定
-
如果 postStart 的操作与主进程无关(如发送通知、记录日志),则完全不影响
-
postStart 是否影响容器正常运行,取决于主进程是否依赖 postStart 的操作结果。如果主进程需要等待 postStart 的结果,则需要额外的协调机制(initContainer、readinessProbe、等待循环),比如
postStart: exec: command: ["sh", "-c", "生成配置文件"] # 如果主进程需要这个配置文件,就会出现问题: containers: - name: app command: ["./app", "--config", "/tmp/config.yml"] # ❌ 可能文件还没生成! - preStop则与postStart完全相反,它是阻塞执行的操作,主要用于实现优雅终止。它的核心作用是在容器被强制终止前,给应用程序一个清理和完成现有工作的机会。如服务停止前持久化数据,释放占用资源等
从生产环境来说,钩子的exec方式更为灵活,是常用手段;http方式局限性很大,用到了在探索也不迟
5、融合所有功能,写一个pod
#创建个域名,为init容器做准备
kubectl create svc clusterip myservice --tcp=80:80
#EXEC版本
apiVersion: v1
kind: Pod
metadata:
name: all
labels:
function: all
namespace: default
spec:
initContainers:
- name: init-all-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/busybox:1.28
imagePullPolicy: IfNotPresent
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"
containers:
- name: main-all-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/busybox:1.28
imagePullPolicy: IfNotPresent
command:
- "sh"
- "-c"
- |
echo The app is running!
sleep 3600
startupProbe:
exec:
command:
- "sh"
- "-c"
- "[ -f /tmp/start.txt ]"
initialDelaySeconds: 3
periodSeconds: 3
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
readinessProbe:
exec:
command:
- "sh"
- "-c"
- "[ -f /tmp/read.txt ]"
initialDelaySeconds: 3
periodSeconds: 3
timeoutSeconds: 3
successThreshold: 3
failureThreshold: 3
livenessProbe:
exec:
command:
- "sh"
- "-c"
- "[ -f /tmp/liven.txt ]"
initialDelaySeconds: 3
periodSeconds: 3
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
lifecycle:
postStart:
exec:
command:
- "sh"
- "-c"
- |
touch /tmp/start.txt
touch /tmp/read.txt
touch /tmp/liven.txt
preStop:
exec:
command:
- "sh"
- "-c"
- "echo bye~"
#注意!钩子的执行过程信息是不会打印出来的!
#另外。这个资源清单逻辑上比较简单,但是有一个小小的不和谐。那就是启动后钩子的命令可以放在容器启动的执行主命令中,并且在主命令中执行一定会在探测之前创建结束,所以生产中EXEC方式要比HTTP方式稍微稳定一些,由此可见钩子的局限性,按需使用!
---------------分割线-------------------
#由于poststart和主服务进程启动顺序未知,http方式钩子实验效果不是很好展示,所以可以在任意节点用docker起一个容器,用钩子进行访问,观察容器日志即可
docker run -it -p 1234:80 --name nginx-container 镜像ID
#HTTP版本
apiVersion: v1
kind: Pod
metadata:
name: all-http
labels:
function: http
spec:
initContainers:
- name: init-all-http-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/busybox:1.28
imagePullPolicy: IfNotPresent
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"
containers:
- name: main-all-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent
command:
- "sh"
- "-c"
- |
touch /usr/share/nginx/html/index1.html
touch /usr/share/nginx/html/index2.html
touch /usr/share/nginx/html/index3.html
nginx -g "daemon off;"
startupProbe:
httpGet:
path: /index1.html
port: 80
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 10
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /index2.html
port: 80
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 10
successThreshold: 3
failureThreshold: 5
livenessProbe:
httpGet:
path: /index3.html
port: 80
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 10
successThreshold: 1
failureThreshold: 5
lifecycle:
postStart:
httpGet:
path: /index.html
port: 1234
host: 192.168.100.102 #如果不指定此字段,默认就是 Pod IP(自己)
preStop:
httpGet:
path: /index.html
port: 1234
host: 192.168.100.102
---------------分割线-------------------
apiVersion: v1
kind: Pod
metadata:
name: test
labels:
test: test
spec:
initContainers:
- name: init-1
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/busybox:1.28
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 10"]
- name: init-2
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/busybox:1.28
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 10"]
containers:
- name: main-container
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:1.27.0
imagePullPolicy: IfNotPresent
lifecycle:
postStart:
exec:
command: ["sh","-c","sleep 20 && touch /tmp/test.txt"] #启动后钩子增加延时,确保不和主容器进程并行执行。像这个操作就是不规范的,避免在生产中出现,因为是做实验所以不讲究
preStop:
exec:
command: ["sh","-c","sleep 30"] #容器停止前会阻塞30秒,才会被杀死
startupProbe:
tcpSocket:
port: 80
initialDelaySeconds: 3
periodSeconds: 3
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
readinessProbe:
exec:
command: ["test","-f","/tmp/test.txt"]
initialDelaySeconds: 3
periodSeconds: 3
timeoutSeconds: 3
successThreshold: 3
failureThreshold: 3
livenessProbe:
httpGet:
path: /index.html
port: 80
6、pod如何被调度运行

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)