Kubernetes之(八)Pod的生命周期
理解Pod
Pod是kubernetes中你可以创建和部署的最⼩也是最简的单位。 ⼀个Pod代表着集群中运⾏的⼀个进程。
Pod中封装着应⽤的容器(有的情况下是好⼏个容器) , 存储、 独⽴的⽹络IP, 管理容器如何运⾏的策略选项。 Pod代
表着部署的⼀个单位: kubernetes中应⽤的⼀个实例, 可能由⼀个或者多个容器组合在⼀起共享资源。
在Kubrenetes集群中Pod有如下两种使⽤⽅式:
- ⼀个Pod中运⾏⼀个容器。 “每个Pod中⼀个容器”的模式是最常⻅的⽤法; 在这种使⽤⽅式中, 你可以把Pod想象成是单个容器的封装, kuberentes管理的是Pod⽽不是直接管理容器。
- 在⼀个Pod中同时运⾏多个容器。 ⼀个Pod中也可以同时封装⼏个需要紧密耦合互相协作的容器, 它们之间共享资源。 这些在同⼀个Pod中的容器可以互相协作成为⼀个service单位——⼀个容器共享⽂件, 另⼀个“sidecar”容器来更新这些⽂件。 Pod将这些容器的存储资源作为⼀个实体来管理。
每个Pod都是应⽤的⼀个实例。 如果你想平⾏扩展应⽤的话(运⾏多个实例) , 你应该运⾏多个Pod, 每个Pod都是⼀个应⽤实例。 在Kubernetes中, 这通常被称为replication。
Pod内如何管理多个容器
Pod中可以同时运行多个进程(作为容器运行)协同工作。同一个Pod中的容器会自动的分配到同一个 node 上。同一个Pod中的容器共享资源、网络环境和依赖,它们总是被同时调度。
注意在一个Pod中同时运行多个容器是一种比较高级的用法。只有当你的容器需要紧密配合协作的时候才考虑用这种模式。例如,你有一个容器作为web服务器运行,需要用到共享的volume,有另一个“sidecar”容器来从远端获取资源更新这些文件,如下图所示:
(图片来源于网络)
Pod中可以共享两种资源:网络和存储。
- 网络
每个Pod都会被分配一个唯一的IP地址。Pod中的所有容器共享网络空间,包括IP地址和端口。Pod内部的容器可以使用localhost互相通信。Pod中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)。 - 存储
可以Pod指定多个共享的Volume。Pod中的所有容器都可以访问共享的volume。Volume也可以用来持久化Pod中的存储资源,以防容器重启后文件丢失。
Pod的使用
Pod也可以⽤于垂直应⽤栈(例如LAMP) , 这样使⽤的主要动机是为了⽀持共同调度和协调管理应⽤程序, 例如:
- 内容管理系统、 ⽂件和数据加载器、 本地换群管理器等。
- ⽇志和检查点备份、 压缩、 旋转、 快照等。
- 数据变更观察者、 ⽇志和监控适配器、 活动发布者等。
- 代理、 桥接和适配器等。
- 控制器、 管理器、 配置器、 更新器等。
通常单个pod中不会同时运⾏⼀个应⽤的多个实例。
其他替代选择
为什么不直接在⼀个容器中运⾏多个应⽤程序呢?
1、透明,让Pod中的容器对基础设施可⻅, 以便基础设施能够为这些容器提供服务, 例如进程管理和资源监控。 这可以为⽤户带来极⼤的便利。
2、解耦软件依赖。 每个容器都可以进⾏版本管理, 独⽴的编译和发布。 未来kubernetes甚⾄可能⽀持单个容器的在线升级。
3、使⽤⽅便。 ⽤户不必运⾏⾃⼰的进程管理器, 还要担⼼错误信号传播等。
4、效率。 因为由基础架构提供更多的职责, 所以容器可以变得更加轻量级。
Pod的持久性
Pod在设计⽀持就不是作为持久化实体的。 在调度失败、 节点故障、 缺少资源或者节点维护的状态下都会死掉会被驱逐。
通常, ⽤户不需要⼿动直接创建Pod, ⽽是应该使⽤controller(例如Deployments) , 即使是在创建单个Pod的情况下。 Controller可以提供集群级别的⾃愈功能、 复制和升级管理。
Pod原语有利于:
- 调度程序和控制器可插拔性
- 支持pod级操作,无需通过控尸气API代理他们
- 将pod生命周期与控制器生命周期分离
- 控制器与服务的分离,端点控制器只是监视pod
- 将集群集功能与kubelet及共鞥的清晰组合
- 高可用性应用程序,他们可以在终止前及在删除前更换pod
Pod的终止
因为Pod作为在集群的节点上运⾏的进程, 所以在不再需要的时候能够优雅的终⽌掉是⼗分必要的(⽐起使⽤发送KILL信号这种暴⼒的⽅式) 。 ⽤户需要能够放松删除请求, 并且知道它们何时会被终⽌, 是否被正确的删除。 ⽤户想终⽌程序时发送删除pod的请求, 在pod可以被强制删除前会有⼀个宽限期, 会发送⼀个TERM请求到每个容器的主进程。 ⼀旦超时, 将向主进程发送KILL信号并从API server中删除。 如果kubelet或者container manager在等待进程终⽌的过程中重启, 在重启后仍然会重试完整的宽限期。
示例流程如下:
- 用户发送删除pod的命令,默认宽限期30秒
- 、在Pod超过该宽限期后API server就会更新Pod的状态为dead
- 在客户端命令行上显示的Pod的状态为为terminating;
- 跟上一部同时,当kubelet发现pod被标记为terminating时,开始停止pod进程:
- 如果在pod中定义了preStop hook,在停止pod前会被调用。如果宽限期后 preStop hook仍然在运行,第二部会增加2秒宽限期;
- 向Pod中的进程发送TERM信号
- 跟第三部同时,该Pod将从该service的端点列表中删除,不再是replication controller中的一部分,关闭的慢的pod将继续处理load balancer转发的流量
- 过了宽限期后,将向Pod中易安存在的进程发SIGKILL信号杀掉进程。
- kubelet会在APIserver中完成Pod的删除,通过将优雅周期设置为0(立即删除)Pod在API中小时,并且客户端也不可见。
删除宽限期默认是30秒。 kubectl delete 命令⽀持 —grace-period=
Init容器
Pod能够具有多个容器,应用运行在容器里面,但是它也可能有一个或多个咸鱼应用容器启动的Init容器。
Init容器与普通容器很像,除了一下亮点:
- Init容器总是u以女性到成功完成为止。
- 每个Init容器都必要在下一个Init容器启动之前完成。
如果 Pod 的 Init 容器失败, Kubernetes 会不断地重启该 Pod, 直到 Init 容器成功为⽌。 然⽽, 如果 Pod 对应的restartPolicy 为 Never, 它不会重新启动。
Pause容器
Pause容器,又叫Infra容器。我们检查node节点的时候会发现每个node上都运行了很多的pause容器,例如如下。
[root@node02 ~]# docker ps |grep pause
db1f4da98626 registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 24 hours ago Up 24 hours k8s_POD_myapp-9b4987d5-djdr9_default_995067e0-5124-11e9-80a7-000c295ec349_0
e344da31cee8 registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 24 hours ago Up 24 hours k8s_POD_client-f5cdb799f-pklmc_default_25f4bf9b-5121-11e9-80a7-000c295ec349_0
de5e811fa5fa registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 28 hours ago Up 28 hours k8s_POD_nginx-deploy-84cbfc56b6-tcssz_default_10003a4a-5104-11e9-80a7-000c295ec349_0
3f7d179b79b9 registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 30 hours ago Up 30 hours k8s_POD_kube-flannel-ds-amd64-gwbql_kube-system_0f2de1c5-506c-11e9-80a7-000c295ec349_1
cc07e6411d32 registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 30 hours ago Up 30 hours k8s_POD_kube-proxy-cz2rf_kube-system_1f58e488-5068-11e9-80a7-000c295ec349_1
kubernetes中的pause容器主要为每个业务容器提供以下功能:
- 在pod中担任Linux命名空间共享的基础;
- 启用pid命名空间,开启init进程。
pause容器的作⽤可以从这个例⼦中看出, ⾸先⻅下
图:
(图片来源于网络)
我们⾸先在节点上运⾏⼀个pause容器。
[root@node02 ~]# docker run --name pause -d -p 8880:80 xiaobai20201/pause:3.1
Unable to find image 'xiaobai20201/pause:3.1' locally
3.1: Pulling from xiaobai20201/pause
Digest: sha256:59eec8837a4d942cc19a52b8c09ea75121acc38114a2c68b98983ce9356b8610
Status: Downloaded newer image for xiaobai20201/pause:3.1
d4078dc99bec81167c8d288c2b44485d407780913eee88458986d5bb3750c509
然后再运⾏⼀个nginx容器, nginx将为 localhost:2368 创建⼀个代理。
[root@node02 ~]# vim nginx.conf
error_log stderr;
events { worker_connections 1024; }
http {
access_log /dev/stdout combined;
server {
listen 80 default_server;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:2368;
}
}
}
[root@node02 ~]# docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause nginx
然后为ghost创建一个容器应用,这是一款博客软件。
[root@node02 ~]# docker run -d --name ghost --net=container:pause --ipc=container:pause --pid=container:pause ghost
现在访问http://10.0.0.12:8880/ 就可以看到ghost博客的界面了
解析
pause容器将内部的80端⼝映射到宿主机的8880端⼝, pause容器在宿主机上设置好了⽹络namespace后, nginx容器加⼊到该⽹络namespace中, 我们看到nginx容器启动的时候指定了 --net=container:pause , ghost容器同样加⼊到了该⽹络namespace中, 这样三个容器就共享了⽹络, 互相之间就可以使⽤ localhost 直接通信, --ipc=contianer:pause --pid=container:pause 就是三个容器处于同⼀个namespace中, init进程为 pause , 这时我们进⼊到ghost容器中查看进程情况。
Pod的生命周期
Pod的phase
Pod的status信息保存在PodStatus中定义,有一个phase字段
Pod的相位(phase)是Pod在其生命周期中的简单宏观概述,该阶段并不是对容器或Pod的总和汇总,也不是为了作为综合状态机。
Pod香味的数量和含义是严格指定的。除了文档中列举的状态外,不应该再假定Pod有其他phase值。
下面是phase可能的值:
- 挂起 Pending:Pod已经被Kubernetes系统接受,但是有一个或多个容器镜像尚未创建,等待时间包括调度Pod的时间和通过网络下载镜像的时间。
- 运行中 Running:该Pod已经绑定到一个节点上,Pod中所有的容器都已经被创建,至少有一个容器正在运行或者处于启动或者重启状态。
- 成功 Succeeded:Pod中的所有容器都被成功终止,并且不会在重启。
- 失败 Failed:Pod中的所有容器已经终止,并且至少有一个容器是因为失败终止,也就是说,容器以非0状态退出或者被系统终止。
- 位置 Unknown:因为某些议无法获取Pod的状态,哦那个厂是因为与Pod所在的节点通信失败。
Pod的状态
Pod 有一个 PodStatus 对象,其中包含一个 PodCondition 数组。 PodCondition 数组的每个元素都有一个 type 字段和一个 status 字段。type 字段是字符串,可能的值有 PodScheduled、Ready、Initialized 和 Unschedulable。status 字段是一个字符串,可能的值有 True、False 和 Unknown。
容器探针
在pod生命周期中可以做的一些事情。主容器启动前可以完成初始化容器,初始化容器可以有多个,他们是串行执行的,执行完成后就推出了,在主程序刚刚启动的时候可以指定一个post start 主程序启动开始后执行一些操作,在主程序结束前可以指定一个 pre stop 表示主程序结束前执行的一些操作。在程序启动后可以做两类检测 liveness probe(存活性探测) 和 readness probe(就绪性探测)
探针是由 kubelet 对容器执⾏的定期诊断。 要执⾏诊断, kubelet 调⽤由容器实现的 Handler。 有三种类型的处理程序:
- ExecAction: 在容器内执⾏指定命令。 如果命令退出时返回码为 0 则认为诊断成功。
- TCPSocketAction: 对指定端⼝上的容器的 IP 地址进⾏ TCP 检查。 如果端⼝打开, 则诊断被认为是成功的
- HTTPGetAction: 对指定的端⼝和路径上的容器的 IP 地址执⾏ HTTP Get 请求。 如果响应的状态码⼤于等于200且⼩于 400, 则诊断被认为是成功的。
Kubelet 可以选择是否执行在容器上运行的两种探针执行和做出反应:
- livenessProbe:指示容器是否正在运行。如果存活探测失败,则 kubelet 会杀死容器,并且容器将受到其 重启策略 的影响。如果容器不提供存活探针,则默认状态为 Success。
- readinessProbe:指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址。初始延迟之前的就绪状态默认为 Failure。如果容器不提供就绪探针,则默认状态为 Success。
存活性探测 livenessProbe
解析
[root@master ~]# kubectl explain pods.spec.containers.livenessProbe.
KIND: Pod
VERSION: v1
exec <Object> #命令式探针
failureThreshold <integer> #探测失败次数,超过该次数才算失败,默认是3
periodSeconds <integer> #探测间隔时间,默认10s
successThreshold <integer> #探测成功的最少次数,默认是1,即探测成功1次就算是成功
tcpSocket <Object> #检测端口的探测
initialDelaySeconds #初始化延迟探测,第一次探测的时候,因为主程序未必启动完成
timeoutSeconds <integer> # 探测超时的秒数 默认1s
httpGet <Object> #http请求探测
exec探针举例
[root@master manifests]# vim liveness-exec.yaml
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/healthy;sleep 30;rm -f /tmp/healthy;sleep 600"]
livenessProbe:
exec:
command: ["test","-e","/tmp/healthy"]
initialDelaySecond: 1
periodSecond: 3
#运行并查看pod
[root@master manifests]# kubectl apply -f liveness-exec.yaml
pod/liveness-exec-pod created
[root@master manifests]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 25h
liveness-exec-pod 1/1 Running 0 5s
myapp-9b4987d5-47sjj 1/1 Running 0 25h
myapp-9b4987d5-684q9 1/1 Running 0 25h
myapp-9b4987d5-djdr9 1/1 Running 0 25h
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 29h
pod-demo 2/2 Running 4 4h33m
liveness-exec-pod 1/1 Running 1 87s
#重启次数变成1
上面的资源清单中定义了一个Pod对象,基于busybox镜像启动一个运行“touch /tmp/healthy;sleep 30;rm -f /tmp/healthy;sleep 600”命令的容器,此命令在容器启动时创建/tmp/healthy文件,并于60秒之后将其删除。存活性探针运行“test -e /tmp/healthy”命令检查/tmp/healthy文件的存在性,若文件存在则返回状态码0,表示成功通过测试。
httpGet探测举例
[root@master manifests]# vim liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
namespace: default
spec:
containers:
- name: liveness-httpget-container
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
livenessProbe:
httpGet:
port: http
path: /index.html
#创建Pod并查看
[root@master manifests]# kubectl create -f liveness-httpget.yaml
pod/liveness-httpget-pod created
[root@master manifests]# kubectl get pods
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 26h
liveness-exec-pod 1/1 Running 8 20m
liveness-httpget-pod 1/1 Running 0 8s
myapp-9b4987d5-47sjj 1/1 Running 0 25h
myapp-9b4987d5-684q9 1/1 Running 0 25h
myapp-9b4987d5-djdr9 1/1 Running 1 25h
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 29h
pod-demo 2/2 Running 4 4h53m
#手动进入容器删除index.html文件,再监控pod状态
[root@master manifests]# kubectl exec -it liveness-httpget-pod -- /bin/sh
/ # rm -f /usr/share/nginx/html/index.html
#查看pod
^C[root@master manifests]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 26h
liveness-exec-pod 0/1 CrashLoopBackOff 9 24m
liveness-httpget-pod 1/1 Running 1 3m41s
myapp-9b4987d5-47sjj 1/1 Running 0 25h
myapp-9b4987d5-684q9 1/1 Running 0 25h
myapp-9b4987d5-djdr9 1/1 Running 1 25h
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 29h
pod-demo 2/2 Running 4 4h57m
#发现liveness-httpget-pod已经重启了一次
TCP探测举例
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness-tcp
name: liveness-tcp
spec:
containers:
- name: liveness-tcp-demo
image: nginx:1.12-alpine
ports:
- name: http
containerPort: 80
livenessProbe:
tcpSocket:
port: http
就绪性探测 readnessProbe
[root@master manifests]# vim readness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
namespace: default
spec:
containers:
- name: readiness-httpget-container
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
readinessProbe:
httpGet:
port: http
path: /index.html
initialDelaySecond: 1
periodSeconds: 3
此时pod运行正常,进入容器删除首页文件后观察pod状态
[root@master manifests]# kubectl exec -it readiness-httpget-pod -- /bin/sh
/ # rm -f /usr/share/nginx/html/index.html
[root@master manifests]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 26h
myapp-9b4987d5-47sjj 1/1 Running 0 26h
myapp-9b4987d5-684q9 1/1 Running 0 26h
myapp-9b4987d5-djdr9 1/1 Running 1 26h
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 30h
pod-demo 2/2 Running 5 5h27m
readiness-httpget-pod 0/1 Running 0 115s
重写写入index.html文件后继续观察pod
/ # echo ad >/usr/share/nginx/html/index.html
^C[root@master manifests]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 26h
myapp-9b4987d5-47sjj 1/1 Running 0 26h
myapp-9b4987d5-684q9 1/1 Running 0 26h
myapp-9b4987d5-djdr9 1/1 Running 1 26h
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 30h
pod-demo 2/2 Running 5 5h29m
readiness-httpget-pod 1/1 Running 0 3m54s
livenessProbe和readinessProbe使用场景
如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活探针; kubelet 将根据 Pod 的restartPolicy 自动执行正确的操作。
如果希望容器在探测失败时被杀死并重新启动,那么请指定一个存活探针,并指定restartPolicy 为 Always 或 OnFailure。
如果要仅在探测成功时才开始向 Pod 发送流量,请指定就绪探针。在这种情况下,就绪探针可能与存活探针相同,但是 spec 中的就绪探针的存在意味着 Pod 将在没有接收到任何流量的情况下启动,并且只有在探针探测成功后才开始接收流量。
如果您希望容器能够自行维护,您可以指定一个就绪探针,该探针检查与存活探针不同的端点。
请注意,如果您只想在 Pod 被删除时能够排除请求,则不一定需要使用就绪探针;在删除 Pod 时,Pod 会自动将自身置于未完成状态,无论就绪探针是否存在。当等待 Pod 中的容器停止时,Pod 仍处于未完成状态。
lifecycle
定义容器启动后和终止前立即执行的动作
解析
[root@master ~]# kubectl explain pods.spec.containers.lifecycle.
postStart <Object> #启动后
- exec
- httpGet
- tcpSocket
preStop <Object> #终止前
- exec
- httpGet
- tcpSocket
postStart举例
[root@master manifests]# vim poststart-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: poststart-pod
namespace: default
spec:
containers:
- name: busybox-httpd
image: busybox
imagePullPolicy: IfNotPresent
lifecycle:
postStart:
exec:
command: ["mkdir","-p","/tmp/share"]
command: ["/bin/sh","-c","sleep 3600"]
#进入容器查看是否有/tmp/share目录
[root@master manifests]# kubectl exec -it poststart-pod -- /bin/sh
/ # ls -l /tmp
total 0
drwxr-xr-x 2 root root 6 Mar 29 09:19 share
参考资料
https://www.cnblogs.com/linuxk
马永亮. Kubernetes进阶实战 (云计算与虚拟化技术丛书)
Kubernetes-handbook-jimmysong-20181218