Skip to content

Commit

Permalink
feat: add rolling update post
Browse files Browse the repository at this point in the history
  • Loading branch information
zeromake committed Sep 21, 2021
1 parent 58921da commit f59e8b7
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -16,3 +16,4 @@ database.js
!/app/config/config.default.ts
!/app/config/config.unittest.ts
hugo
hugo.exe
1 change: 1 addition & 0 deletions Makefile
@@ -1,4 +1,5 @@
hugo:
curl -fLo hugo https://download.fastgit.org/zeromake/hugo/releases/download/v0.84.0/hugo_linux_amd64 && chmod +x hugo
# curl -fLo hugo.exe https://download.fastgit.org/zeromake/hugo/releases/download/v0.84.0/hugo_windows_amd64.exe
build: hugo ## Build the non-production site, which adds noindex headers to prevent indexing
./hugo --enableGitInfo --gc --minify
81 changes: 81 additions & 0 deletions content/post/stateful-rolling-update.md
@@ -0,0 +1,81 @@
---
title: 有状态服务的滚动更新
date: 2021-09-21 12:25:00 +08:00
tags:
- devops
- stateful
- rolling-update
- service
- kubernetes
lastmod: 2021-09-21 16:05:00 +08:00
categories:
- devops
slug: stateful-rolling-update
draft: false
---

## 前言

作为后端我们时常遇到服务的更新,而作为 `http` 服务的后端则一般是不用考虑这件事的,因为 `http` 服务的必须是无状态的,只需要在服务前加一个负载均衡就可以做到轻松的滚动更新,让用户无感知更新。但是我现在的工作的服务上包含的有状态的情况,但是更新又是必须的。

<!--more-->

## 一、简单的构架介绍

```text
+------------------------+ +-------------+
| +---------+ | | client |
| | room | | /-> |
| | | |<------- /ws-- +-------------+
| +---------+ | \---rpc call---- +------------+<--
| +---------+ | \------- | +-------------+
| | room | game | | api <----ws----> client |
| | | | -----> <\ | |
| +---------+ | mq broadcast----/ +------------+ --\ +-------------+
| +---------+ | ----------/ ws
| | | -----/ --\ +-------------+
| | room | | -> client |
| +---------+ | | |
+------------------------+ +-------------+
```

这里不具体介绍我公司的游戏服务框架,用一个简单的模型来描述一下,`api``game` 都是需要支持多实例部署的,`api` 是连接客户端的实例,然后进入某个匹配房间后 `api` 会和 `game` 通信以对房间进行操作 `game` 则使用 `mq` 的订阅方式通知所有在对应房间的客户端进行推送消息,这里主要的问题在于用户进入房间后是有状态的,`api``game` 服务里都有着对应的 `room` 信息和游戏过程信息,这些都不好切换到 `redis` 这种地方去。

## 二、方案和思考

滚动更新需要的是:

1. 服务必须是支持多实例运行的(用于新旧实例共存)。
2. 服务健康检查是必须有的。
3. 负载均衡必须支持滚动更新的流量切换。

### 2.1 去状态

迁移服务里的状态到 `redis` 这种地方,类似于 `http``session` 方案,但是由于切换到 `redis` 之前的设计是在内存中快速访问,增加了故障点,并且也会增加用户操作时的对 `redis` 的操作,而且内存与 `redis` 的同步也很麻烦,而且需要修改大量的逻辑。


### 2.2 等待到状态结束

即让用户把这次游戏结束,再退出服务,这种方案有好处就是不用处理任何状态转移,对原有逻辑几乎没有什么改动,但是又回引入其他问题,那就是什么时候才能退出该服务,而且也不能无限等待,这里我选择这种方式。

## 三、实现方式

### 3.1 去状态

这个去状态实际上只是将一部分的复杂性转移到了另一个系统上,获取倒是很简单和 `http``session` 一样的做法就行了,但是更新会很麻烦需要入侵到所有操作状态的地方,但是也就这么多了。


### 3.2 等待到状态结束

1. 需要捕捉退出信号 `SIGTERM`, `SIGINT`
1. 拒绝所有新连接,防止有负载均衡的流量跑到这个服务节点上。
1. 并手动处理各个连接的退出和等待状态已经不需要时断开。
1. 由于保持状态是两个服务,所以 `game` 服也要做相同的处理,并通知客户端。
1. 由于这里是通过信号后等待所以需要让滚动更新管理器去等待。
1. 例如 `k8s` 需要设置 `spec.template.spec.terminationGracePeriodSeconds` 为足够的长否则会直接发送 `SIGKILL` 信号。


## 参考

- [【k8s】Pod-terminationGracePeriodSeconds](https://www.cnblogs.com/jiangbo44/p/14588956.html)
- [Performing a Rolling Update](https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/)

0 comments on commit f59e8b7

Please sign in to comment.