其他分享
首页 > 其他分享> > Kubernetes 中优雅停止服务的那些事

Kubernetes 中优雅停止服务的那些事

作者:互联网

前言

所谓 "优雅停止服务" 一般指不对线上产生影响,或尽可能减少影响地停止服务产生的影响。

现在高可用服务一般由多实例构成,并且客户端请求由负载均衡器 (Load Balancer) 统一路由。

优雅停止流程大致如下:

在实际 Kubernetes 分布式系统中,要做好这件事有许多细节需要注意。本文以 TiDB Server 的优雅停止举例,说明 Kubernetes 中 Pod 删除流程,我们可以做什么,来实现优雅地停止服务。

在使用姿势上,我们并不引入新的流程。而是结合 Kubernetes 提供容器生命周期扩展点实现功能。

从负载均衡器下线

Pod 在标记删除后,Kubernetes 的 Endpoint 控制器会开始将 Pod 的 IP 从 Sevice 后端移除。kube-proxy 以及外部的负载均衡器实现,就会将其从后端移除。

同时 Pod 在标记删除后,kubelet 也会停止容器。这两项操作,由不同组件在发现 Pod 即将删除后操作,并没有先后顺序。若进程在负载均衡器将 Pod IP 从后端移除前,kubelet 就将容器停止,则仍然可能会有新的请求,尝试连接一个不存在的 IP 。若业务层没有重试机制,则请求会失败。

所以,我们需要等待一段时间,再去停止容器。我们可以借助 preStopHook 钩子,在容器停止前等待一段时间:

lifecycle:
      preStop:
        exec:
          command: ["sh", "-c", "sleep 10"]

我们不采用同步方案,让负载均衡器与 kubelet 严格地保证操作顺序执行,因为会过于复杂,且与具体外部负载均衡器实现耦合。例子中的 sleep 时间,可结合生产环境中负载均衡器下线后端反应时间来调整。

结束当前实例上连接

前面我们实现了,先让 Pod 从负载均衡器下线,再结束进程,以避免新的请求在进程结束后,连入进来。但进程还存在当前连接,我们需要结束进程前,先通知进程主动通知客户端关闭连接等。

这步操作与具体的业务的实现有关,我们以 tidb-server 举例。

kubelet 的默认停止容器使用的 SIGTERM 信号,tidb-server 在此信号下会进行优雅退出,但超时时间只有 15 秒,若线上有比较耗时较长的请求,是不够当前连接正常退出的。

tidb-server 在收到 SIGQUIT 信号时会进行不限时的优雅退出,流程如下:

因此,我们不可能使用 kubelet 的默认停止容器使用的 SIGTERM 信号,而是应主动发送 SIGQUIT 给 tidb-server 进程通知其以更优雅的方式运行。

lifecycle:
      preStop:
        exec:
          command: ["sh", "-c", "sleep 10 && kill -QUIT 1"]

PID 1 为容器内 root 进程。注意需要将进行作为 root 进程运行,或者 root 进程可以将信号转发给子业务进程,比如使用 tini 时。

优雅停止超时时间

前面我们不仅在停止服务前通知负载均衡器先将 Pod IP 从后端移除,同时通知应用采用主动通知并结束当前连接后,再退出。但还有一个问题是,许多时候,当前连接要完全优雅结束需要很久,比如一些长连接应用。Kubelet 给 Pod 默认允许的优雅退出时间是 30s,我们需要结合具体应用,配置恰当的超时时间。可以在 pod.spec.terminationGracePeriodSeconds 字段配置:

terminationGracePeriodSeconds: 60
lifecycle:
      preStop:
        exec:
          command: ["sh", "-c", "sleep 10 && kill -QUIT 1"]

至此,我们就实现了完美的优雅退出方案。

结语

我们可以将优雅退出阶段分为以下几部分:

其中具体时间参数,以及通知应用方法需要结合具体应用而定。本文抛砖引玉,以 tidb-server 为例,主要分析思路,和 Kubernetes 中可采用机制。

转发自:https://zhuanlan.zhihu.com/p/188674410

标签:负载,Kubernetes,优雅,停止,均衡器,Pod,连接
来源: https://www.cnblogs.com/fuyuteng/p/16332122.html