业务容器化最佳实践--优雅退出

栏目: IT技术 · 发布时间: 4年前

内容简介:kubernetes的重要性已经不言而喻了,目前不少的企业已经选择将自己的业务运行在kubernetess上。我本人在多家企业落地kubernetes,也和很多企业负责人聊过类似话题。我们发现很多人把业务容器化简单理解为dockerfile + 原有代码。这是不对的,新的运行形态就决定了我们的代码必须做相应的适配,才可以充分发挥kubernetes的强大。本系列文章主要讲述业务容器化最佳实践,希望对大家有所帮助。今天我们主要阐述

kubernetes的重要性已经不言而喻了,目前不少的企业已经选择将自己的业务运行在kubernetess上。我本人在多家企业落地kubernetes,也和很多企业负责人聊过类似话题。我们发现很多人把业务容器化简单理解为dockerfile + 原有代码。这是不对的,新的运行形态就决定了我们的代码必须做相应的适配,才可以充分发挥kubernetes的强大。本系列文章主要讲述业务容器化最佳实践,希望对大家有所帮助。

业务容器化最佳实践--优雅退出

今天我们主要阐述 应用优雅退出

不要把代码架构的失败归结于Kubernetes

开源的世界里,大家已经习惯了引入各种框架来解决自己的问题,但是我们都知道技术的世界是没有银弹的。

我在早期参加一些meetup的时候,很多来参会的人会提出一个问题:如何在pod缩容的时候,避免5xx?进而怀疑kubernetes可用性。

这个问题其实比较好理解。业务pod通过service或是ingress的方式,暴露出去。大多数场景都是pod作为负载均衡器的upstream存在,但是任何的负载均衡器不能实时探测你的健康状态,当你的pod退出的时候,中间有一定的时间差,真实流量被分发到了一个退出的pod当中,自然就出现了5xx。

业务运行在pod形相对于普通的虚拟机,具有动态的特性,比如扩缩容,或是被驱赶等等。这就要求我们的业务要做对应的适配。

如何优雅退出

在kubernetes中,正确的平滑退出的顺序应该如下:

  • 监听并捕获SIGTERM信号
  • 停止接受新的连接
  • 完成已经存在的活跃请求
  • 关闭keepalive
  • 退出

我们知道当pod被delete的时候,SIGTERM信号被发送到每个容器中的主进程(PID 1),默认开始20s的倒计时(该值可以设置),20s后,SIGKILL信号发送,程序被kill。

首先可以查看我们的应用是否是1号主进程,如果不是,那么我们需要让1号主进程去通知业务进程,即1号进程的子进程。此类问题,1号进程很多可能是一个 run.sh 启动脚本。

例如golang项目:

sigTerm := make(chan os.Signal, 1)

signal.Notify(sigTerm, syscall.SIGTERM)

...

go server.ListenAndServe()

<-sigTerm

log.Println("got SIGTERM, prepare to shut down in 20s")

对于 停止接受新的请求连接 ,我们知道在k8s中存在LivenessProbe 和 ReadinessProbe两种healthcheck探针。

  • LivenessProbe :用于判断容器是否存活,如果 LivenessProbe 探针探测到容器不健康,则 kubelet 将杀掉容器,并根据容器的启动策略做相应的处理,如果一个容器不包括 LivenessProbe 探针,kubelet 认为该容器的 LivenessProbe 探针返回的值永远是 Success。
  • ReadinessProbe:用于判断容器是否启动完成(ready 状态),可以接受请求,如果 ReadinessProbe 探针检测到失败,则 Pod 的状态则被更改。

业务容器化最佳实践--优雅退出

这里我们应该使用的是ReadinessProbe。ReadinessProbe 探测影响的是该pod是否是一个准备就绪的pod,只有检测通过的pod才会加到lb的upstream中。

所以我们需要保证:

  • 我们的应用提供并设置了ReadinessProbe。
  • 我们的ReadinessProbe是退出有关系的。

两个保证是有一定的递进关系的,详细解释一下。

如果我们设置了如下的探针:

func healthHandler(w http.ResponseWriter, r *http.Request) {
    typ := r.URL.Query().Get("type")
    log.Printf("healthy - %s", typ)
    w.WriteHeader(200)
    w.Write([]byte("ok"))
}

该探针只是可以保证,pod在启动的过程中,pod处于就绪状态,流量才会打过来。在pod彻底被kill之前,该探针一直返回就绪状态,而我们要实现在停机的过程中,停止接受新的请求连接。所以我们的探针必须是和停止有关,正确的实现方式应该如下:

func healthHandler(w http.ResponseWriter, r *http.Request) {
    typ := r.URL.Query().Get("type")
    if !healthy {
        log.Printf("not healthy - %s", typ)
        w.WriteHeader(503)
        w.Write([]byte("not ready"))
    } else {
        log.Printf("healthy - %s", typ)
        w.WriteHeader(200)
        w.Write([]byte("ok"))
    }
}

设置一个环境变量healthy,初始化值为true。

当接收到系统发送的SIGTERM, 业务pod开始了停机过程,先将healthy更改为false。进而探针探测失败,新的流量不再打到处于停机过程中的pod。

对于完成已经存在的活跃请求

此处要求不要急于退出,sleep一定时间,保证已经活跃的请求完成。

代码如下:

time.Sleep(sigtermTimeout)

假如存在一些长连接服务, 关闭keepalive 也是非常重要的。

代码实现如下:

if !keepAlive {
        log.Println("SetKeepAlivesEnabled=false")
        server.SetKeepAlivesEnabled(false)
    }

总结

该文主要讲了业务如何在代码上做一些措施,实现pod的优雅退出。接下来,我们会继续实践系列,计划从资源分配等角度展开。


以上所述就是小编给大家介绍的《业务容器化最佳实践--优雅退出》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法交易:制胜策略与原理

算法交易:制胜策略与原理

[美]欧内斯特·陈(Ernest P. Chan) / 高闻酉、黄蕊 / 机械工业出版社 / 49.00

本书是一本引人入胜、信息量大、覆盖各类交易策略的图书。无论个人投资者,还是机构投资者,都可以借鉴和使用其中的策略。本书中的策略大致可分为均值回归系统和动量系统两大类。书中不仅介绍了如何使用每种类别的交易策略,更解释了各种策略之所以有效的原因。本书始终以简单、线性的交易策略为重心,因为复杂的交易策略容易受到过度拟合及数据窥探的侵害。数学和软件是算法交易的两条腿。本书用到了一定程度的数学知识,使其对各......一起来看看 《算法交易:制胜策略与原理》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器