Pod 调度流程、状态、与容器探针
Pod调度流程
master 节点:kubectl -> kube-api -> kubelet -> CRI 容器环境初始化
第一步:
客户端提交创建 Pod 的请求,可以通过调用 API Server 的 Rest API 接口,也可以通过 kubectl 命令行
工具。如 kubectl apply -f filename.yaml(资源清单文件)
第二步:
apiserver 接收到 pod 创建请求后,会将 yaml 中的属性信息(metadata)写入 etcd。
第三步:
apiserver 触发 watch 机制准备创建 pod,信息转发给调度器 scheduler,调度器使用调度算法选择
node,调度器将 node 信息给 apiserver,apiserver 将绑定的 node 信息写入 etcd
调度器用一组规则过滤掉不符合要求的主机。比如 Pod 指定了所需要的资源量,那么可用资源比 Pod 需要的资源量少的主机会被过滤掉。
scheduler 查看 k8s api ,类似于通知机制。
首先判断:pod.spec.Node == null?
若为 null,表示这个 Pod 请求是新来的,需要创建;因此先进行调度计算,找到最“闲”的 node。
然后将信息在 etcd 数据库中更新分配结果:pod.spec.Node = nodeA (设置一个具体的节点)
ps:同样上述操作的各种信息也要写到 etcd 数据库中中
第四步:
apiserver 又通过 watch 机制,调用 kubelet,指定 pod 信息,调用 Docker API 创建并启动 pod 内的容器。
第五步:
创建完成之后反馈给 kubelet, kubelet 又将 pod 的状态信息给
Pod 常见的状态和重启策略
常见的 pod 状态
Pod 的 status 定义在 PodStatus 对象中,其中有一个 phase 字段。它简单描述了 Pod 在其生命周
期的阶段。熟悉 Pod 的各种状态对我们理解如何设置 Pod 的调度策略、重启策略是很有必要的。
下面是 phase 可能的值,也就是 pod 常见的状态:
挂起(Pending):我们在请求创建 pod 时,条件不满足,调度没有完成,没有任何一个节点能满
足调度条件,已经创建了 pod 但是没有适合它运行的节点叫做挂起,调度没有完成,处于 pending
的状态会持续一段时间:包括调度 Pod 的时间和通过网络下载镜像的时间。
运行中(Running):Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容
器正在运行,或者正处于启动或重启状态。
成功(Succeeded):Pod 中的所有容器都被成功终止,并且不会再重启。
失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是
说,容器以非 0 状态退出或者被系统终止。
未知(Unknown):未知状态,所谓 pod 是什么状态是 apiserver 和运行在 pod 节点的 kubelet 进
行通信获取状态信息的,如果节点之上的 kubelet 本身出故障,那么 apiserver 就连不上
kubelet,得不到信息了,就会看 Unknown
扩展:还有其他状态,如下:
Evicted 状态:出现这种情况,多见于系统内存或硬盘资源不足,可 df-h 查看 docker 存储所在目
录的资源使用情况,如果百分比大于 85%,就要及时清理下资源,尤其是一些大文件、docker 镜
像。
CrashLoopBackOff:容器曾经启动了,但可能又异常退出了
Error 状态:Pod 启动过程中发生了错误
pod 重启策略 Pod 的重启策略(RestartPolicy)应用于 Pod 内的所有容器,并且仅在 Pod 所处的 Node 上由
kubelet 进行判断和重启操作。当某个容器异常退出或者健康检查失败时,kubelet 将根据
RestartPolicy 的设置来进行相应的操作。
Pod 的重启策略包括
Always、OnFailure 和 Never,默认值为 Always。 Always:当容器失败时,由 kubelet 自动重启该容器。
OnFailure:当容器终止运行且退出码不为 0 时,由 kubelet 自动重启该容
镜像拉取策略
lfNotPresent #node节点没有此镜像就去指定的镜像仓库拉取,node有就使用node本地镜像。
Always #每次重建pod都会重新拉取镜像
Never #从不到镜像中心拉取镜像,只使用本地镜像
Pod生命周期
init容器
init容器的特点:
1.一个pod可以有多个业务容器还能在有多个init容器,但是每个init容器和业务容器的运行环境都是隔离的。
2.init容器会比业务容器先启动。
3.init容器运行成功之后才会继续运行业务容器
4.如果一个pod有多个init容器,则需要从上到下逐个运行并且全部成功,最后才会运行业务容器。
5.init容器不支持探针检测(因为初始化完成后就退出再也不运行了)
6.Init 容器不支持 Readiness,因为它们必须在 Pod 就绪之前运行完成
7、每个 Init 容器必须运行成功,下一个才能够运行
8、如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止,然 而,如果 Pod 对应的 restartPolicy 值为 Never,它不会重新启动
[root@xksmaster1 Others]# 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@xksmaster1 Others]# kubectl apply -f init.yaml pod/myapp-pod created
#此时 service还有没有创建 所以pod一直init状态 等待service创建好 容器变为running状态 [root@xksmaster1 Others]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-pod 0/1 Init:0/2 0 9s nfs-provisioner-68d8f76665-cqftz 1/1 Running 0 4h19m pod-secret-volume 1/1 Running 0 26h web-0 1/1 Running 0 4h14m web-1 1/1 Running 0 4h11m
vim service.yaml [root@xksmaster1 Others]# 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@xksmaster1 Others]# kubectl apply -f service.yaml service/myservice created service/mydb created [root@xksmaster1 Others]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-pod 0/1 Init:0/2 0 98s nfs-provisioner-68d8f76665-cqftz 1/1 Running 0 4h21m pod-secret-volume 1/1 Running 0 26h web-0 1/1 Running 0 4h15m web-1 1/1 Running 0 4h12m [root@xksmaster1 Others]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-pod 0/1 Init:1/2 0 99s nfs-provisioner-68d8f76665-cqftz 1/1 Running 0 4h21m pod-secret-volume 1/1 Running 0 26h web-0 1/1 Running 0 4h15m web-1 1/1 Running 0 4h12m [root@xksmaster1 Others]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-pod 0/1 PodInitializing 0 100s nfs-provisioner-68d8f76665-cqftz 1/1 Running 0 4h21m pod-secret-volume 1/1 Running 0 26h web-0 1/1 Running 0 4h15m web-1 1/1 Running 0 4h12m [root@xksmaster1 Others]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-pod 1/1 Running 0 102s nfs-provisioner-68d8f76665-cqftz 1/1 Running 0 4h21m pod-secret-volume 1/1 Running 0 26h web-0 1/1 Running 0 4h15m web-1 1/1 Running 0 4h12m
污点和容忍度
pause容器
探针
两种探针区别: ReadinessProbe 和 livenessProbe 可以使用相同探测方式,只是对 Pod 的处置方式不同:
readinessProbe 当检测失败后,将 Pod 的 IP:Port 从对应的 EndPoint 列表中删除。
livenessProbe 当检测失败后,将杀死容器并根据 Pod 的重启策略来决定作出对应的措施。
LivenessProbe 探针使用示例 (1)、通过 exec 方式做健康探测
apiVersion: v1 kind: Pod metadata: name: liveness-exec labels: app: liveness spec: containers: - name: liveness image: busybox args: #创建测试探针探测的文件 - /bin/sh - -c - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600 livenessProbe: initialDelaySeconds: 10 #延迟检测时间 periodSeconds: 5 #检测时间间隔 exec: command: - cat - /tmp/healthy
容器启动设置执行的命令:
/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"
容器在初始化后,首先创建一个 /tmp/healthy 文件,然后执行睡眠命令,睡眠 30 秒,到
时间后执行删除 /tmp/healthy 文件命令。而设置的存活探针检检测方式为执行 shell 命令,用
cat 命令输出 healthy 文件的内容,如果能成功执行这条命令,存活探针就认为探测成功,否则
探测失败。在前 30 秒内,由于文件存在,所以存活探针探测时执行 cat /tmp/healthy 命令成
功执行。30 秒后 healthy 文件被删除,所以执行命令失败,Kubernetes 会根据 Pod 设置的重
启策略来判断,是否重启 Pod。
(2)、通过 HTTP 方式做健康探测 示例文件 liveness-http.yaml
[root@xksmaster1 Others]# cat liveness-http.yaml apiVersion: v1 kind: Pod metadata: name: liveness-http labels: test: liveness spec: containers: - name: liveness image: mydlqclub/springboot-helloworld:0.0.1 livenessProbe: initialDelaySeconds: 20 #延迟加载时间 periodSeconds: 5 #重试时间间隔 timeoutSeconds: 10 #超时时间设置 httpGet: scheme: HTTP port: 8081 path: /actuator/health
上面 Pod 中启动的容器是一个 SpringBoot 应用,其中引用了 Actuator 组件,提供了
/actuator/health 健康检查地址,存活探针可以使用 HTTPGet 方式向服务发起请求,请求 8081
端口的 /actuator/health 路径来进行存活判断:
任何大于或等于 200 且小于 400 的代码表示探测成功。
任何其他代码表示失败。
如果探测失败,则会杀死 Pod 进行重启操作。
httpGet 探测方式有如下可选的控制字段:
scheme: 用于连接 host 的协议,默认为 HTTP。
host:要连接的主机名,默认为 Pod IP,可以在 http request head 中设置 host 头部。
port:容器上要访问端口号或名称。
path:http 服务器上的访问 URI。
httpHeaders:自定义 HTTP 请求 headers,HTTP 允许重复 headers。
(3)、通过 TCP 方式做健康探测
[root@xksmaster1 Others]# cat liveness-tcp.yaml apiVersion: v1 kind: Pod metadata: name: liveness-tcp labels: app: liveness spec: containers: - name: liveness image: nginx livenessProbe: initialDelaySeconds: 15 periodSeconds: 20 tcpSocket: port: 80
TCP 检查方式和 HTTP 检查方式非常相似,在容器启动 initialDelaySeconds 参数设定的时间
后,kubelet 将发送第一个 livenessProbe 探针,尝试连接容器的 80 端口,如果连接失败则将
杀死 Pod 重启容器。
ReadinessProbe 探针使用示例
[root@xksmaster1 Others]# cat readiness-exec.yaml 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
Pod 的 ReadinessProbe 探针使用方式和 LivenessProbe 探针探测方法一样,也是支持三种,只
是一个是用于探测应用的存活,一个是判断是否对外提供流量的条件。这里用一个 Springboot
项目,设置 ReadinessProbe 探测 SpringBoot 项目的 8081 端口下的 /actuator/health 接
口,如果探测成功则代表内部程序以及启动,就开放对外提供接口访问,否则内部应用没有成功
启动,暂不对外提供访问,直到就绪探针探测成功。
ReadinessProbe + LivenessProbe 配合使用示例
readinessProbe: initialDelaySeconds: 20 periodSeconds: 5 timeoutSeconds: 10 httpGet: scheme: HTTP port: 8081 path: /actuator/health livenessProbe: initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 httpGet: scheme: HTTP port: 8081 path: /actuator/health
Pod的终止流程
1创建pod完成调度流程
容器启动并执行postStart
实况调查
进入running状态
readinessProbe
服务关联pod
接受客尸端请求
2.删除pod
Pod被设置为”Terminating”状态、从service的Endpoints列表中删除并不再接受客户端请求
执行PreStop
Kubernetes向pod中的容器发送SIGTERM信号(正常终止信号)终止pod里面的主进程,这个信号让容器知道自己很快将会被关闭
terminationGracePeriodSeconds:60 #可选终止等待期,如果有设置删除宽限时间,则等待宽限时间到期,否则最多等待30s.
Kubernees等待指定的时间称为优雅终止宽限期,默认情况下是30秒,值得注意的是等待期与DreStop Hook和SIGTERM信号并行执行,
Kubernetes可能不会等待preStopHook完成(最长30秒之后主进程还没有结束就就强制终止pod)。
SIGKILL信号被发送到Pod,并删除Pod