Kubernetes-Pod的启停和正确处理客户端请求

Kubernetes-Pod的启停和正确处理客户端请求

  • Pod的创建流程
  • 生命周期钩子
  • 优雅的关闭Pod
  • Pod启停时的应用
    • Pod启动与服务就绪
    • Pod关闭和客户端请求

Pod的创建流程

pod是k8s中的基本单元,每个pod都包含了一个特殊的根容器Pause和一或多个紧密相关的业务容器。
Pause容器解决了Pod中一个容器死亡是整体死亡还是部分死亡的问题,即以pause容器的状态代表了整个pod的状态。另外pause还简化了pod中容器之间的通信和文件共享的问题。
在CRI(container runtime interface)中,pod中容器所共有的环境和定义的资源约束被称为PodSandBox,PodSandBox在作为Docker运行时就是一个Linux的命名空间,kubelet将创建一个cgroup给容器运行时,并运行所有进程来保障对Pod的资源约束。
我们都知道kubelet是启动、停止和删除容器的组件,其流程如下:
1、kubectl 向 Api server 发出创建pod的请求;
2、Api server接收请求,生成包含创建信息的yaml写入etcd;
3、scheduler 查看Api server,判断该Pod是否是新建的,如需创建,则进行调度计算,分配节点,并将信息写入etcd;
4、kubelet 监测etcd,发现创建pod信息,将该信息中的node编号与自己比较,如果相同,则调用Docker的api创建container;
5、在启动pod前,kubelet会调用RuntimeService.RunPodSandbox创建基础环境(网络资源等),基础环境就绪后,就可以创建、启停和删除不同的容器。

生命周期钩子

lifecycle hooks:
Post-start hooks:启动后的钩子
Pre-stop hooks:停止前的钩子
这两种生命周期的钩子是针对容器来指定的,和初始化init容器不同,init容器是针对整个pod。

Post-start hooks:
该类型的钩子是在容器的主进程启动时被执行的,它与主进程是并行执行的,它可以在我们的应用启动时做一些其他工作,可以让用户在不改动应用程序的情况下运行一些需要的命令。
Pre-stop hooks:
该类型的钩子是在容器被关闭之前立即执行的,在一个配置了Pre-stop hooks的容器中,其关闭之前会执行该钩子,执行后才会向容器发出sigterm的信号。
与Post-start hooks不同,无论Pre-stop hooks是否执行成功,容器最终都会被终止。如果该钩子失败,你会看到FailedPreStopHook的警告,但是因为Pod被关闭终止,你可能很难看到这个警告。
因此这里需要注意的是,如果你的Pre-stop hooks定义了很重要的内容,你需要确认的是该钩子是否成功执行。

优雅的关闭Pod

在kubelet终止pod时,会给每个容器一定的时间来优雅的停止,这个时间叫做终止宽限期(termination grace period)。
该值可在 Pod 的的.spec.terminationGracePeriodSeconds 字段中定义,默认为30s,也可在kubectl delete 时通过 --grace-period 参数指定一个时间(如Pod中已定义,则覆盖 Pod 中的配置)。当该值超过后,会使用SIGKILL 强制干掉 Pod。

在一个设置了Pre-stop hooks的Pod关闭流程:
1、Api server接收delete请求,修改etcd中的相关状态
2、Api server给pod设置deletion timestamp,终止进程开始工作
这里api server还会将删除时间通知给kubelet和controller manager中的endpoint控制器,这两个并行事件在这里用A(kubelet)和B(endpoint controller)表示:
A.1、执行Pre-stop hooks,等待执行完毕
A.2、向容器主进程发送sigterm信号
A.3、等待容器优雅的关闭或者宽限期超时
A.4、如果主进程没有优雅的关闭则使用SIGKILL 强制终止进程

B.1、向api server发送修改endpoint api对象的请求,从endpoint中移除pod 的ip地址
B.2、api server通知所有kube-proxy服务更新iptables,kube-proxy从iptables中移除对应pod

这里我们关注kubelet该事件的流程:
Kubernetes-Pod的启停和正确处理客户端请求_第1张图片

对于一些需要严格准守优雅关闭的服务,比如数据库、分布式的数据存储来说,如果不能确保你的服务在宽限期之内被正常的关闭,这里推荐使用一个专门用来处理关闭流程的Pod(批处理或者定时任务),来持续或者周期性的监控那些没有被正常处理或者迁移的数据。

Pod启停时的应用

Pod启动与服务就绪

ReadinessProbe探针是用来判断容器服务是否达到可用的状态,达到可用状态的Pod才可以接收请求。
对于被Service管理的Pod来说,Service与Pod后端ip的映射也是基于pod是否就绪来设置的,如果pod的状态从ready变为flase,那么系统会将Service对应的后端ip隔离出去,知道Pod的状态恢复到ready,这样就保证了客户端请求不会被转发到服务不可用的Pod上。

如果我们在一个Pod中没有指定就绪探针,那么pod默认总是ready状态,这个被认为总是就绪的pod会立即开始提供服务,尽管这个时候服务是不可用的,这时客户端就会收到connection refused等报错信息。
妥善处理Pod启动的第一步就是添加一个正常获取服务url请求的就绪探针。

Pod关闭和客户端请求

结合在上述的Pod关闭流程里对于两个并行事件的描述,我们不难推测在kube-proxy更新iptables比容器主进程收到SIGNTERM信号花费时间长的情况,在这种情况下客户端请求依旧会通过没有完成更新的iptables到达已经停止接收请求的服务,这时客户端就会收到连接被拒绝的错误。
Kubernetes-Pod的启停和正确处理客户端请求_第2张图片

那么怎么去解决客户端连接被拒绝的错误,我们知道如果在Pod接收到SIGNTERM信号后立即停止服务,那么所有尝试连接该pod的客户端都会收到连接被拒绝的错误。,因此这里Pod必须在接收到SIGNTERM信号后仍然提供服务直到所有的iptables更新完毕。
但是我们无法保证所有工作节点上的kube-proxy或者其他负载均衡分发的组件对网络状态更新完毕的时间,目前我们能做的只有用Pre-stop hooks来给kube-proxy预留一定的完成时间,来尽可能提高用户的体验。
需要注意的是preStop的时间不能太长,长时间的延迟会导致容器无法正常关闭的异常情况,并且会导致delete后pod还会显示在pod列表中,这会给删除pod的用户带来困扰。

lifecycle:
  preStop:
     exec:
         command:
         - sh
         - -c
         - "sleep 5"

你可能感兴趣的:(容器云)