pod的生命周期
pod在整个生命周期的过程中的几个状态:
Pending:创建了pod资源并存入etcd中,但尚未完成调度。
ContainerCreating:Pod 的调度完成,被分配到指定 Node 上。处于容器创建的过程中。通常是在拉取镜像的过程中。
Running:Pod 包含的所有容器都已经成功创建,并且成功运行起来。
Succeeded:Pod中的所有容器都已经成功终止并且不会被重启
Failed:所有容器都已经终止,但至少有一个容器终止失败,也就是说容器返回了非0值的退出状态或已经被系统终止。
Unknown:因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。
pod生命周期的重要行为
pause容器:在启动任何容器之前,先创建pause基础容器,它初始化Pod的环境并为后续加⼊的容器提供共享的名称空间。
初始化容器:一个pod可以拥有任意数量的init容器。init容器是按照顺序以此执行的,并且仅当最后一个init容器执行完毕才会去启动主容器。
生命周期钩子:pod允许定义两种类型的生命周期钩子,启动后(post-start)钩子和停止前(pre-stop)钩子
这些生命周期钩子是基于每个容器来指定的,和init容器不同的是,init容器是应用到整个pod。而这些钩子是针对容器的,是在容器启动后和停止前执行的。
容器探测:
- Startup(启动探测):探测容器是否正常运行
- Liveness(存活探测):判断容器是否处于runnning状态,根据重启策略决定是否重启容器
- Readiness(就绪检测):判断容器是否准备就绪并对外提供服务,将容器设置为不可用,不接受
pod启动流程:
pause container (基础容器)——> initcontainer(初始化容器) ——> containers (主容器)——> post-start(容器启动后钩子)——>StartupProbe(启动探测)——>LivenessProbe(存活探测)——>ReadinessProbe(就绪探测)——>Pre-stop(容器结束前钩子)
pod终止流程:
(1)用户发出删除 pod 命令:kubectl delete pods ,kubectl delete -f yaml
(2)Pod 对象随着时间的推移更新,在宽限期(默认情况下30秒),pod 被视为“dead”状态
(3)将 pod 标记为“Terminating”状态
(4)第三步同时运行,监控到 pod 对象为“Terminating”状态的同时启动 pod 关闭过程
(5)第三步同时进行,endpoints 控制器监控到 pod 对象关闭,将pod与service匹配的 endpoints 列表中删除
(6)如果 pod 中定义了 preStop 钩子处理程序,则 pod 被标记为“Terminating”状态时以同步的方式启动执行;若宽限期结束后,preStop 仍未执行结束,第二步会重新执行并额外获得一个2秒的小宽限期
(7)Pod 内对象的容器收到 TERM 信号
(8)宽限期结束之后,若存在任何一个运行的进程,pod 会收到 SIGKILL 信号
(9)Kubelet 请求 API Server 将此 Pod 资源宽限期设置为0从而完成删除操作
定义初始化容器
初始化容器会在主容器之前执行,与主容器共享存储卷。
初始化容器与主容器区别:
- 初始化容器不支持Readinessprobe,运维他们必须在Pod就绪之前运行完成
- 每个初始化容器必须运行成功,下一个才能够运行(串行执行)
# 如果pod初始化容器失败,kubernetes会根据重启策略决定是否重启初始化容器,不断地重启该pod,知道初始化容器成功为止,然而如果Pod对于的restartPolicy值为Never,就不会重新启动。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
initContainers:
- name: init-myservice
image: 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"]
# 第一个initcontainer没有运行完,不会运行此initcontainer
- name: init-mydb
image: busybox:1.28
imagePullPolicy: IfNotPresent
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"]
# 更加不会运行container里面的容器
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
apiVersion: v1
kind: Pod
metadata:
name: initnginx
spec:
initContainers:
- name: install
image: docker.io/library/busybox:1.28
imagePullPolicy: IfNotPresent
# 下载百度页面到/work-dir目录
command:
- wget
- "-O"
- "/work-dir/index.html"
- "https://www.baidu.com"
# 挂载workdir卷到/work-dir
volumeMounts:
- name: workdir
mountPath: /work-dir
containers:
- name: nginx
image: docker.io/xianchao/nginx:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
# 挂载workerdir物理卷到容器的/usr/share/nginx/html,该卷和initcontainer挂载的是同一个卷
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
dnsPolicy: Default
# 临时创建一个workdir的卷
volumes:
- name: workdir
emptyDir: {}
定义钩子
postStart:容器创建之后立刻执行,用于资源部署、环境准备等。
preStop:在容器被终止前执行,用于优雅关闭应用程序、通知其他系统等
### 用法
......
containers:
- image: sample:v2
name: war
# 定义生命周期
lifecycle:
# 在容器创建成功后,复制/sample.war到/app文件夹中
postStart:
exec:
command:
- “cp”
- “/sample.war”
- “/app”
prestop:
# 容器结束前请求接口,把请求发送给监控系统
httpGet:
host: monitor.com
path: /waring
port: 8080
scheme: HTTP
......
优雅删除资源对象
#(默认)K8S通知node执行docker stop命令,docker会先向容器中PID为1的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认超时时间(30s),会继续发送SIGKILL的系统信号强行kill掉进程。
#使用pod生命周期(利用PreStop回调函数),它执行在发送终止信号之前。
#默认情况下,所有的删除操作的优雅退出时间都在30秒以内。kubectl delete命令支持--grace-period=的选项,以运行用户来修改默认值。0表示删除立即执行,并且立即从API中删除pod。在节点上,被设置了立即结束的的pod,仍然会给一个很短的优雅退出时间段,才会开始被强制杀死。
apiVersion: v1
kind: Pod
metadata:
name: life-demo
spec:
containers:
- name: lifecycle-demo-container
image: docker.io/xianchao/nginx:v1
imagePullPolicy: IfNotPresent
lifecycle:
# 启动容器后执行此命令,重定向此文件
postStart:
exec:
command: ["/bin/sh", "-c","echo 'lifecycle hookshandler' > /usr/share/nginx/html/test.html"]
# 结束容器前执行此命令,提前关闭nginx服务(可以在删除pod时正常停止nginx服务)
preStop:
exec:
command:
- "/bin/sh"
- "-c"
- "nginx -s stop"
探测Probe
#startupProbe:探测容器中的应用是否已经启动。如果提供了启动探测(startup probe),则禁用所有其他探测,直到它成功为止。如果启动探测失败,kubelet 将杀死容器,容器服从其重启策略进行重启。如果容器没有提供启动探测,则默认状态为成功Success。
### livenessprobe:用指定的方式(**exec、tcp、http**)检测pod中的容器是否正常运行,如果检测失败,则认为容器不健康,那么Kubelet将根据Pod中设置的 restartPolicy策略来判断Pod 是否要进行重启操作,如果容器配置中没有配置 livenessProbe,Kubelet 将认为存活探针探测一直为success(成功)状态。
### readnessprobe:就绪性探测,用于检测容器中的应用是否可以接受请求,当探测成功后才使Pod对外提供网络访问,将容器标记为就绪状态,可以加到pod前端负载,如果探测失败,则将容器标记为未就绪状态,会把pod从前端负载移除
# 常见探测模式(所有的探测都支持下面三种探针):
- exec:在容器中执行一个命令,并根据返回的状态码进行诊断**,只有返回0为成功。
- cpSocket:通过与容器的某TCP端口尝试建立连接,**如果能够建立TCP连接,说明容器健康。
- httpGet:通过向容器IP地址的某指定端口的path发起HTTP GET请求。**如果响应的状态码大于等于200且小于400,则认为容器健康。
startupProbe 启动探测
apiVersion: v1
kind: Pod
metadata:
name: check
namespace: default
labels:
app: check
spec:
containers:
- name: check
image: busybox:1.28
imagePullPolicy: IfNotPresent
# 容器启动后,运行10秒退出,改变容器状态
# 这时pod根据启动探测发现pod的服务异常,会自动重启服务
command:
- /bin/sh
- -c
- sleep 10;exit
# 在上面的例子中, 容器进程返回值非零, Kubernetes则认为容器发生故障, 需要重启。 有不少情况是发生了故障, 但进程并不会退出。 比如访问Web服务器时显示500内部错误, 可能是系统超载, 也可能是资源死锁, 此时httpd进程并没有异常退出, 在这种情况下重启容器可能是最直接、 最有效的解决方案。
# Pod探针相关的属性:
# 探针(Probe)有许多可选字段,可以用来更加精确的控制Liveness和Readiness两种探针的行为
initialDelaySeconds:容器启动后要等待多少秒后探针开始工作,单位“秒”,默认是 0 秒,最小值是 0
periodSeconds: 执行探测的时间间隔(单位是秒),默认为 10s,单位“秒”,最小值是1
timeoutSeconds: 探针执行检测请求后,等待响应的超时时间,默认为1,单位“秒”。
successThreshold:连续探测几次成功,才认为探测成功,默认为 1,在 Liveness 探针中必须为1,最小值为1。
failureThreshold: 探测失败的重试次数,重试一定次数后将认为失败,在 readiness 探针中,Pod会被标记为未就绪,默认为 3,最小值为 1
# ExecAction
apiVersion: v1
kind: Pod
metadata:
name: startupprobe
spec:
containers:
- name: startup
image: xianchao/tomcat-8.5-jre8:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
# 启动探测
startupProbe:
exec:
command:
- "/bin/sh"
- "-c"
# 该指令会报错,就会触发exec探测,重启pod,重启次数多了就会变成clashbackoff状态
- "aa ps aux | grep tomcat"
initialDelaySeconds: 20 #容器启动后多久开始探测
periodSeconds: 20 #执行探测的时间间隔
timeoutSeconds: 10 #探针执行检测请求后,等待响应的超时时间
successThreshold: 1 #成功多少次才算成功
failureThreshold: 3 #失败多少次才算失败
# tcpSocket
apiVersion: v1
kind: Pod
metadata:
name: startupprobe
spec:
containers:
- name: startup
image: xianchao/tomcat-8.5-jre8:v1
imagePullPolicy: IfNotPresent
ports:
# 声明容器端口为8080
- containerPort: 8080
# 启动探测
startupProbe:
# 探测8080端口是否存在,存在则成功
tcpSocket:
port: 8080
initialDelaySeconds: 20 #容器启动后多久开始探测
periodSeconds: 20 #执行探测的时间间隔
timeoutSeconds: 10 #探针执行检测请求后,等待响应的超时时间
successThreshold: 1 #成功多少次才算成功
failureThreshold: 3 #失败多少次才算失败
apiVersion: v1
kind: Pod
metadata:
name: startupprobe
spec:
containers:
- name: startup
image: xianchao/tomcat-8.5-jre8:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
# 启动探测
startupProbe:
# 探测http服务是否正常,正常则成功
httpGet:
path: /
port: 8080
initialDelaySeconds: 20 #容器启动后多久开始探测
periodSeconds: 20 #执行探测的时间间隔
timeoutSeconds: 10 #探针执行检测请求后,等待响应的超时时间
successThreshold: 1 #成功多少次才算成功
failureThreshold: 3 #失败多少次才算失败
livenessprobe 存活探测
### 容器在初始化后,首先创建一个 /tmp/healthy 文件,然后执行睡眠命令,睡眠 30 秒,到时间后执行删除 /tmp/healthy 文件命令。而设置的存活探针检检测方式为执行 shell 命令,用 cat 命令输出 healthy 文件的内容,如果能成功执行这条命令,存活探针就认为探测成功,否则探测失败。在前 30 秒内,由于文件存在,所以存活探针探测时执行 cat /tmp/healthy 命令成功执行。30 秒后 healthy 文件被删除,所以执行命令失败,Kubernetes 会根据 Pod 设置的重启策略来判断,是否重启 Pod。
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec
labels:
app: liveness
spec:
containers:
- name: liveness
image: busybox:1.28
imagePullPolicy: IfNotPresent
#创建测试探针探测的文件
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
# 存活探测
livenessProbe:
initialDelaySeconds: 10 #延迟检测时间
periodSeconds: 5 #检测时间间隔
# 使用exec探针,如果cat /tmp/healthy返回错误请求,则说明异常。连续两次都成功,说明正常
exec:
command:
- cat
- /tmp/healthy
### 上面 Pod 中启动的容器是一个 SpringBoot 应用,其中引用了 Actuator 组件,提供了 /actuator/health 健康检查地址,存活探针可以使用 HTTPGet 方式向服务发起请求,请求 8081 端口的 /actuator/health 路径来进行存活判断:任何大于或等于200且小于400的代码表示探测成功。任何其他代码表示失败。
apiVersion: v1
kind: Pod
metadata:
name: liveness-http
labels:
test: liveness
spec:
containers:
- name: liveness
image: mydlqclub/springboot-helloworld:0.0.1
imagePullPolicy: IfNotPresent
livenessProbe:
initialDelaySeconds: 20 #延迟加载时间
periodSeconds: 5 #重试时间间隔
timeoutSeconds: 10 #超时时间设置
httpGet:
scheme: HTTP
port: 8081
path: /actuator/health
### httpGet相关字段
- scheme: 用于连接host的协议,默认为HTTP。
- host:要连接的主机名,默认为Pod IP,可以在http request head中设置host头部。
- port:容器上要访问端口号或名称。
- path:http服务器上的访问URI。
- httpHeaders:自定义HTTP请求headers,HTTP允许重复headers。
# TCP 检查方式和 HTTP 检查方式非常相似,在容器启动 initialDelaySeconds 参数设定的时间后,kubelet 将发送第一个 livenessProbe 探针,尝试连接容器的 80 端口,如果连接失败则将杀死 Pod 重启容器。
apiVersion: v1
kind: Pod
metadata:
name: liveness-tcp
labels:
app: liveness
spec:
containers:
- name: liveness
image: docker.io/xianchao/nginx:v1
imagePullPolicy: IfNotPresent
livenessProbe:
initialDelaySeconds: 15
periodSeconds: 20
tcpSocket:
port: 80
readnessprobe
Pod 的ReadinessProbe 探针使用方式和 LivenessProbe 探针探测方法一样,也是支持三种,只是一个是用于探测应用的存活,一个是判断是否对外提供流量的条件。
# 容器启动正常后,启动service服务并访问服务看能否正常,能否正常提供流量等
apiVersion: v1
kind: Service
metadata:
name: springboot
labels:
app: springboot
spec:
type: NodePort
ports:
- name: server
port: 8080
targetPort: 8080
nodePort: 31180
- name: management
port: 8081
targetPort: 8081
nodePort: 31181
selector:
app: springboot
---
apiVersion: v1
kind: Pod
metadata:
name: springboot
labels:
app: springboot
spec:
containers:
- name: springboot
image: mydlqclub/springboot-helloworld:0.0.1
imagePullPolicy: IfNotPresent
ports:
- name: server
containerPort: 8080
- name: management
containerPort: 8081
readinessProbe:
initialDelaySeconds: 20
periodSeconds: 5
timeoutSeconds: 10
httpGet:
scheme: HTTP
port: 8081
path: /actuator/health
几种探测混合使用
# 先执行startupPorbe,成功后再同步并行livenessProbe和readinessProbe
apiVersion: v1
kind: Service
metadata:
name: springboot-live
labels:
app: springboot
spec:
type: NodePort
ports:
- name: server
port: 8080
targetPort: 8080
nodePort: 31180
- name: management
port: 8081
targetPort: 8081
nodePort: 31181
selector:
app: springboot
---
apiVersion: v1
kind: Pod
metadata:
name: springboot-live
labels:
app: springboot
spec:
containers:
- name: springboot
image: mydlqclub/springboot-helloworld:0.0.1
imagePullPolicy: IfNotPresent
ports:
- name: server
containerPort: 8080
- name: management
containerPort: 8081
# 探测服务是否能提供对应的功能,成功则加到service资源
readinessProbe:
initialDelaySeconds: 20
periodSeconds: 5
timeoutSeconds: 10
httpGet:
scheme: HTTP
port: 8081
path: /actuator/health
# 探测服务是否存活
# 如果在容器内kill掉进程,就会触发
livenessProbe:
initialDelaySeconds: 20
periodSeconds: 5
timeoutSeconds: 10
httpGet:
scheme: HTTP
port: 8081
path: /actuator/health
# 探测服务是否存在
# 探测服务是否返回正常的状态码
startupProbe:
initialDelaySeconds: 20
periodSeconds: 5
timeoutSeconds: 10
httpGet:
scheme: HTTP
port: 8081
path: /actuator/health
总结:
pod在整个生命周期中有非常多的用户行为:
**1、**初始化容器完成初始化
**2、**主容器启动后可以做启动后钩子主容器结束前可以做结束前钩子
**3、**在主容器运行中可以做一些健康检测,如startupprobe、livenessprobe,readnessprobe。livenessprobe和readnessprobe没有先后顺序之分。