308 lines
14 KiB
Markdown
308 lines
14 KiB
Markdown
# 腾讯云 TAT 滚动部署方案
|
||
|
||
本方案只部署本仓库 Go 微服务,不接管同机已有 Java 项目。远端连接使用腾讯云 TAT,发布时只操作 `hyapp-*` systemd unit、`hyapp-*` Docker 容器和 `/etc/hyapp/<service>/docker.env`。
|
||
|
||
MySQL、Redis、MQ 都按腾讯云托管资源处理。本方案不会在这些 CVM 上部署、重启或迁移本地 MySQL/Redis/MQ;服务配置只需要指向腾讯云托管实例的内网地址。
|
||
|
||
## 机器拓扑
|
||
|
||
`deploy` 机器是发布控制面,不承载业务流量:
|
||
|
||
| 机器 | 实例 ID | 公网 IP | 私网 IP | 角色 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `deploy` | `ns-ntmpcd3a` | `43.164.75.199` | `10.2.1.3` | 构建镜像、推送镜像、调用 TAT/CLB API 调度发布 |
|
||
|
||
当前机器按截图落到以下服务:
|
||
|
||
| 机器 | 实例 ID | 私网 IP | 本仓库服务 |
|
||
| --- | --- | --- | --- |
|
||
| `gateway-1` | `ins-aeyaoj40` | `10.2.1.8` | `gateway-service` |
|
||
| `gateway-2` | `ins-irvi5b7u` | `10.2.2.17` | `gateway-service` |
|
||
| `new-app-1` | `ins-fi5zufpk` | `10.2.1.4` | `room-service`, `user-service` |
|
||
| `new-app-2` | `ins-hwhaoe8c` | `10.2.1.13` | `room-service`, `user-service` |
|
||
| `new-game-1` | `ins-ir229jtw` | `10.2.1.10` | `game-service` |
|
||
| `new-game-2` | `ins-460rw7gu` | `10.2.1.6` | `game-service` |
|
||
| `new-app-activity-1` | `ins-n8wut6n8` | `10.2.1.11` | `activity-service`, `notice-service` |
|
||
| `new-app-activity-2` | `ins-d1n303sa` | `10.2.1.7` | `activity-service`, `notice-service` |
|
||
| `pay-1` | `ins-ceqfcxd2` | `10.2.11.14` | `wallet-service` |
|
||
| `pay-2` | `ins-awhb74q6` | `10.2.12.5` | `wallet-service` |
|
||
|
||
如果 `new-app-*` 只部署 `room-service` 或只部署 `user-service`,直接删掉 `inventory` 中对应服务的 host 绑定,不要让脚本猜。
|
||
|
||
## 部署模式
|
||
|
||
采用“deploy 服务器调度 + 业务机只运行镜像”的模式:
|
||
|
||
1. `deploy` 服务器拉取本仓库代码,构建 Docker 镜像,推送到 CCR/Harbor。
|
||
2. `deploy` 服务器调用腾讯云 CLB API 对目标实例摘流。
|
||
3. `deploy` 服务器通过 TAT 在目标业务机执行 `docker pull` 和 `systemctl restart hyapp-<service>`。
|
||
4. 目标业务机只保存 `/etc/hyapp/<service>/config.yaml` 和 `docker.env`,不拉代码、不编译、不保存 Git 凭据。
|
||
|
||
不要让每台业务服务器自己 `git pull && build`。那会把代码权限、构建环境、版本一致性和回滚都扩散到业务机,且多服务发布时无法统一做 CLB 摘流。
|
||
|
||
## 前置条件
|
||
|
||
1. 每台目标机已安装腾讯云 TAT agent,控制台能执行命令。
|
||
2. 每个 Go 服务已按 `deploy/standalone/systemd` 安装对应 systemd unit。
|
||
3. 每个 Go 服务已有 `/etc/hyapp/<service>/config.yaml` 和 `/etc/hyapp/<service>/docker.env`。
|
||
4. 每个服务至少有两台实例,CLB 后端都挂实例端口。
|
||
5. `room-service` 每台机器必须有唯一 `node_id`,`advertise_addr` 必须是当前机器私网地址,例如 `10.2.1.4:13001`,不能写内网 CLB。
|
||
6. 每个 `config.yaml` 的 `mysql_dsn`、`redis_addr`、MQ endpoint/topic/group 指向腾讯云托管资源的私网地址。
|
||
7. 发布机安装腾讯云 Python SDK 和 YAML 解析依赖:
|
||
|
||
```bash
|
||
python3 -m pip install tencentcloud-sdk-python PyYAML
|
||
```
|
||
|
||
凭据通过环境变量提供:
|
||
|
||
```bash
|
||
export TENCENTCLOUD_SECRET_ID="..."
|
||
export TENCENTCLOUD_SECRET_KEY="..."
|
||
export TENCENTCLOUD_REGION="me-saudi-arabia"
|
||
```
|
||
|
||
如果继续沿用 `hy-app-monitor`,可以把这些变量放在 deploy 机器的 `/opt/hy-app-monitor/.env`,运行脚本时传 `--env-file /opt/hy-app-monitor/.env`。本地开发机路径 `/Users/hy/Documents/hy/hy-app-monitor/.env` 只用于本机调试。
|
||
|
||
## Deploy 服务器初始化
|
||
|
||
在 `deploy / 10.2.1.3` 上安装基础工具:
|
||
|
||
```bash
|
||
sudo mkdir -p /opt/deploy/sources /opt/deploy/inventory
|
||
sudo yum install -y git docker python3 python3-pip || sudo apt-get update && sudo apt-get install -y git docker.io python3 python3-pip
|
||
sudo systemctl enable --now docker
|
||
python3 -m pip install --user tencentcloud-sdk-python PyYAML
|
||
```
|
||
|
||
拉取本仓库:
|
||
|
||
```bash
|
||
cd /opt/deploy/sources
|
||
git clone <YOUR_GIT_REPO_URL> hyapp-server
|
||
cd hyapp-server
|
||
```
|
||
|
||
准备 inventory:
|
||
|
||
```bash
|
||
cp deploy/tencent-tat/inventory.prod.example.json /opt/deploy/inventory/hyapp.prod.json
|
||
vi /opt/deploy/inventory/hyapp.prod.json
|
||
```
|
||
|
||
`inventory` 里的 `registry` 必须和实际镜像仓库一致。如果使用 `hy-app-monitor` 里的 Harbor 配置,通常是:
|
||
|
||
```text
|
||
registry = ${HARBOR_REGISTRY}/${HARBOR_PROJECT}
|
||
```
|
||
|
||
业务配置放到各目标机 `/etc/hyapp/<service>/config.yaml`。腾讯云 MySQL、Redis、MQ、JWT、IM/RTC/COS 等密钥不要提交到 Git。
|
||
|
||
配置字段契约以 `services/<service>/configs/config.tencent.example.yaml` 为准。开发时如果在本地 `config.yaml` 或 `config.docker.yaml` 新增字段,必须同时补到 `config.tencent.example.yaml`;生产渲染前运行校验,避免线上漏字段:
|
||
|
||
```bash
|
||
python3 deploy/tencent-tat/validate_rendered_configs.py \
|
||
--inventory deploy/tencent-tat/inventory.prod.example.json
|
||
```
|
||
|
||
如果已经渲染了生产最终配置,还要校验每台机器的最终 YAML:
|
||
|
||
```bash
|
||
python3 deploy/tencent-tat/validate_rendered_configs.py \
|
||
--inventory deploy/tencent-tat/inventory.prod.example.json \
|
||
--rendered-dir /tmp/hyapp-rendered-prod
|
||
```
|
||
|
||
该脚本只比较 YAML key,不打印配置值,避免把生产密钥带到日志里。
|
||
|
||
## 构建并推送镜像
|
||
|
||
在 `deploy` 服务器上执行。2 核 2GiB 机器可以作为调度器;如果全量构建 OOM 或太慢,把构建放到 CI,deploy 服务器只负责调度发布。
|
||
|
||
```bash
|
||
cd /opt/deploy/sources/hyapp-server
|
||
TAG="$(date -u +%Y%m%d%H%M%S)-$(git rev-parse --short HEAD)"
|
||
REGISTRY="10.2.1.3:18082/hyapp"
|
||
|
||
docker build -t "$REGISTRY/gateway-service:$TAG" -f services/gateway-service/Dockerfile .
|
||
docker build -t "$REGISTRY/room-service:$TAG" -f services/room-service/Dockerfile .
|
||
docker build -t "$REGISTRY/user-service:$TAG" -f services/user-service/Dockerfile .
|
||
docker build -t "$REGISTRY/wallet-service:$TAG" -f services/wallet-service/Dockerfile .
|
||
docker build -t "$REGISTRY/activity-service:$TAG" -f services/activity-service/Dockerfile .
|
||
docker build -t "$REGISTRY/game-service:$TAG" -f services/game-service/Dockerfile .
|
||
docker build -t "$REGISTRY/notice-service:$TAG" -f services/notice-service/Dockerfile .
|
||
|
||
docker push "$REGISTRY/gateway-service:$TAG"
|
||
docker push "$REGISTRY/room-service:$TAG"
|
||
docker push "$REGISTRY/user-service:$TAG"
|
||
docker push "$REGISTRY/wallet-service:$TAG"
|
||
docker push "$REGISTRY/activity-service:$TAG"
|
||
docker push "$REGISTRY/game-service:$TAG"
|
||
docker push "$REGISTRY/notice-service:$TAG"
|
||
```
|
||
|
||
如果实际用 Harbor,把 `REGISTRY` 换成 `${HARBOR_REGISTRY}/${HARBOR_PROJECT}`,并先 `docker login`。
|
||
|
||
## 配置 inventory
|
||
|
||
复制模板后填真实 CLB:
|
||
|
||
```bash
|
||
cp deploy/tencent-tat/inventory.prod.example.json deploy/tencent-tat/inventory.prod.json
|
||
vi deploy/tencent-tat/inventory.prod.json
|
||
```
|
||
|
||
必须替换:
|
||
|
||
- `gateway-service.clb.load_balancer_id`:公网 gateway CLB。
|
||
- `gateway-service.clb.listener_ids`:公网 HTTP/HTTPS 监听器。
|
||
- 内部服务各自的 `load_balancer_id` 和 `listener_ids`:room/user/wallet/activity/game 内网 CLB。
|
||
|
||
脚本通过实例 ID、私网 IP、端口三者匹配 CLB target。发布前会把当前实例在相关 listener 下的权重改成 `0`,服务健康后恢复原权重。
|
||
|
||
`managed_dependencies` 只记录部署边界:腾讯云 MySQL/Redis/MQ 是外部托管依赖,不纳入 TAT 发布动作。生产变更这些资源时单独走云数据库、Redis 或 MQ 的变更流程。
|
||
|
||
当前生产 inventory 已写入真实绑定:
|
||
|
||
| 服务 | CLB | 监听器 | 对外/内网入口 |
|
||
| --- | --- | --- | --- |
|
||
| `gateway-service` | `lb-cbiabr1q` | `lbl-ktvz8m6k` / rule `loc-pwedmsmm` | `https://api.global-interaction.com` -> `13000` |
|
||
| `room-service` | `lb-epvnr4o0` | `lbl-d2urlzba` | `10.2.1.16:13001` |
|
||
| `user-service` | `lb-epvnr4o0` | `lbl-honi8z3a` | `10.2.1.16:13005` |
|
||
| `activity-service` | `lb-epvnr4o0` | `lbl-j7oqtfhq` | `10.2.1.16:13006` |
|
||
| `game-service` | `lb-epvnr4o0` | `lbl-ku138b4y` | `10.2.1.16:13008` |
|
||
| `notice-service` | `lb-epvnr4o0` | `lbl-aux9xsjq` | `10.2.1.16:13009` |
|
||
| `wallet-service` | `lb-4f5xi6p0` | `lbl-9wi5mvu4` | `10.2.1.15:13004` |
|
||
|
||
Harbor 项目使用 `hyapp`,当前生产镜像前缀是 `10.2.1.3:18082/hyapp`。不要使用其他业务项目名前缀。
|
||
|
||
## 线上服务配置边界
|
||
|
||
这些机器上只部署 Go 服务进程:
|
||
|
||
- `gateway-service`:`room_service_addr/user_service_addr/wallet_service_addr/activity_service_addr/game_service_addr` 指向各服务内网 CLB/DNS。
|
||
- `room-service`:`mysql_dsn` 指向腾讯云 MySQL,`redis_addr` 指向腾讯云 Redis;`advertise_addr` 指向本机私网 IP。
|
||
- `user-service`:`mysql_dsn`、worker 里引用的 wallet/room MySQL DSN、登录风控 Redis 都指向腾讯云托管实例。
|
||
- `wallet-service`、`activity-service`、`game-service`:MySQL 和下游 gRPC 地址都使用腾讯云托管资源或内网 CLB/DNS。
|
||
- `notice-service`:`mysql_dsn` 指向 `hyapp_notice`,`wallet_database` 指向 `hyapp_wallet`;它只读取 wallet outbox 并写自己的投递位点,不修改钱包事实表。
|
||
- MQ 当前只作为 outbox/event bus 方向的外部托管依赖;本脚本不会创建 topic、consumer group 或本地 broker。
|
||
|
||
腾讯云 MySQL DSN 必须保留 `loc=UTC`。Redis 使用同一托管实例时,用 `redis_db` 或 key prefix 区分业务域,不要把 Redis 当恢复事实来源。
|
||
|
||
## 常用命令
|
||
|
||
先看计划,不执行远端动作:
|
||
|
||
```bash
|
||
python3 deploy/tencent-tat/hyapp_tat_deploy.py plan \
|
||
--inventory deploy/tencent-tat/inventory.prod.json \
|
||
--services gateway-service,room-service
|
||
```
|
||
|
||
用 TAT 连接目标机并自动发现真实部署状态和 CLB 绑定:
|
||
|
||
```bash
|
||
python3 deploy/tencent-tat/hyapp_tat_deploy.py discover \
|
||
--env-file /opt/hy-app-monitor/.env \
|
||
--inventory /opt/deploy/inventory/hyapp.prod.json \
|
||
--services gateway-service,room-service,user-service,wallet-service,activity-service,game-service,notice-service
|
||
```
|
||
|
||
`discover` 会读取每台目标机的 `hyapp-*` systemd unit、`/etc/hyapp/*/docker.env`、`hyapp-*` 容器和监听端口,并通过 CLB API 按后端私网 IP/端口反查负载均衡绑定。
|
||
|
||
单服务重启:
|
||
|
||
```bash
|
||
python3 deploy/tencent-tat/hyapp_tat_deploy.py restart \
|
||
--env-file /opt/hy-app-monitor/.env \
|
||
--inventory /opt/deploy/inventory/hyapp.prod.json \
|
||
--services wallet-service \
|
||
--yes
|
||
```
|
||
|
||
多服务重启:
|
||
|
||
```bash
|
||
python3 deploy/tencent-tat/hyapp_tat_deploy.py restart \
|
||
--env-file /opt/hy-app-monitor/.env \
|
||
--inventory /opt/deploy/inventory/hyapp.prod.json \
|
||
--services room-service,user-service,wallet-service,activity-service,game-service,notice-service \
|
||
--yes
|
||
```
|
||
|
||
单服务更新部署:
|
||
|
||
```bash
|
||
python3 deploy/tencent-tat/hyapp_tat_deploy.py deploy \
|
||
--env-file /opt/hy-app-monitor/.env \
|
||
--inventory /opt/deploy/inventory/hyapp.prod.json \
|
||
--services gateway-service \
|
||
--tag 20260513-1349 \
|
||
--yes
|
||
```
|
||
|
||
多服务更新部署:
|
||
|
||
```bash
|
||
python3 deploy/tencent-tat/hyapp_tat_deploy.py deploy \
|
||
--env-file /opt/hy-app-monitor/.env \
|
||
--inventory /opt/deploy/inventory/hyapp.prod.json \
|
||
--services gateway-service,room-service,user-service,wallet-service,activity-service,game-service,notice-service \
|
||
--tag 20260513-1349 \
|
||
--yes
|
||
```
|
||
|
||
如果某个服务镜像 tag 不同,可以显式传镜像:
|
||
|
||
```bash
|
||
python3 deploy/tencent-tat/hyapp_tat_deploy.py deploy \
|
||
--env-file /opt/hy-app-monitor/.env \
|
||
--inventory /opt/deploy/inventory/hyapp.prod.json \
|
||
--services gateway-service,wallet-service \
|
||
--image gateway-service=10.2.1.3:18082/hyapp/gateway-service:gw-20260513 \
|
||
--image wallet-service=10.2.1.3:18082/hyapp/wallet-service:pay-20260513 \
|
||
--yes
|
||
```
|
||
|
||
只维护某一台机器时使用 `--hosts`,例如只重启 `gateway-1`:
|
||
|
||
```bash
|
||
python3 deploy/tencent-tat/hyapp_tat_deploy.py restart \
|
||
--env-file /opt/hy-app-monitor/.env \
|
||
--inventory /opt/deploy/inventory/hyapp.prod.json \
|
||
--services gateway-service \
|
||
--hosts gateway-1 \
|
||
--yes
|
||
```
|
||
|
||
## 无感更新顺序
|
||
|
||
对每个服务实例,脚本固定执行:
|
||
|
||
1. 读取该实例当前 CLB 绑定和原权重。
|
||
2. 将该实例在所有匹配 listener/rule 下的权重改为 `0`。
|
||
3. 等待 `drain_seconds`,让 CLB 停止分配新请求。
|
||
4. 通过 TAT 在目标机更新 `docker.env` 的 `IMAGE`,执行 `docker pull`。
|
||
5. `systemctl restart hyapp-<service>`,Docker 发送 `SIGTERM`,服务进入 graceful shutdown。
|
||
6. 等待 systemd active 和 Docker health 恢复。
|
||
7. 恢复该实例原 CLB 权重。
|
||
8. 等待 `stabilize_seconds` 后进入下一台实例。
|
||
|
||
如果重启或健康检查失败,脚本会停止后续步骤;已经摘流的故障实例保持权重 `0`,避免坏实例重新接流,需要人工确认后再恢复。
|
||
|
||
## 用户无感边界
|
||
|
||
- 客户端长连接、房间公屏、单聊仍由腾讯云 IM 承接,gateway 重启不会断开 IM。
|
||
- gateway 是 HTTP 入口,摘流后只影响当前实例的新 HTTP 请求;另一台 gateway 继续接流。
|
||
- 内部服务通过各自内网 CLB 摘流滚动;gateway 的 gRPC client 已配置短重试,只覆盖瞬时 `UNAVAILABLE`。
|
||
- `room-service` 已有 owner route/node registry。旧 owner 停机时会释放 lease;异常退出时最多等 `lease_ttl` 到期后其他节点接管。
|
||
- 正在执行的单个 HTTP/gRPC 请求可能返回失败,客户端必须按现有接口幂等键重试;“无感”指发布期间整体入口不断流、房间 IM 不断连。
|
||
|
||
## 禁止项
|
||
|
||
- 不要使用 `--no-clb` 做正常发布;它只适合孤立排障。
|
||
- 不要多个 `room-service` 共用同一个 `node_id`。
|
||
- 不要把 `room-service.advertise_addr` 写成 CLB 地址。
|
||
- 不要先 `systemctl restart` 再摘 CLB。
|
||
- 不要在同机 Java compose 目录里执行本仓库部署命令。
|