概述
对大多数互联网应用来说,应用是没有状态的,所以不停服更新相对较容易实现,我们有非常多带状态服务,这里对不停服更新的实现做一些记录。
实现拆解
完整启动
在停机更新时,此组件开放的粒度是整个平台,不停服更新时,开放的粒度是单个组件进程,组件对外之前,必须确保内部已经初始化完毕。
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;
服务发现优雅关闭
参考启动,关闭顺序是:
- 摘除服务注册信息。
- 等待在途包消失加一小段时间。
长连接优雅关闭
长连接是状态的一种,需要处理流量切换。
切流控制命令字
断开参考了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半关闭,在一方关闭请求后,还可能需要回复返回包。
服务发现接口
只要接入了切流控制命令字,无需接入服务发现接口,也可以做到长连接优雅关闭。
服务发现接口有助于:
- 负载均衡。
- 容灾,可以快速摘除故障节点。
用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还是很合适的。
总结
这里对前段时间所做的不停服更新专项的主要思想做了一些总结。实际落地的情况比本文总结的要繁复一些,这里就不讨论细节了。
不停服更新,技术上不难,难的是保持内部接口、数据的兼容性。在内部接口、数据保持兼容的情况下,我们可以使用不停服更新,从而降低停服更新的频率。
参考
- https://kubernetes.io/docs/concepts/services-networking/servi…
- https://datatracker.ietf.org/doc/html/rfc7540#section-6.8
- https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecy…
- https://www.erlang.org/doc/design_principles/release_handling…
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
调用打开程序,或软件图片音乐等等: using namespace System::Diagnostics; openFileDialog1->ShowDialog(); Process::Start(openFileDialog1->FileNa…