谈谈不停服更新

概述

对大多数互联网应用来说,应用是没有状态的,所以不停服更新相对较容易实现,我们有非常多带状态服务,这里对不停服更新的实现做一些记录。

实现拆解

完整启动

在停机更新时,此组件开放的粒度是整个平台,不停服更新时,开放的粒度是单个组件进程,组件对外之前,必须确保内部已经初始化完毕。

K8S探针

K8S定义了一些探针,其中和不停服相关的,主要是就绪探针,和启动探针,它们定义了应用在什么时候算是准备好(业务流量什么时候进入节点):

  • 就绪探针 (Readiness),循环调用,决定服务发现,有没有流量进来(包括INGRESS和DNS,DNS几乎是实时的,当节点滚动更新开始后,到节点就绪探针返回成功前,DNS无法发现对应节点,推测INGRESS也是依赖DNS,所以,同样部署在K8S上的HTTP服务不需要额外增加SLB)。
  • 存活探针 (Liveness),循环调用,失败重启容器。
  • 启动探针 (Startup),先于就绪和存活探针调用,只成功调用一次,可以用于预热。

erlang进程启动、关闭顺序梳理

  • 注意启动、关闭顺序,让内部服务先开启,入口后(如对外端口开启,服务发现注册)开启,被依赖项先于依赖项启动。
  • 如果使用异步加载,可以使用K8S Startup 探针。

优雅关闭

http短连接优雅关闭

节点关闭时,DNS会马上移除,考虑在途包,stop脚本需要sleep一小段时间(5秒)。
nginx 可以设置一个等待时间。

worker_shutdown_timeout 10;

服务发现优雅关闭

参考启动,关闭顺序是:

  1. 摘除服务注册信息。
  2. 等待在途包消失加一小段时间。

长连接优雅关闭

长连接是状态的一种,需要处理流量切换。

切流控制命令字

断开参考了HTTP2的GOAWAY指令。
至少有两条对等的通道,在连接关闭时,用其它连接进行调用。

一个正常的关闭流程:

sequenceDiagram
    server->>+server: 停止端口监听,避免有新连接创建。
    server->>+client1: RPC1
    client1->>+server: RPC1 RESP
    server->>+server: 确保己方所有连接不再有扇出的新请求,逐个发送GOAWAY。
    server->>+client1: GOAWAY
    server->>+client2: GOAWAY
    client1->>client1: 切流
    client1->>server: GOAWAY
    server->>+server: client1连接已经发送&接收GOAWAY,可以马上关闭。
    server->>+server: 等待己方所有连接断开,虽然client2连接未断开,在一段时间后强制关闭应用。

注意不能使用TCP半关闭,在一方关闭请求后,还可能需要回复返回包。

服务发现接口

只要接入了切流控制命令字,无需接入服务发现接口,也可以做到长连接优雅关闭。

服务发现接口有助于:

  1. 负载均衡。
  2. 容灾,可以快速摘除故障节点。

用k8s原生的dns是可以通过轮询感知节点变化的,因为我们还有swarm部署方式,故用etcd实现了类似于k8s endpoints 的 long pooling 服务发现接口:

状态迁移

对有状态的节点,需要多做一些处理:在关闭前,需要先将状态迁移走。
此前我实现了一个有中心节点管理的一致性哈希集群库,提供了声明式的rebalance_cluster接口:

rebalance_cluster("cluster1", ["node1", "node2"])

这里参考了K8S的节点状态,核心思想是:让应用上报自己的状态到中心节点,让中心节点用rebalance_cluster接口摘除下线(terminating)节点,并将running节点自动加入到集群中。对长时间不上报的节点,认为是unknown状态,会做摘除处理。

长时间运行任务

视任务运行时常而定,可以选择:

  • 等待任务执行完毕,需要关注节点最大退出时间。
  • 将大任务拆分成小任务,写入外存,分而治之。

其它

erlang的热更为什么不行?
erlang的application-upgrade-file的粒度过小了。以一个简单的无状态组件为例,如果用K8S做热更新,程序员心智负担较小,只需要保持接口、数据向前兼容,就可以将节点灰度上线。而erlang的application-upgrade-file,程序员要考虑load module的顺序,操纵内存数据等,心智负担明显更大。
当然erlang application-upgrade-file做在线hot fix还是很合适的。

总结

这里对前段时间所做的不停服更新专项的主要思想做了一些总结。实际落地的情况比本文总结的要繁复一些,这里就不讨论细节了。
不停服更新,技术上不难,难的是保持内部接口、数据的兼容性。在内部接口、数据保持兼容的情况下,我们可以使用不停服更新,从而降低停服更新的频率。

参考

你可能感兴趣的:(后端热更新)