Pod 生命周期
- Pod 生命周期
- 概述
- 生命周期阶段
- Pause容器
- Pause的功能
- Init 容器
- Init容器特点
- Init 注意事项
- Init 容器能做什么
- 生命周期行为
- 初始化容器
- 容器探测
- Pod检测方式
- 重启策略
- Pod的终止过程
- 健康检查
- 为什么需要健康检查
- 检查策略
- 存活探测
- 就绪探测
- 如何配置
- 使用场景
- 默认的健康检查
- 创建资源清单
- 创建容器
- 探针类型
- exec存活探针
- HTTP就绪探针
- TCP探针
Pod 生命周期
概述
Pod对象自动其创建开始直至终止退出的时间范围称其生命周期
创建主容器为其必须的操作,初始化容器(init containter)、启动后钩子post start hook、存活性探测、就绪性探测、pre stop hook为可选执行。
生命周期阶段
Pod总会处于以下几个相位(phase)之一:
Pause容器
每个Pod里运行着一个特殊的被称之为Pause的容器,其他容器则为业务容器,这些业务容器共享Pause容器的网络栈和Volume挂载卷,因此他们之间通信和数据交换更为高效。在设计时可以充分利用这一特性,将一组密切相关的服务进程放入同一个Pod中;同一个Pod里的容器之间仅需通过localhost就能互相通信。
Pause的功能
kubernetes中的pause容器主要为每个业务容器提供以下功能:
- PID命名空间:Pod中的不同应用程序可以看到其他应用程序的进程ID。
- 网络命名空间:Pod中的多个容器能够访问同一个IP和端口范围。
- IPC命名空间:Pod中的多个容器能够使用System V IPC或POSIX消息队列进行通信。
- UTS命名空间:Pod中的多个容器共享一个主机名;Volumes(共享存储卷)。
- Pod中的各个容器可以访问在Pod级别定义的Volumes。
Init 容器
Pod可以包含多个容器,应用运行在这些容器里面,同时 Pod 也可以有一个或多个先于应用容器启动的 Init 容器。
如果为一个 Pod 指定了多个 Init 容器,这些Init容器会按顺序逐个运行。每个 Init 容器都必须运行成功,下一个才能够运行。当所有的 Init 容器运行完成时,Kubernetes 才会为 Pod 初始化应用容器并像平常一样运行。
Init容器特点
Init容器与普通的容器非常像,除了以下两点:
- Init容器总是运行到成功完成且正常退出为止
- 只有前一个Init容器成功完成并正常退出,才能运行下一个Init容器。
Init 注意事项
如果Pod的Init容器失败,Kubernetes会不断地重启Pod,直到Init容器成功为止。但如果Pod对应的restartPolicy为Never,则不会重新启动。
在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态。 Init 容器的端口将不会在 Service 中进行聚集。 正在初始化中的 Pod 处于 Pending 状态,但会将条件 Initializing 设置为 true。
如果 Pod 重启,所有 Init 容器必须重新执行。
在 Pod 中的每个应用容器和 Init 容器的名称必须唯一;与任何其它容器共享同一个名称,会在校验时抛出错误
Init 容器能做什么
因为 Init 容器是与应用容器分离的单独镜像,其启动相关代码具有如下优势:
- Init 容器可以包含一些安装过程中应用容器不存在的实用工具或个性化代码。例如,在安装过程中要使用类似 sed、 awk、 python或 dig这样的工具,那么放到Init容器去安装这些工具;再例如,应用容器需要一些必要的目录或者配置文件甚至涉及敏感信息,那么放到Init容器去执行。而不是在主容器执行。
- Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低。
- 应用镜像的创建者和部署者可以各自独立工作,而没有必要联合构建一个单独的应用镜像。
- Init 容器能以不同于Pod内应用容器的文件系统视图运行。因此,Init容器可具有访问 Secrets 的权限,而应用容器不能够访问。
- 由于 Init 容器必须在应用容器启动之前运行完成,因此 Init容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。一旦前置条件满足,Pod内的所有的应用容器会并行启动。
生命周期行为
Pod 生命周期中的重要行为:
初始化容器
初始化容器(init container)即应用程序的主容器启动之前要运行的容器,常用于为主容器执行一些预置操作,它们具有两种典型特征。
- 初始化容器必须运行完成直至结束,若某初始化容器运行失败,那么kubernetes需要重启它直到成功完成。(注意:如果pod的spec.restartPolicy字段值为“Never”,那么运行失败的初始化容器不会被重启。)
- 初始化容器要先于应用容器串行启动并运行完成,因此可用于延后应用容器启动直至其依赖的条件得到满足
容器探测
它是kubectl对容器周期性执行的健康状态诊断
- Liveness(存活性探测):判断容器是否处于runnning状态,策略是重启容器
- Readiness(就绪性检测):判断容器是否准备就绪并对外提供服务,将容器设置为不可用,不接受service转发的请求
Pod检测方式
容器探测(container probe)是Pod对象生命周期中的一项重要的日常任务,它是kubelet对容器周期性执行的健康状态诊断,诊断操作由容器的处理器(handler)进行定义。Kubernetes支持三种处理器用于Pod探测:
- ExecAction:在容器内执行指定命令,并根据其返回的状态码进行诊断的操作称为Exec探测,状态码为0表示成功,否则即为不健康状态。
- TCPSocketAction:通过与容器的某TCP端口尝试建立连接进行诊断,端口能够成功打开即为正常,否则为不健康状态。
- HTTPGetAction:通过向容器IP地址的某指定端口的指定path发起HTTP
GET请求进行诊断,响应码为2xx或3xx时即为成功,否则为失败。
任何一种探测方式都可能存在三种结果:“Success”(成功)、“Failure”(失败)、“Unknown”(未知),只有success表示成功通过检测。
重启策略
在Pod中的容器可能会由于异常等原因导致其终止退出,Kubernetes提供了重启策略以重启容器。
Pod通过restartPolicy字段指定重启策略,重启策略类型为:Always、OnFailure 和 Never,默认为 Always。
重启策略对同一个Pod的所有容器起作用,容器的重启由Node上的kubelet执行。Pod支持三种重启策略,在配置文件中通过restartPolicy字段设置重启策略:
注意:pod一旦绑定到一个节点,Pod 将永远不会重新绑定到另一个节点。
Pod的终止过程
当用户提交删除请求后,系统就会进行强制删除操作的宽限期倒计时,并将TERM信息发送给Pod对象的每个容器中的主进程,倒计时结束后,这些进程将会受到强制终止的Kill信号
健康检查
强大的自愈能力是Kubernetes这类容器编排引擎的一个重要特性。自愈的默认实现方式是自动重启发生故障的容器。
为什么需要健康检查
用户还可以利用Liveness和Readiness探测机制设置更精细的健康检查,进而实现如下需求:
- 零停机部署。
- 避免部署无效的镜像。
- 更加安全的滚动升级。
检查策略
在Pod部署到Kubernetes集群中以后,为了确保Pod处于健康正常的运行状态,Kubernetes提供了两种探针,用于检测容器的状态:
存活探测
Liveness是检查容器是否处于运行状态。如果检测失败,kubelet将会杀掉掉容器,并根据重启策略进行下一步的操作。如果容器没有提供Liveness Probe,则默认状态为Success;
Liveness探测器是让Kubernetes知道你的应用是否活着。如果你的应用还活着,那么Kubernetes就让它继续存在。如果你的应用程序已经死了,Kubernetes将移除Pod并重新启动一个来替换它。
让我们想象另一种情况,当我们的应用在成功启动以后因为一些原因“宕机”,或者遇到死锁情况,导致它无法响应用户请求。
在默认情况下,Kubernetes会继续向Pod发送请求,通过使用存活探针来检测,当发现服务不能在限定时间内处理请求(请求错误或者超时),就会重新启动有问题的pod。
就绪探测
Readiness 是检查容器是否已经处于可接受服务请求的状态。如果Readiness Probe失败,端点控制器将会从服务端点(与Pod匹配的)中移除容器的IP地址。Readiness的默认值为Failure,如果一个容器未提供Readiness,则默认是Success。
就绪探针旨在让Kubernetes知道你的应用是否准备好为请求提供服务。Kubernetes只有在就绪探针通过才会把流量转发到Pod。如果就绪探针检测失败,Kubernetes将停止向该容器发送流量,直到它通过。
一个应用往往需要一段时间来预热和启动,比如一个后端项目的启动需要连接数据库执行数据库迁移等等,一个Spring项目的启动也需要依赖Java虚拟机。即使该过程已启动,您的服务在启动并运行之前也无法运行。应用在完全就绪之前不应接收流量,但默认情况下,Kubernetes会在容器内的进程启动后立即开始发送流量。通过就绪探针探测,直到应用程序完全启动,然后才允许将流量发送到新副本。
两者对比
- Liveness探测和Readiness探测是两种Health Check机制,如果不特意配置,Kubernetes将对两种探测采取相同的默认行为,即通过判断容器启动进程的返回值是否为零来判断探测是否成功。
- 两种探测的配置方法完全一样,支持的配置参数也一样。不同之处在于探测失败后的行为:Liveness探测是重启容器;Readiness探测则是将容器设置为不可用,不接收Service转发的请求。
- Liveness探测和Readiness探测是独立执行的,二者之间没有依赖,所以可以单独使用,也可以同时使用。用Liveness探测判断容器是否需要重启以实现自愈;用Readiness探测判断容器是否已经准备好对外提供服务
如何配置
对于LivenessProbe和ReadinessProbe用法都一样,拥有相同的参数和相同的监测方式。
- initialDelaySeconds:用来表示初始化延迟的时间,也就是告诉监测从多久之后开始运行,单位是秒
- timeoutSeconds: 用来表示监测的超时时间,如果超过这个时长后,则认为监测失败
- periodSeconds:指定每多少秒执行一次探测,Kubernetes如果连续执行3次Liveness探测均失败,则会杀掉并重启容器
使用场景
如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活探针; kubelet 将根据 Pod 的restartPolicy 自动执行正确的操作。
如果希望容器在探测失败时被杀死并重新启动,那么请指定一个存活探针,并指定restartPolicy 为 Always 或 OnFailure。
如果要仅在探测成功时才开始向 Pod 发送流量,请指定就绪探针。在这种情况下,就绪探针可能与存活探针相同,但是 spec 中的就绪探针的存在意味着 Pod 将在没有接收到任何流量的情况下启动,并且只有在探针探测成功后才开始接收流量。
如果您希望容器能够自行维护,您可以指定一个就绪探针,该探针检查与存活探针不同的端点。
如果您只想在 Pod 被删除时能够排除请求,则不一定需要使用就绪探针;在删除 Pod 时,Pod 会自动将自身置于未完成状态,无论就绪探针是否存在。当等待 Pod 中的容器停止时,Pod 仍处于未完成状态。
默认的健康检查
我们首先学习Kubernetes默认的健康检查机制:每个容器启动时都会执行一个进程,此进程由Dockerfile的CMD或ENTRYPOINT指定。
如果进程退出时返回码非零,则认为容器发生故障,Kubernetes就会根据restartPolicy重启容器
创建资源清单
Pod的restartPolicy设置为OnFailure,默认为Always,sleep 10; exit 1模拟容器启动10秒后发生故障
vi pod-default-health.yml
apiVersion: v1
kind: Pod
metadata:
name: pod-default-health
namespace: default
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
args: ["/bin/sh","-c"," sleep 10;exit 1"]
创建容器
kubectl apply -f pod-default-health.yml
监控Pod变化
kubectl get pods -o wide -w
该命令可以不断显示容器的因为失败不断重启
在上面的例子中,容器进程返回值非零,Kubernetes则认为容器发生故障,需要重启。
有不少情况是发生了故障,但进程并不会退出。比如访问Web服务器时显示500内部错误,可能是系统超载,也可能是资源死锁,此时httpd进程并没有异常退出,在这种情况下重启容器可能是最直接、最有效的解决方案,那我们如何利用HealthCheck机制来处理这类场景呢?
探针类型
探针类型是指通过何种方式来进行健康检查,K8S有三种类型的探测:HTTP,Command和TCP。
exec存活探针
对于命令探测,是指Kubernetes在容器内运行命令。如果命令以退出代码0返回,则容器将标记为正常。否则,它被标记为不健康。
下面的资源会在先创建一个nginx的任务,生存测试探针livenessProbe会执行test -e /tmp/healthy命令检查文件是否存在, 若文件存在则返回状态码 0,表示成功通过测试。
apiVersion: v1
kind: Pod
metadata:
name: pod-default-health
namespace: default
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
livenessProbe:
initialDelaySeconds: 5
periodSeconds: 3
exec:
command: ["test","-e","/tmp/healthy"]
创建pod
kubectl create -f pod-demo.yamlkubectl get pod -w -o wide
启动后不断检测/tmp/healthy是否存在,不存在重启容器
创建文件
新开一个窗口写入登录pod容器,写入healthy文件
# 登录pod容器
kubectl exec pod-nginx-demo -it /bin/bash
# 在html目录写入healthy文件
echo healthy > /tmp/healthy
查看原来的pod状态
kubectl get pods -o wide -w
我们发现pod不在不断地重启了
HTTP就绪探针
HTTP探测可能是最常见的探针类型。即使应用不是HTTP服务,也可以创建一个轻量级HTTP服务器来响应探测。比如让Kubernetes通过HTTP访问一个URL,如果返回码在200到300范围内,就将应用程序标记为健康状态,否则它被标记为不健康。
上面 清单 文件 中 定义 的 httpGet 测试 中, 请求的资源路径 为/healthy, 地址 默认 为 Pod IP, 端口使用了容器中定义的端口名称 HTTP, 这也是明确为容器指明要暴露的端口的用途之一。
apiVersion: v1
kind: Pod
metadata:
name: pod-default-health
namespace: default
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
livenessProbe:
initialDelaySeconds: 5
periodSeconds: 3
httpGet:
port: 80
path: /healthy
scheme: HTTP
创建pod
kubectl create -f pod-demo.yaml
#查看pod状态
kubectl get pods -o wide -w
我们发现nginx 一致处于未就绪状态
查看pod详情
kubectl describe pod pod-nginx-demo
创建文件
新开一个窗口写入登录pod容器,写入healthy文件
# 登录pod容器
kubectl exec pod-nginx-demo -it /bin/bash
# 在html目录写入healthy文件
echo healthy > /usr/share/nginx/html/healthy
查看原来的pod状态
kubectl get pods -o wide -w
访问
curl 10.244.1.30/healthy
再次删除healthy文件
rm -f /usr/share/nginx/html/healthy
再次查看pod状态,进入未就绪状态
kubectl get pods -o wide -w
TCP探针
TCP探测是指Kubernetes尝试在指定端口上建立TCP连接。
如果它可以建立连接,容器被认为是健康的; 如果它不能被认为是不健康的。
这常用于对gRPC或FTP服务的探测。
下面的资源清单文件,向Pod IP的80/tcp端口发起连接请求,并根据连接建立的状态判断Pod存活状态。
apiVersion: v1
kind: Pod
metadata:
name: pod-nginx-demo
namespace: default
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
livenessProbe:
tcpSocket:
port: 80
探测结果
每次探测都将获得以下三种结果之一:
- 成功:容器通过了诊断。
- 失败:容器未通过诊断。
- 未知:诊断失败,因此不会采取任何行动。
创建pod
kubectl create -f pod-demo.yaml
#查看pod状态
kubectl get pods -o wide -w
只要80端口正常一致就是正常状态