腾讯云 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 服务器调度 + 业务机只运行镜像”的模式:
deploy服务器拉取本仓库代码,构建 Docker 镜像,推送到 CCR/Harbor。deploy服务器调用腾讯云 CLB API 对目标实例摘流。deploy服务器通过 TAT 在目标业务机执行docker pull和systemctl restart hyapp-<service>。- 目标业务机只保存
/etc/hyapp/<service>/config.yaml和docker.env,不拉代码、不编译、不保存 Git 凭据。
不要让每台业务服务器自己 git pull && build。那会把代码权限、构建环境、版本一致性和回滚都扩散到业务机,且多服务发布时无法统一做 CLB 摘流。
前置条件
- 每台目标机已安装腾讯云 TAT agent,控制台能执行命令。
- 每个 Go 服务已按
deploy/standalone/systemd安装对应 systemd unit。 - 每个 Go 服务已有
/etc/hyapp/<service>/config.yaml和/etc/hyapp/<service>/docker.env。 - 每个服务至少有两台实例,CLB 后端都挂实例端口。
room-service每台机器必须有唯一node_id,advertise_addr必须是当前机器私网地址,例如10.2.1.4:13001,不能写内网 CLB。- 每个
config.yaml的mysql_dsn、redis_addr、MQ endpoint/topic/group 指向腾讯云托管资源的私网地址。 - 发布机安装腾讯云 Python SDK 和 YAML 解析依赖:
python3 -m pip install tencentcloud-sdk-python PyYAML
凭据通过环境变量提供:
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 上安装基础工具:
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
拉取本仓库:
cd /opt/deploy/sources
git clone <YOUR_GIT_REPO_URL> hyapp-server
cd hyapp-server
准备 inventory:
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 配置,通常是:
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;生产渲染前运行校验,避免线上漏字段:
python3 deploy/tencent-tat/validate_rendered_configs.py \
--inventory deploy/tencent-tat/inventory.prod.example.json
如果已经渲染了生产最终配置,还要校验每台机器的最终 YAML:
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 服务器只负责调度发布。
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:
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 当恢复事实来源。
常用命令
先看计划,不执行远端动作:
python3 deploy/tencent-tat/hyapp_tat_deploy.py plan \
--inventory deploy/tencent-tat/inventory.prod.json \
--services gateway-service,room-service
用 TAT 连接目标机并自动发现真实部署状态和 CLB 绑定:
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/端口反查负载均衡绑定。
单服务重启:
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
多服务重启:
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
单服务更新部署:
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
多服务更新部署:
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 不同,可以显式传镜像:
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:
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
无感更新顺序
对每个服务实例,脚本固定执行:
- 读取该实例当前 CLB 绑定和原权重。
- 将该实例在所有匹配 listener/rule 下的权重改为
0。 - 等待
drain_seconds,让 CLB 停止分配新请求。 - 通过 TAT 在目标机更新
docker.env的IMAGE,执行docker pull。 systemctl restart hyapp-<service>,Docker 发送SIGTERM,服务进入 graceful shutdown。- 等待 systemd active 和 Docker health 恢复。
- 恢复该实例原 CLB 权重。
- 等待
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 目录里执行本仓库部署命令。