目录
1,概述
Pod Phase阶段
2,创建和终止
pod的创建过程
pod的终止过程
3 Init容器
容器探针
容器回调
1,概述
我们一般将pod对象从创建至终止的这段时间范围内称为pod生命周期,它主要包含下面过程:
1.pod创建过程
2.运行初始化容器过程(init container)
3.运行主容器过程(main container)
4.容器启动钩子(post start),容器终止前钩子(pre stop)(这两个钩子的作用就是,如果你想让容器启动之后做一些事情,你可以传递一些参数或者命令给容器启动钩子这个节点。当然如果你想在终止之前执行一些命令,你可以传递一些参数和一些命令给终止前钩子这个节点)
5.容器的存活性探测(liveness probe),就绪性探测(readliness probe)(这两个作用就是子过程)
6.pod终止过程
Pod生命周期过程简单图如下
Pod Phase阶段
Pod 的 status 字段是一个 PodStatus 对象,其中包含一个 phase 字段。Pod 的阶段(Phase)是 Pod 在其生命周期中所处位置的简单宏观概述。
状态(Phase阶段):
Pod 遵循一个预定义的生命周期,起始于 Pending
阶段,如果至少其中有一个主要容器正常启动,则进入 Running
,之后取决于 Pod 中是否有容器以失败状态结束而进入 Succeeded
或者 Failed
阶段。在整个生命周期终,pod会出现5种状态(相位),分别如下:
挂起(Pending
):apiserver已经创建了pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中
运行中(Running
):pod已经被调度至某节点,并且所有容器都已经被kubectkl创建完成
成功(Succeeded
):pod中所有的容器都已经成功终止并且不会被重启
失败(Failed
): 所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态
未知(Unknown): apiserver无法正常获取到pod对象的状态信息,通常由网络通信失败所导致。
Phase
可能的值:
phase 字段 | 描述 |
Pending | Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间 |
Running | Pod 已经绑定到了某个节点,Pod 中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态。 |
Succeeded | Pod 中的所有容器都已成功终止,并且不会再重启。 |
Failed | Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。 |
Unknown | 因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。 |
2,创建和终止
pod的创建过程
1) 用户通过kubectl或其他api客户端提交所需要创建的pod信息给server。
2)apiserver开始生成pod对象的信息,并将信息存入etcd,然后返回确认信息至客户端。
3)apiServer开始反映etcd中的pod对象变化,其它组件使用watch机制来跟踪检查apiServer上的变动。
4)scheduler发现有新的pod对象要创建,开始为pod分配主机并将结果信息更新至apiServer。
5)node节点上的kubectl发现有pod调度过来,尝试调用docker启动容器,并将结果回送至apiServer。
6)apiServer将接收到的pod状态信息存入etcd中。
pod的终止过程
1)用户向apiServer发送删除pod对象的命令
2)apiServer中的pod对象信息会随着时间的推移而更新,在宽限期内(默认30s),pod被视为dead
3)将pod标记为terminating状态
4)kubelet在监控到pod对象转为terminating状态的同时启动pod关闭过程
5)端点控制器监控到pod对象的关闭行为时将其从所有匹配到此端点的service资源的端点列表中移除
6)如果当前pod对象定义了preStop钩子处理器,则在其标记为terminating后即会以同步的方式启动执行
7)pod对象中的容器进程受到停止信号
8)宽限期结束后,若pod中还存在仍在运行的进程,那么pod对象会收到立即终止的信号。
9)kubelet请求apiServer将此pod资源的宽限期设置为0从而完成删除操作,此时pod对于用户已不可见。
3 Init容器
1. init container理解:
Pod能够具有多个容器,应用运行在容器里面,但是也可能有一个或者多个先应用容器启动的Init容器。
Init容器与应用容器很相似,除了以下两点:
- Init容器总是运行到成功完成为止
- 每个Init容器都必须在下一个Init容器启动之前成功完成
如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止。 然而,如果 Pod 对应的参数restartPolicy 值为 “Never”,Kubernetes 不会重新启动 Pod。
2. Init容器示例:
initContainers: 下的配置
apiVersion: v1
kind: Pod
metadata:
name: init-demo
labels:
app: init-demo
spec:
containers:
- name: myapp-container
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox
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
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"]
---
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
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 设置为 false。
如果 Pod 重启,所有 Init 容器必须重新执行。
对 Init 容器规约的修改仅限于容器的 image 字段。 更改 Init 容器的 image 字段,等同于重启该 Pod。
因为 Init 容器可能会被重启、重试或者重新执行,所以 Init 容器的代码应该是幂等的。 特别地,基于 emptyDirs 写文件的代码,应该对输出文件可能已经存在做好准备。
Init 容器具有应用容器的所有字段。然而 Kubernetes 禁止使用 readinessProbe, 因为 Init 容器不能定义不同于完成态(Completion)的就绪态(Readiness)。 Kubernetes 会在校验时强制执行此检查。
在 Pod 上使用 activeDeadlineSeconds 和在容器上使用 livenessProbe 可以避免 Init 容器一直重复失败。activeDeadlineSeconds 时间包含了 Init 容器启动的时间。
在 Pod 中的每个应用容器和 Init 容器的名称必须唯一; 与任何其它容器共享同一个名称,会在校验时抛出错误。
4. Init容器的作用:
因为init容器具有与应用程序分离的单独镜像,所以它们的启动相关代码具有以下优势:
- init容器可以包含运行实用工具,但是处于安全考虑,是不建议在应用程序容器镜像里包含这些工具的, 避免这些工具导致应用镜像的安全性降低 。
- Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。 例如,没有必要仅为了在安装过程中使用类似 sed、awk、python 或 dig 这样的工具而去 FROM 一个镜像来生成一个新的镜像。
- 应用程序镜像可以分离出创建和部署的角色,而没有必要联合它们构建一个单独的镜像
- Init 容器能以不同于 Pod 内应用容器的文件系统视图运行。因此,Init 容器可以访问 应用容器不能访问的 Secret 的权限 。
- 由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器 提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。 一旦前置条件满足,Pod 内的所有的应用容器会并行启动。
容器探针
容器是由 kubelet对容器执行的定期诊断 。 要执行诊断,kubelet 调用由容器实现的 Handler (处理程序)。有三种类型的处理程序:
- ExecAction: 在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
- TCPSocketAction: 对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。
- HTTPGetAction: 对容器的 IP 地址上指定端口和路径执行 HTTP Get 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。
每次探测都将获得以下三种结果之一:
- Success(成功):容器通过了诊断。
- Failure(失败):容器未通过诊断。
- Unknown(未知):诊断失败,因此不会采取任何行动。
针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:
- livenessProbe:指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略restartPolicy 决定未来。如果容器不提供存活探针, 则默认状态为 Success。
- readinessProbe:指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success。
- startupProbe: 指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其 重启策略restartPolicy 进行重启。 如果容器没有提供启动探测,则默认状态为 Success。
liveness probes:存活性探针,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启容器。
readliness probes:就绪性探针,用于检测应用实例当前是否可以接收请求,如果不能,k8s不会转发流量。
livenessProbe 决定是否重启容器,readlinessProbe决定是否将请求转发给容器。
Probe有很多配置字段,可以使用这些字段精确的控制存活和就绪检测的行为:
- initialDelaySeconds:容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0。
- periodSeconds:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。
- timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。
- successThreshold:探测器在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1。
- failureThreshold:当探测失败时,Kubernetes 的重试次数。 存活探测情况下的放弃就意味着重新启动容器。 就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。
重启策略
在上述探测描述中,一旦容器探测出现了问题,kubernetes就会对容器所在的pod进行重启,其实这是由pod的重启策略决定的,pod的重启策略分三种,反别如下:
- Always:容器失效时,自动重启该容器,这也是默认值
- OnFaliure:容器终止运行且退出码不为0时重启
- Never: 不论状态为什么,都重启该容器
下面分别列举ExecAction
、TCPSocketAction
、HTTPGetAction
示例简单应用容器探测功能。
ExecAction
探测示例:
# Exec 方式探针
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
在这个配置文件中,可以看到 Pod 中只有一个容器。 periodSeconds 字段指定了 kubelet 应该每 5 秒执行一次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 5 秒。 kubelet 在容器内执行命令 cat /tmp/healthy 来进行探测。 如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。 如果这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。HTTPGetAction
探测示例:
# HttpGet方式探针
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: lsrong0414/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
在这个配置文件中,可以看到 Pod 也只有一个容器。 periodSeconds 字段指定了 kubelet 每隔 3 秒执行一次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。 kubelet 会向容器内运行的服务(服务会监听 8080 端口)发送一个 HTTP GET 请求来执行探测。 如果服务器上 /healthz 路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。 如果处理程序返回失败代码,则 kubelet 会杀死这个容器并且重新启动它。
任何大于或等于 200 并且小于 400 的返回代码标示成功,其它返回代码都标示失败。
liveness的容器server服务逻辑是最开始 10 秒中,/healthz 处理程序返回一个 200 的状态码。之后处理程序返回 500 的状态码。所以kubelet 在容器启动之后 3 秒开始执行健康检测。所以前几次健康检查都是成功的。 但是 10 秒之后,健康检查会失败,并且 kubelet 会杀死容器再重新启动容器。TCPSocketAction
探测示例:
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: lsrong0414/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
TCP 检测的配置和 HTTP 检测很相似,上面示例中同时使用就绪和存活探测。kubelet 会在容器启动 5 秒后发送第一个就绪探测。 这会尝试连接 goproxy 容器的 8080 端口。 如果探测成功,这个 Pod 会被标记为就绪状态,kubelet 将继续每隔 10 秒运行一次检测。除了就绪探测,这个配置包括了一个存活探测。 kubelet 会在容器启动 15 秒后进行第一次存活探测。 就像就绪探测一样,会尝试连接 goproxy 容器的 8080 端口。 如果存活探测失败,这个容器会被重新启动。
容器回调
kubelet 管理生命周期中的事件触发,运行指定代码,使容器能够了解其管理生命周期中的事件,并在执行相应的生命周期回调时运行在处理程序中实现的代码。
kubernetes有两个回调暴露给容器。
PostStart:这个回调在容器被创建之后立即被执行。 但是,不能保证回调会在容器入口点(ENTRYPOINT)之前执行。 没有参数传递给处理程序。
PreStop:在容器因 API 请求或者管理事件(诸如存活态探针失败、资源抢占、资源竞争等)而被终止之前, 此回调会被调用。 如果容器已经处于终止或者完成状态,则对 preStop 回调的调用将失败。 此调用是阻塞的,也是同步调用,因此必须在发出删除容器的信号之前完成。 没有参数传递给处理程序。
针对容器,有两种类型的回调处理程序可供实现:
- Exec - 在容器的 cgroups 和名称空间中执行特定的命令(例如
pre-stop.sh
)。 命令所消耗的资源计入容器的资源消耗。 - HTTP - 对容器上的特定端点执行 HTTP 请求。
示例:
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]
上述配置文件中,你可以看到 postStart 命令在容器的 /usr/share 目录下写入文件 message。 命令 preStop 负责优雅地终止 nginx 服务。当因为失效而导致容器终止时,这一处理方式很有用。
Kubernetes 在容器创建后立即发送 postStart 事件。 然而,postStart 处理函数的调用不保证早于容器的入口点(entrypoint) 的执行。postStart 处理函数与容器的代码是异步执行的,但 Kubernetes 的容器管理逻辑会一直阻塞等待 postStart 处理函数执行完毕。 只有 postStart 处理函数执行完毕,容器的状态才会变成 RUNNING。
Kubernetes 在容器结束前立即发送 preStop 事件。除非 Pod 宽限期限超时,Kubernetes 的容器管理逻辑 会一直阻塞等待 preStop 处理函数执行完毕。