十二. Kubernetes Pod 与 探针
目录
一. Pod
- 官方文档
- 什么是Pod:
- 在docker中支持以containers容器的方式部署应用,但是一个容器只能部署一个软件应用,实际情况中如果想让一个软件正常运行通常需要部署多个应用配合运行才能对外提供功能,docker中针对这种情况会把这些应用部署为一组容器,比较繁琐
- Pod是k8s中最小的部署单元,一个pod中可以运行一个或多个容器,这些容器共享存储、网络、以及怎样运行这些容器的声明,一般不直接创建Pod,而是创建一些工作负载由工作负载来创建Pod(下方有工作负载的解释)
- Pod内的容器都是平等的关系,共享Network Namespace、共享文件
- 直接创建Pod的缺点:
- 直接创建的pod,该pod中的容器如果宕机异常,有恢复功能, 但是这个pod如果宕机异常不会有恢复功能,所以创建一些工作负载由他们来创建Pod,实现pod的异常恢复功能
- 在直接部署pod时,虽然可以通过podIP属性指定该pod的访问ip,但是集群网络是私有的,该地址只能在集群内部访问,如果想允许外部访问需要创建Service来公开Pod的网络终结点
- 通过yaml方式创建pod示例
- 执行"vi 文件名称.yaml" 创建pod需要的yaml文件
- 执行"kubectl apply -f 文件名称.yaml" 设置生效,创建对应的pod
kind: Pod
apiVersion: v1
metadata:
name: 自定义当前创建pod名称
namespace: 当前创建pod所在命名空间(默认default)
labels: #当前pod上的标签
aa: bb
cc: dd
spec: #当前创建pod内部信息与期望状态值
containers: #设置容器
- image: nginx #使用镜像
name: 自定义容器名称
podIP: 192.168.0.100 #虽然可以指定ip但是集群的网络是私有的,该地址也只能在集群内部进行访问
Pod 中的多容器协同
- 什么是多容器协同: 在一个pod中部署多个Container应用容器,多个应用容器配合工作,多容器配合工作时,同一个pod下
- 网络:每个Pod都会被分配一个唯一的IP地址**,Pod中的所有容器共享网络空间**,包括IP地址和端口。Pod内部的容器可以使用localhost互相通信。Pod中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)
- 存储: 可以Pod指定多个共享的Volume。Pod中的所有容器共享volume。Volume也可以用来持久化Pod中的存储资源,以防容器重启后文件丢失
- 多容器协同的好处: 举个不太恰当的例子,服务间相互调用现在有a,b,c,d四个服务,d是配合工作的附加服务,多个服务间是通过d服务进行通信的,这样在pod中就可以部署为"a,d",“b,d”,"c,d"在pod内部abc三个主服务都附加一个d服务,优点:
- 将相关的服务放在同一个 Pod 中可以共享资源,例如网络和存储,避免了在不同 Pod 之间进行跨节点通信的开销
- 更高的可靠性:这种部署方式可以增加服务之间的稳定性和可靠性,可以通过本地的 localhost 直接通信,而且共享相同的生命周期
- 简化部署和管理:将相关服务打包到同一个 Pod 中可以简化部署和管理过程。例如可以通过 Deployment 或 StatefulSet 来管理 Pod,可以实现集中的配置、更新和扩展,同时降低了管理成本
- 安全性增强:因为打包到同一个pod的服务之间的通信是通过 localhost 进行的,不需要经过网络暴露,减少了一些潜在的攻击
- 同一pod下的多容器共享网络与存储
- pod中部署多容器简单示例(实际就是编写创建pod的yaml时在spec.containes下指定多个容器,多个容器配合运行实现某些功能),如下yaml创建的pod中存在两个容器,两个容器公用一个存储挂载卷,其中一个容器向挂载的文件中写数据,一共容器读取挂载卷中的数据
Pod 的组成与paush (重要)
- 上面说了一个pod中可以部署一个或多个容器, 同一个pod下的多个容器之间共享网络通过localhost就可以互相通信,共享文件资源,那么一个pod到底是由哪些东西组成的,在pod中除了运行应用的多个container容器外,每个pod内部都会存在一个Pause特殊容器
- 注意在执行"kubectl get pod"是看不到pause的,要执行"docker ps|grep pod名称"命令才能看到,如下图
- 每个pod内部都会部署一个Pause特殊容器,pod在启动容器时会优先启动这个特殊容器,可以将这个特殊容器看成用来管理同一pod下的其它应用容器的,它负责了: 创建和占用pod的NetworkNamespace网络命名空间和PID命名空间:
- 简单解释就是pause容器在启动时,会为 pod 生成一个独立的网络环境和进程环境,并将这些环境与其他 pod 隔离开,其中:
- 创建和占用pod的NetworkNamespace网络命名空间,网络命名空间是Linux的一种技术,通过pause为pod分配一个虚拟 IP 地址分配一个独立的虚拟网络接口和 IP 地址,当前pod中的其他用户容器就可以加入到这个网络命名空间中,从而共享同一个IP和网络接口,提供了网络通信环境,使得它们可以通过 localhost 进行通信,也可以访问外部网络
- 创建和占用pod 的 PID 命名空间,并为自己分配一个 PID 为 1 的进程,pod 中的其他用户容器就可以加入到这个 PID 命名空间中,共享同一个进程 ID 空间,通过共享进程ID空间实现了: 1pod 内的容器之间的进程监控和管理, 2pod 内的容器之间的信号传递, 3pod 内的容器之间的僵尸进程的回收避免了资源泄漏(注意好像在1.8版本中被修改默认情况下是禁用的)
- 回收僵尸进程的步骤如下:
- 首先,当 pod 中的某个用户容器启动时,它会加入到 pause 容器创建和占用的 PID 命名空间中,从而共享同一个进程 ID 空间
- 当 pod 中的某个用户容器中的某个子进程退出时,会变成一个僵尸进程,也就是已经结束运行,但是还没被父进程回收
- 由于 pod 中的所有容器都在同一个 PID 命名空间中,所以都可以看到这个僵尸进程,并且可以向它发送 SIGCHLD 信号,通知它的父进程回收它
- pause 容器是 pod 中第一个启动的容器,并且分配了 PID 为 1 的进程,它会成为 pod 中所有孤儿进程(没有父进程或者父进程已经退出的进程)的父进程。因此,当 pause 容器收到 SIGCHLD 信号时,它会调用 wait() 函数来回收僵尸进程,并释放其占用的资源
- 面试题1: 为什么一个pod中的多个容器可以共享网络: 因为在pod在部署容器时,每个应用都会多部署一个对应的paush容器,通过这个paush容器设置来设置当前实际容器的网络
- 面试题2: 如果pod中的容器宕机重启,ip会变吗,不会,底层会通过对应的paush去设置网络
Pod 的生命周期
- pod对象从创建到终止的这段时间范围称为pod的生命周期,主要包含:
- pod创建
- 运行初始化容器(initContainer)
- 运行主容器(main container)过程
3.1 容器启动后钩子postStart执行,容器终止前preStop钩子
3.2 容器的存活性探测(liveness probe),就绪性探测(readness probe)
- pod终止过程
- 在整个声明周期中,Pod会出现5种状态(相位)
- 挂起Pending:挨批server已经创建了pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中
- 运行中Running:pod已经被调度至某节点,并且所有容器都已经被kubectl创建完成
- 成功Succeed:pod中的所有容器都已经成功终止并且不会被重启
- 失败Failed:所有容器都已经停止,但至少有一个容器终止失败,即容器返回了非0的退出状态
- 未知Unknown:apiserver无法正常获取到pod对象的状态信息,通常由网络通信失败所致
- Pod的创建过程详解:
- 用户通过kubectl或其他api客户端提交需要创建的pod信息给apiServer
- apiServer开始生成pod对象的信息,并将信息存入etcd,然后返回确认信息至客户端
- apiServer开始反映etcd中的pod对象的变化,其他组件使用watch机制来跟踪检查apiServer上的变动
- scheduler发现有新的pod对象要创建,开始为pod分配足迹并将结果更新只apiServer
- node节点上的kubectl发现有pod调度过来,尝试调用docker启动容器,并将结果回送至apiServer
- apiServer将接收到的pod状态信息存入etcd中
- Pod的终止过程详解:
- 用户向apiServer发送删除pod对象的命令,执行kubectl delete {pod_id}删除老pod
- 在删除pod时好像有一个在宽限期默认30秒,此时pod被视为dead
- 后续apiServer接收到删除命令后,会在etd中将根据pod_id将老pod标记为terminating退出中
- kube-proxy监听endPoint,当发现pod对象转为terminating状态,会触发iptables-restore,生成全新的iptables规则(上线pod时会执行多次,下线pod时好些只会执行一次),新的iptables DNAT规则中就会删除当前pod_id,将当前pod提出负载均衡
- 如果当前pod对象定义了preStop钩子处理器,则在其标记为terminating后即会以同步的方式执行
- kubelet监听到etcd中pod status状态变化,当发现属于当前节点时,走针对pod的实际退出流程—>preStop执行pod的退出回调–>关闭监听端口–>处理现有连接–>完成退出
- 注意kube-proxy检查到下线pod命令执行删除地址动作与kubelet执行删除实际pod是同步执行的,所以可能会出现pod已经实际删除了,但是地址还存在endpoint列表中,造成请求异常
Pod状态与重启策略
- 先了解一下pod的几种状态
- Pending挂起: 请求创建pod时条件不满足,调度没有完成,已经创建了但是没有节点运行当前pod则叫做挂起,这其中也包含集群为容器创建网络或者下载镜像的过程
- Running: Pod内所有的容器都已经被创建,且至少一个容器正在处于运行、启动或重启状态。
- Succeeded:Pod中所以容器都执行成功后退出,并且没有处于重启的容器
- Failed:Pod中所以容器都已退出,但是至少还有一个容器退出时为失败状态
- Unknown未知状态:所谓pod是什么状态是apiserver和运行在pod节点的kubelet进行通信获取状态信息的,如果节点之上的kubelet本身出故障,那么apiserver就连不上kubelet,得不到信息了,就会看Unknown
- Pod支持的重启策略:(如果pod的restartpolicy没有设置,那么默认值是Always)
- Always: 只要容器失效退出就重新启动容器。
- OnFailure: 当容器以非正常(异常)退出后才自动重新启动容器。
- Never: 无论容器状态如何,都不重新启动容器
- Pod状态流转的几种情况
- 接下来根据Pod的重启策略结合探针来看
- 参考博客
静态Pod
- kl8s中pod分为静态pod与动态pod,我们使用k8s指定部署的应用pod称为动态pod,而k8s这个服务中的基础设施启动需要的pod称为静态pod, 在/etc/kubernetes/manifests位置放了创建这些静态pod的Pod.yaml文件,机器启动kubelet自己就把他启动起来。静态Pod一直守护在他的这个机器上
二. 探针
- 上面了解到pod是有状态有生命周期的,通过这个状态又延伸出了重启策略, 重启底层实际就基于探针实现的,通过探测和重启策略实现了服务的健康检查,0宕机
- 在容器的containers中存在三个属性: startupProbe启动探针, livenessProbe存活探针, readinessProbe就绪探针
- startupProbe启动探针: 用来探测当前容器是否启动成功
- livenessProbe存活探针: 用来判断当前容器是否存活,例如当探测到容器不存活时,会重新拉起
- readinessProbe就绪探针: 用来探测当前容器是否就绪,能否能够对外提供服务,以调用服务负载均衡为例,当接收到请求后如果通过该探针探测到某个服务节点不可用,则不会将该节点加入负载均衡
- 探针支持的三种设置方法(与钩子相同)
- exec: 通过钩子程序执行命令
- httpGet: 通过钩子发送http get请求
- tcpSocket: 容器创建之后连接tcp端口进行指定操作
kind: Pod
apiversion: v1
metadata:
name: my-nginx
namespace: hello
spec:
containers: #容器相关设置
- image: nginx
name: nginx-test
startupProbe: #启动探针
exec:
httpGet:
tcpSXocket:
livenessProbe: #存活探针
readinessProbe: #就绪探针
1. livenessProbe存活探针
- livenessProbe存活探针用于判断容器是不是健康,如果不满足健康条件,Kubelet 将根据 Pod 中设置的 restartPolicy 重启策略来判断,Pod 是否要进行重启。
- LivenessProbe按照配置去探测 ( 进程、或者端口、或者命令执行后是否成功等等),来判断容器是不是正常。如果探测不到,代表容器不健康(可以配置连续多少次失败才记为不健康),则 kubelet 会杀掉该容器,并根据容器的重启策略做相应的处理。
- 如果未配置存活探针,则默认容器启动为Success通过状态。即Success后pod状态是RUNING
2. readinessProbe就绪探针
- readinessProbe就绪探针,用于判断容器内的程序是否存活或者说是否健康,是否启动完成并就绪,正常对外提供服务
- 容器启动后会按照readinessProbe的配置进行探测, 探测成功返回 Success。pod的READY状态变为 true,更新pod成功数量比如1/1,否则还是0/1。
- 若未配置就绪探针,则默认容器启动后状态Success。此时pod、pod关联的Service、EndPoint 等资源都会进入Ready 状态,进行相关设置
- 后续程序运行中还可以通过readinessProbe继续监测, 如果探测失败,更新Pod 的 Ready 状态变为 false,系统则会在对应的Service关联的 EndPoint 列表中去除此pod地址,实现服务的异常踢除。如果 Pod 恢复为 Ready 状态。将再会被加回 Endpoint 列表。kube-proxy也将有概率通过负载机制会引入流量到此pod中
3. startupProbe启动探针
- 启动探针时 k8s 在1.16版本后增加startupProbe探针,主要解决在复杂的程序中readinessProbe、livenessProbe探针无法更好的判断程序是否启动、是否存活。进而引入startupProbe探针为readinessProbe、livenessProbe探针服务,
- startupProbe探针与另两种区别: 如果三个探针同时存在,先执行startupProbe探针,其他两个探针将会被暂时禁用,直到pod满足startupProbe探针配置的条件,其他2个探针启动,如果不满足按照规则重启容器, 另外两种探针在容器启动后,会按照配置,直到容器消亡才停止探测,而startupProbe探针只是在容器启动后按照配置满足一次后,不在进行后续的探测
4. 就绪、存活两种探针的区别
ReadinessProbe 和 livenessProbe 可以使用相同探测方式,只是对 Pod 的处置方式不同:
- readinessProbe 当检测失败后,将 Pod 的 IP:Port 从对应的 EndPoint 列表中删除。
- livenessProbe 当检测失败后,将杀死容器并根据 Pod 的重启策略来决定作出对应的措施
5. 就绪、存活两种探针的使用方法与相关属性解释
- 探针支持通过一下方式探测
- exec: 通过钩子程序执行命令
- httpGet: 通过钩子发送http get请求
- tcpSocket: 容器创建之后连接tcp端口进行指定操作
- 探测后能拿到的结果值
- Success:表示通过检测。
- Failure:表示未通过检测。
- Unknown:表示检测没有正常进行。
- 使用探针时相关属性解释:
- initialDelaySeconds:容器启动后等待多少秒后开始探测,默认是 0 秒,最小值是 0
- periodSeconds:执行探测的时间间隔(单位秒),默认为 10s,最小值是 1
- timeoutSeconds:探针执行检测请求后,等待响应的超时时间,默认为 1秒,最小值是 1
- successThreshold:最小连续成功检查数, 在确认是否探测成功时,可以设置为探测多次成功才认为成功,默认为 1,在 Liveness 和 readiness 探针中通常设置为1,在startup可以设置大点,来延时,防止程序刚启动,其它探测探测异常
- failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,在 readiness 探针中,Pod会被标记为未就绪,默认为 3,最小值为 1
- initialDelaySeconds在readinessProbe其实可以不用配置,不配置默认pod刚启动,开始进行readinessProbe探测,但那有怎么样,除了startupProbe,readinessProbe、livenessProbe运行在pod的整个生命周期,刚启动的时候readinessProbe检测失败了,只不过显示READY状态一直是0/1,readinessProbe失败并不会导致重启pod,只有startupProbe、livenessProbe失败才会重启pod。而等到多少后,真正服务启动后,检查success成功后,READY状态自然正常
- 示例一, 解释:
容器在初始化后,执行(/bin/sh -c “touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600”)首先创建一个 /tmp/healthy 文件,然后执行睡眠命令,睡眠 30 秒,到时间后执行删除 /tmp/healthy 文件命令。而设置的存活探针检检测方式为执行 shell 命令,用 cat 命令输出 healthy 文件的内容,如果能成功执行这条命令一次(默认successThreshold:1),存活探针就认为探测成功,由于没有配置(failureThreshold、timeoutSeconds),所以执行(cat /tmp/healthy)并只等待1s,如果1s内执行后返回失败,探测失败。在前 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
args: #创建测试探针探测的文件
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
initialDelaySeconds: 10 #延迟检测时间
periodSeconds: 5 #检测时间间隔
exec: #使用命令检查
command: #指令,类似于运行命令sh
- cat #sh 后的第一个内容,直到需要输入空格,变成下一行
- /tmp/healthy #由于不能输入空格,需要另外声明,结果为sh cat"空格"/tmp/healthy
- HTTP方式健康探测示例
Pod 中启动的容器是一个 SpringBoot 应用,其中引用了 Actuator 组件,提供了 /actuator/health 健康检查地址,在pod启动后,初始化等待20s后,livenessProbe开始工作,去请求HTTP://podIP:8081/actuator/health 接口,类似于curl -I HTTP://podIP:8081/actuator/health接口,考虑到请求会有延迟(curl -I后一直出现假死状态),所以给这次请求操作一直持续5s,如果5s内访问返回数值在>=200且<=400代表第一次检测success,如果是其他的数值,或者5s后还是假死状态,执行类似(ctrl+c)中断,并反回failure失败。等待10s后,再一次的去请求HTTP://podIP:8081/actuator/health接口。如果有连续的2次都是success,代表无问题。如果期间有连续的5次都是failure,代表有问题,直接重启pod,此操作会伴随pod的整个生命周期
apiVersion: v1
kind: Pod
metadata:
name: liveness-http
labels:
test: liveness
spec:
containers:
- name: liveness
image: mydlqclub/springboot-helloworld:0.0.1
livenessProbe:
failureThreshold: 5 #检测失败5次表示未就绪
initialDelaySeconds: 20 #延迟加载时间
periodSeconds: 10 #重试时间间隔
timeoutSeconds: 5 #超时时间设置
successThreshold: 2 #检查成功为2次表示就绪
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
- 自定义HTTP请求headers示例
livenessProbe:
failureThreshold: 5 #检测失败5次表示未就绪
initialDelaySeconds: 20 #延迟加载时间
periodSeconds: 10 #重试时间间隔
timeoutSeconds: 5 #超时时间设置
successThreshold: 2 #检查成功为2次表示就绪
httpGet:
port: 8080
path: /health
httpHeaders:
- name: end-user
- value: Jason
6. startupProbe启动探针与属性解释
- k8s 在1.16版本后增加startupProbe探针,主要解决在复杂的程序中readinessProbe、livenessProbe探针无法更好的判断程序是否启动、是否存活。进而引入startupProbe探针为readinessProbe、livenessProbe探针服务
- startupProbe探针与另两种区别: 如果三个探针同时存在,先执行startupProbe探针,直到探测成功,其他2个探针才会启动执行,如果startupProbe探测失败则会按照配置重启容器, 并且startupProbe探针在容器启动后按照探测规则只要确认成功,不在进行后续的探测, 其它两种探针在容器启动后,会按照配置定时执行,直到容器消亡才停止探测
- startupProbe启动探针属性解释(与 ReadinessProbe 和 livenessProbe 相同)
- initialDelaySeconds:容器启动后等待多少秒后开始探测,默认是 0 秒,最小值是 0
- periodSeconds:执行探测的时间间隔(单位秒),默认为 10s,最小值是 1
- timeoutSeconds:探针执行检测请求后,等待响应的超时时间,默认为 1秒,最小值是 1
- successThreshold:最小连续成功检查数, 在确认是否探测成功时,可以设置为探测多次成功才认为成功,默认为 1,在 Liveness 和 readiness 探针中通常设置为1,在startup可以设置大点,来延时,防止程序刚启动,其它探测探测异常
- failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,在 readiness 探针中,Pod会被标记为未就绪,默认为 3,最小值为 1
- 注:在startupProbe执行完之后,其他2种探针的所有配置才全部启动,相当于容器刚启动的时候,所以其他2种探针如果配置了initialDelaySeconds,建议不要给太长
- startupProbe的存在意义?startupProbe 和 livenessProbe 最大的区别就是startupProbe在探测认为成功之后就不会继续探测了,而livenessProbe在pod的生命周期中一直在探测,并且其它探针是在startupProbe认为成功后才会执行,。如果只设置livenessProbe探针会存在如下问题: 一个服务如果前期启动需要很长时间,那么它后面死亡未被发现的时间就越长,为什么会这么说呢?假设我们一个服务A启动完成需要2分钟,那么我们如下开始定义livenessProbe,5s就会根据重启策略进行一次重启,这个时候你会发现pod一直会陷入死循环
livenessProbe:
httpGet:
path: /test
prot: 80
failureThreshold: 1
initialDelay:5
periodSeconds: 5
- 修改为,使用启动探针startupProbe,程序有605s=300s的启动时间,当startupProbe探针探测成功之后,才会被livenessProbe接管,这样在运行中出问题livenessProbe就能在15=5s内发现。如果启动探测是3分钟内还没有探测成功,则接受Pod的重启策略进行重启
livenessProbe:
httpGet:
path: /test
prot: 80
failureThreshold: 1
initialDelay:5
periodSeconds: 5
startupProbe:
httpGet:
path: /test
prot: 80
failureThreshold: 60
initialDelay:5
periodSeconds: 5
7. 探针总结
- k8s中提供了三种探针: livenessProbe存活探针, readinessProbe就绪探针和startupProbe启动探针,并且启动探针是1.6版本添加的
- 支持的探测方式:
- exec: 通过钩子程序执行命令
- httpGet: 通过钩子发送http get请求
- tcpSocket: 容器创建之后连接tcp端口进行指定操作
- 不同探针中都存在几个属性,分别是:
- initialDelaySeconds: 容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认0,最小值0,设置过长可能导致应用一段时间不可用,设置过短可能造成启动循环
- periodSeconds: 执行探测的时间间隔, 默认是10秒。最小值是1
- successThreshold: 探测失败后,被视为成功的最小连续成功数,默认值是1(存活和启动探针的这个值必须是1。最小值是1)
- failureThreshold: 当探测失败时,Kubernetes的重试次数。存活探测情况下的放弃就意味着重新启动容器。就绪探测情况下的放弃Pod会被打上未就绪的标签。默认值是3。最小值是1
- timeoutSeconds: 探测的超时后等待多少秒。默认值是1秒。最小值是1
- kubelet会主动按照Pod里面配置的探针,给对应的容器容器发送探测请求进行监听
- kubelet使用启动探针,来检测应用是否已经启动。启动探针启动成功后就不再使用,后续通过存活探针和就绪探针持续探测,慢容器一定指定启动探针。 一个服务如果前期启动需要很长时间,那么它后面死亡未被发现的时间就越长,如果只使用存活探针检查,探测间隔时间要超过这个启动时间否则会造成一直循环重启,所以添加启动探针,即使重启最开始也是有启动探针检测,当启动探针检测成功后才交由其它探针检测
- 当探测到容器启动成功后,后续kubelet通过livenessProbe存活探针探测,每隔periodSeconds时间探测一次获取容器状态,如果连续successThreshold次返回成功当前容器表示存活,如果连续failureThreshold次拿到的都是失败则认为容器为不存活状态,对容器进行重启
- 通过livenessProbe存活探针只探测容器的存活状态,至于容器能不能访问,能不能对外提供服务,k8s中通过readinessProbe就绪探针定时检查,当就绪探针发现对应容器返回失败,那么这个容器不会加入Service负载均衡网络,不接受流量,此时执行"kubectlexec-it"进不去,可以通过"Kubectl describe"查看异常情况,当前我们使用的就绪检测都是基于http的,例如定时发送http访问当前容器服务,如果返回不是0,那就是探测失败.注意就绪探针探测失败时不会对容器重启
- 当前生产上配置就绪探针,存活探针都是5秒执行一次,连续两次失败才认为失败,1次成功则认为是成功,存活探针基于tcp,就绪探针基于httpGet
tcp存活探针: 类似于telnet 80端口,如果连接失败则将杀死 Pod 重启容器
http就绪探针: 与tcp类似,给指定端口发送http请求,根据响应结果判断容器状态,当前访问的服务域名
readinessProbe:
httpGet:
path: /
port: <PORT>
scheme: HTTP #返回不是0,那就是探测失败
initialDelaySeconds: 30 #指定的这个秒以后才执行探测
periodSeconds: 5 #每隔几秒来运行这个
failureThreshold: 2 #失败阈值,连续几次失败才算真失败
successThreshold: 1 #成功阈值,连续几次成才算成功
timeoutSeconds: 5 #探测超时,到了超时时间探测还没返回结果说明失败
livenessProbe:
tcpSocket:
port: <PORT>
initialDelaySeconds: 30
periodSeconds: 5
failureThreshold: 2
successThreshold: 1
timeoutSeconds: 5
三. Pod的tolerations容忍策略相关
- Pod中可以通过tolerations进行容忍相关设置,实现了如下功能
- 调度约束:通过设置tolerations容忍,可以将pod调度安装到指定节点上
- 弹性和容错性:在节点出现故障或维护时,节点上的Pod可能需要迁移到其他节点上。通过定义tolerations,可以确保Pod仍然能够被分配到指定节点上,并且可以设置等待迁移时间
- Pod下tolerations内部是一个数组属性,可以设置多个容忍规则,其中内部包含:
- key:设置需要匹配的污点(注意此处除了可以设置自定义污点外,也可以设置k8s内部预定义的污点,实现某些特殊功能)
- operator:指定如何与节点上的污点进行比较。常用的匹配操作符有以下几种:
2.1 Equal:要求污点的键值与toleration规则的键值完全相等。
2.2 Exists:只要存在与toleration规则的键相同的污点键,即可匹配成功。
- value:匹配的值,污点也是有值的,当污点匹配成功后,匹配污点的值,如果未指定该属性,则默认为""空字符串。
- effect:设置匹配成功后的执行规则,可以设置为以下几种:
4.1 NoSchedule:当Pod满足toleration规则时,将不会被调度到带有匹配污点的节点上。
4.2 PreferNoSchedule:当Pod满足toleration规则时,尽量不要被调度到带有匹配污点的节点上,但并不是绝对禁止调度
4.3 NoExecute:当Pod满足toleration规则时,如果该节点上已经存在该污点,则会从该节点上驱逐(删除)Pod。
- tolerationSeconds:定义容忍时间,用于指定容忍期限。当一个节点上的污点超过了容忍时间,Pod将被驱逐。该属性是可选的,如果未指定,默认为tolerationSeconds: null,表示没有容忍期限
- 使用示例
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: nginx:latest
tolerations: # 指定tolerations部分
- key: "critical-node" # 指定需要容忍的污点
operator: "Equal" # 指定如何与节点上的污点进行比较,当前是做等值判断
value: "true" # 指定节点上污点的预期值判断,默认为""空字符串
effect: "NoSchedule" # 当匹配规则满足时的作用效果,当前表示为不调度安装到存在该污点的节点
k8s内置的污点与节点异常容忍时间示例
- k8s中内置了多种污点,可以将这些污点设置到tolerations的key属性上实现一些特殊功能,例如
- node.kubernetes.io/not-ready: 用于表示节点当前处于不可用状态
- node.kubernetes.io/unreachable:用于标记节点不可达。当节点无法与集群通信时,该污点会自动添加到节点上,以确保不会将新的Pod调度到该节点上。
- node.kubernetes.io/out-of-disk:用于标记节点磁盘空间不足。当节点的可用磁盘空间不足时,该污点会自动添加到节点上,防止将新的Pod调度到可能导致更多磁盘使用的节点上。
- node.kubernetes.io/memory-pressure:用于标记节点内存压力过大。当节点的可用内存资源不足时,该污点会自动添加到节点上,以避免将新的Pod调度到可能导致更多内存使用的节点上。
- node.kubernetes.io/disk-pressure:用于标记节点磁盘压力过大。当节点的磁盘压力过大时,该污点会自动添加到节点上,以避免将新的Pod调度到可能导致更多磁盘使用的节点上。
- node.kubernetes.io/network-unavailable:用于标记节点网络不可用。当节点的网络无法正常工作时,该污点会自动添加到节点上,以防止将新的Pod调度到网络不可用的节点上。
- 以节点异常容忍时间为例举例说明内置污点的使用,首先什么是节点的异常容忍时间,意思是k8s集群中如果某个节点意外宕机,在发生宕机时,该节点上部署的应用会自动迁移到其它可用节点,迁移过程可能需要一些时间,取决与硬件性能,网络性能,镜像拉取时间等等,为此k8s引入了一个称为"容忍时间"Toleration的概念,用于控制每个Pod在发生迁移时,允许等待的时间,默认为300秒,如果在这个等待时间内没有重新调度到其它节点完成运行,这个pod将会标记为丢失状态(注意这个等待时间只对已经允许的pod有效,对于新部署的pod无效)
- 如何修改容忍时间,或者如何缩短容忍时间(不要修改过短,要考虑服务的启动时间,如果过短考虑网络延迟问题,会出现pod反复迁移),通过部署pod的tolerations属性设置
- 首先在节点发生异常时,会自动给节点添加 node.kubernetes.io/not-ready表示当前节点处于不可用状态的污点,和node.kubernetes.io/unreachable节点通信异常表示当前节点不可达的污点
- 在pod模板中的tolerations属性中,通过key设置匹配这两个污点,设置不在存在该污点的节点上调度安装
- 在pod模板中的tolerations属性中,通过tolerationSeconds指定容忍时间,当前不设置默认为300秒,既在pod发生迁移时如果指定时间内为迁移成功,该pod会标记为丢失状态
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
containers:
- name: my-container
image: nginx
tolerations:
- key: node.kubernetes.io/unreachable #匹配的污点,该污点是k8s内置的
operator: Exists #匹配规则,Exists表示存在
effect: NoSchedule
tolerationSeconds: 300 # 设置容忍时间默认300秒
- key: node.kubernetes.io/not-ready #匹配的污点,该污点是k8s内置的
operator: Exists #匹配规则,Exists表示存在
effect: NoSchedule #效果,NoSchedule表示如果匹配成功,则在对应的节点上驱逐,也就是不会调度安装到对应的节点上
tolerationSeconds: 300 # 设置容忍时间默认300秒
- 也可以创建一个修改容忍时间的配置文件,将这个配置文件以补丁的形式打到对应的Deployment控制器中(打补丁也会造成pod重新拉起)
- 执行"cat 文件名.yaml"创建修改容忍时间的配置文件
- 执行: kubectl patch deploy 指定资源名称 --patch “$(cat 文件.yaml)”
- 还有一个问题,在节点发送宕机时,迁移过程中,k8s集群还是对外提供服务的,问题在迁移过程中会不会影响对外提供服务,不考虑性能的情况下不会,因为k8s内部有负载均衡,有服务发现,服务踢除等动作,节点宕机且在容忍时间内,Pod还未迁移到其他节点上的情况下,该节点中的Pod将暂时无法对外提供功能,并将从负载均衡列表中移除或标记为不可用。只有在Pod成功迁移并启动后,才能恢复对外提供服务并重新加入负载均衡器列表,
- 在迁移过程中还有一个注意点,假设使用iptables网络代理模式,如果内部服务比较多,迁移时间可能会比较长,可能会出现当前正在迁移的pod地址还未在负载均衡列表中踢除,还对外提供服务,最终导致请求失败,如何解决这个问题:1修改网络代理模式为ipvs,2减少容忍时间
- 注意当一个Pod发生异常并需要重新调度时,并没有像节点宕机那样的容忍时间