Add lucky gift deployment support

This commit is contained in:
zhx 2026-07-08 22:30:46 +08:00
parent 35bc2696f4
commit f441fd49c0
21 changed files with 328 additions and 110 deletions

View File

@ -4,6 +4,6 @@ dist
.DS_Store .DS_Store
*.tsbuildinfo *.tsbuildinfo
npm-debug.log* npm-debug.log*
deploy/tencent-tat/inventory.prod.json deploy/tencent_tat/inventory.prod.json
deploy/tencent-tat/deploy-platform.inventory.prod.json deploy/tencent_tat/deploy-platform.inventory.prod.json
deploy/tencent-tat/hyapp-services.inventory.prod.json deploy/tencent_tat/hyapp-services.inventory.prod.json

View File

@ -36,14 +36,20 @@
## 生产特殊服务 ## 生产特殊服务
1. `admin-server` 不在标准 prod inventory 的滚动服务清单里;它部署在 Saudi 机器 `43.164.70.49` / `10.2.1.2`,实例 `ins-p11begwe` 1. `admin-server` 不在标准 prod inventory 的滚动服务清单里;它部署在 Saudi `admin-platform` 机器 `ins-p11begwe`,公网 `43.164.70.49`,内网 `10.2.1.2`
2. `admin-server` 的 systemd 是 `hyapp-admin-server`,容器是 `hyapp-admin-server`,镜像是 `10.2.1.3:18082/hyapp/admin-server:<tag>`,配置是 `/etc/hyapp/admin-server/config.yaml`Docker env 是 `/etc/hyapp/admin-server/docker.env`,监听 `10.2.1.2:13100` 2. `admin-server` 的 systemd 是 `hyapp-admin-server`,容器是 `hyapp-admin-server`,配置是 `/etc/hyapp/admin-server/config.yaml`Docker env 是 `/etc/hyapp/admin-server/docker.env`migration 目录是 `/opt/hyapp/admin-server/migrations`,监听 `10.2.1.2:13100`
3. 更新 `admin-server` 时,由 deploy 机器 `43.164.75.199``/opt/deploy/sources/hyapp-server` 拉取 Gitea `git@gitea.haiyihy.com:hy/hyapp-server.git``origin/main`,构建并推送 `10.2.1.3:18082/hyapp/admin-server:<tag>`,再通过 Tencent TAT 到 `ins-p11begwe` 修改 docker env 的 `IMAGE` 后重启 `hyapp-admin-server`。线上只允许发布 `origin/main`;发布后必须验证容器镜像 tag、systemd `active``13100` 监听和 `healthz/readyz``hyapp_admin_server_deploy.py` 是旧广州机器二进制发布脚本,不适用于当前生产 `admin-server` 3. 更新 `admin-server` 必须只发布 `origin/main`deploy 机从 Gitea 拉取 `git@gitea.haiyihy.com:hy/hyapp-server.git``origin/main`,构建并推送 `10.2.1.3:18082/hyapp/admin-server:<tag>`,再通过 Tencent TAT 到 `ins-p11begwe` 修改 `/etc/hyapp/admin-server/docker.env``IMAGE` 后重启 `hyapp-admin-server`
4. HyApp `cron-service` 当前部署在 Saudi 主服务机器 `new-app-1`,实例 `ins-fi5zufpk`,内网 `10.2.1.4` 4. `admin-platform` 机器已经配置 Docker 直拉内网 Harbor`/etc/docker/daemon.json` 包含 insecure registry `10.2.1.3:18082``/root/.docker/config.json` 包含 Harbor 登录凭据;如果 `docker pull 10.2.1.3:18082/...` 失败,优先检查这两处。
5. `cron-service` 的 systemd 是 `hyapp-cron-service`,容器是 `hyapp-cron-service`,配置是 `/etc/hyapp/cron-service/config.yaml`Docker env 是 `/etc/hyapp/cron-service/docker.env`,监听 `13007` 5. 更新 `admin-server` 后必须验证:`systemctl is-active hyapp-admin-server`、容器镜像 tag/digest、`ss -lntp | grep 13100``curl http://10.2.1.2:13100/healthz``curl http://10.2.1.2:13100/readyz`、Caddy 容器 `yumi-admin-caddy` 运行、公网 `https://admin-acc.global-interaction.com/` 返回 `200`、未登录 `https://admin-acc.global-interaction.com/api/v1/auth/me` 返回 `401`
6. `cron-service` 当前不在标准 prod inventory 的 `services` 清单里;更新时构建并推送 `10.2.1.3:18082/hyapp/cron-service:<tag>`,通过 Tencent TAT 到 `new-app-1` 修改 docker env 的 `IMAGE` 后重启 `hyapp-cron-service` 6. Saudi `admin-platform` 机器同时承接两个 5 秒轮询的前端 watcher`chatapp3-admin-watch.service``hyapp-admin-platform-watch.service`。它们只在远端 commit 变化或静态目录缺失时构建,不应每 5 秒重复 build。
7. 更新 `cron-service` 后必须验证镜像 tag、Docker health、systemd `active``13007` 监听。 7. `chatapp3-admin-watch.service` 使用 `/opt/chatapp3-admin/bin/watch-build.sh`,部署脚本是 `/opt/chatapp3-admin/bin/deploy-once.sh`,仓库是 `git@gitea.haiyihy.com:hy/chatapp3-admin.git``main`,构建后写入 `/opt/yumi-admin/frontend-admin/site/yumiadmin-acc`,公网入口是 `https://yumiadmin-acc.global-interaction.com`。前端 API 配置必须是 `https://yumiadmin-acc.global-interaction.com/console`Caddy 的 `/console/*` 通过 Saudi 内网 CLB `https://10.2.1.12` 回源,并保持上游 Host/SNI 为 `jvapi.haiyihy.com`
8. 腾讯云 `ap-singapore` 的实例名 `cron` 机器是 `ins-nt6bsay0` / `10.3.1.14`;当前没有 HyApp `hyapp-cron-service`,不要把它当成 HyApp `cron-service` 发布目标。 8. `hyapp-admin-platform-watch.service` 使用 `/opt/hyapp-admin-platform/bin/watch-build.sh`,部署脚本是 `/opt/hyapp-admin-platform/bin/deploy-once.sh`,仓库是 `https://gitea.haiyihy.com/hy/hyapp-admin-platform.git``main`,构建后写入 `/opt/yumi-admin/frontend-admin/site/hyapp-admin-platform`,公网入口是 `https://admin-acc.global-interaction.com`。前端 API 配置必须保持同源 `/api`Caddy 的 `/api/*` 回源到 `10.2.1.2:13100`
9. 更新或排查这两个 Saudi 前端 watcher 后必须验证:`systemctl is-active chatapp3-admin-watch hyapp-admin-platform-watch`、对应 `.last_built_commit` / `.deployed_commit``journalctl -u ...``/opt/*/logs/deploy.log`,以及公网首页 `200`、未登录 API `401`
10. HyApp `cron-service` 当前部署在 Saudi 主服务机器 `new-app-1`,实例 `ins-fi5zufpk`,内网 `10.2.1.4`
11. `cron-service` 的 systemd 是 `hyapp-cron-service`,容器是 `hyapp-cron-service`,配置是 `/etc/hyapp/cron-service/config.yaml`Docker env 是 `/etc/hyapp/cron-service/docker.env`,监听 `13007`
12. `cron-service` 当前不在标准 prod inventory 的 `services` 清单里;更新时构建并推送 `10.2.1.3:18082/hyapp/cron-service:<tag>`,通过 Tencent TAT 到 `new-app-1` 修改 docker env 的 `IMAGE` 后重启 `hyapp-cron-service`
13. 更新 `cron-service` 后必须验证镜像 tag、Docker health、systemd `active``13007` 监听。
14. 腾讯云 `ap-singapore` 的实例名 `cron` 机器是 `ins-nt6bsay0` / `10.3.1.14`;当前没有 HyApp `hyapp-cron-service`,不要把它当成 HyApp `cron-service` 发布目标。
## 测试 server / testbox ## 测试 server / testbox

View File

@ -68,7 +68,7 @@ make up
- API`POST /deploy/api/edgeone/purge-url` - API`POST /deploy/api/edgeone/purge-url`
- 类型:当前只开放 EdgeOne URL 刷新 - 类型:当前只开放 EdgeOne URL 刷新
- 默认站点:`global-interaction.com` / `zone-3qwew52ihaj2` - 默认站点:`global-interaction.com` / `zone-3qwew52ihaj2`
- 默认允许域名:`h5.global-interaction.com` - 默认允许域名:`h5.global-interaction.com``vivagames.global-interaction.com``asset.global-interaction.com`
- 参数:`urls``confirmed=true` - 参数:`urls``confirmed=true`
- 验证:接口等待 EdgeOne 任务结束后返回 `jobId``status``tasks` - 验证:接口等待 EdgeOne 任务结束后返回 `jobId``status``tasks`
- 禁止:页面刷新任意未配置域名;需要新增域名时先改 `EDGEONE_ALLOWED_HOSTS` - 禁止:页面刷新任意未配置域名;需要新增域名时先改 `EDGEONE_ALLOWED_HOSTS`
@ -79,21 +79,74 @@ make up
### admin-server ### admin-server
- 机器:公网 `43.164.70.49`,内网 `10.2.1.2` - 机器Saudi `admin-platform`CVM `ins-p11begwe`,公网 `43.164.70.49`,内网 `10.2.1.2`
- 腾讯云实例:`ins-p11begwe`
- 区域:`me-saudi-arabia`
- systemd`hyapp-admin-server` - systemd`hyapp-admin-server`
- 容器:`hyapp-admin-server` - 容器:`hyapp-admin-server`
- 镜像:`10.2.1.3:18082/hyapp/admin-server:<tag>`
- 配置:`/etc/hyapp/admin-server/config.yaml` - 配置:`/etc/hyapp/admin-server/config.yaml`
- Docker env`/etc/hyapp/admin-server/docker.env` - Docker env`/etc/hyapp/admin-server/docker.env`
- migrations`/opt/hyapp/admin-server/migrations` - migration 目录:`/opt/hyapp/admin-server/migrations`
- 镜像仓库:`10.2.1.3:18082/hyapp/admin-server:<tag>`
- 监听:`10.2.1.2:13100` - 监听:`10.2.1.2:13100`
- 验证:`systemctl is-active hyapp-admin-server``docker inspect hyapp-admin-server --format '{{.Config.Image}}'`、`ss -lntp | grep 13100``curl -fsS http://10.2.1.2:13100/healthz`、`curl -fsS http://10.2.1.2:13100/readyz` - 验证:`systemctl is-active hyapp-admin-server``docker ps --filter name=hyapp-admin-server`、`ss -lntp | grep 13100``curl http://10.2.1.2:13100/healthz`、`curl http://10.2.1.2:13100/readyz`
`admin-server` 当前不是二进制直传部署,而是和其他 HyApp 后端一样通过内网 Harbor 镜像发布。发布时先在 deploy 机器 `43.164.75.199``/opt/deploy/sources/hyapp-server` 拉取 Gitea `git@gitea.haiyihy.com:hy/hyapp-server.git``origin/main`,构建并推送 `10.2.1.3:18082/hyapp/admin-server:<tag>`,再通过 Tencent TAT 到 `ins-p11begwe` 修改 `/etc/hyapp/admin-server/docker.env` 中的 `IMAGE` 并重启 `hyapp-admin-server`。线上只允许发布 `origin/main`;发布后必须确认容器镜像 tag、systemd `active`、端口监听、`healthz/readyz` 正常 `admin-server` 更新只允许发布 `origin/main`。deploy 机拉取 Gitea `git@gitea.haiyihy.com:hy/hyapp-server.git``origin/main`,构建并推送 `10.2.1.3:18082/hyapp/admin-server:<tag>`;然后通过 Tencent TAT 到 `ins-p11begwe` 修改 `/etc/hyapp/admin-server/docker.env``IMAGE`,执行 `systemctl restart hyapp-admin-server``admin-platform` 已配置 Docker 直拉内网 Harbor`/etc/docker/daemon.json` 包含 insecure registry `10.2.1.3:18082``/root/.docker/config.json` 包含 Harbor 登录凭据
注意:`deploy/tencent_tat/hyapp_admin_server_deploy.py` 是旧广州机器 `134.175.160.86 / 172.16.0.6` 的二进制发布链路,不适用于当前 Saudi `admin-server`。除非先更新脚本目标和部署模型,否则不要用它发布生产 `admin-server` 发布后必须确认systemd `active`、容器镜像 tag/digest 正确、`10.2.1.2:13100` 监听、`healthz/readyz` 正常、Caddy 容器 `yumi-admin-caddy` 运行、`https://admin-acc.global-interaction.com/` 返回 `200`、未登录 `https://admin-acc.global-interaction.com/api/v1/auth/me` 返回 `401`
### Saudi admin-platform 前端
- 机器:公网 `43.164.70.49`,内网 `10.2.1.2`
- CVM`ins-p11begwe`
- 区域:`me-saudi-arabia`
- Caddy 容器:`yumi-admin-caddy`
- Caddy 宿主机目录:`/opt/yumi-admin/frontend-admin`
- 静态根目录:`/opt/yumi-admin/frontend-admin/site`
- Node 运行时:`/opt/node-v22.18.0-linux-x64`
这台机器承接两个加速后台前端,都是本机拉 Gitea、构建、替换 Caddy 静态目录:
| 入口 | watcher | 仓库 | 静态目录 | API 回源 |
| --- | --- | --- | --- | --- |
| `https://yumiadmin-acc.global-interaction.com` | `chatapp3-admin-watch.service` | `git@gitea.haiyihy.com:hy/chatapp3-admin.git` `main` | `/opt/yumi-admin/frontend-admin/site/yumiadmin-acc` | `/console/*` -> Saudi 内网 CLB `https://10.2.1.12`Host/SNI `jvapi.haiyihy.com` |
| `https://admin-acc.global-interaction.com` | `hyapp-admin-platform-watch.service` | `https://gitea.haiyihy.com/hy/hyapp-admin-platform.git` `main` | `/opt/yumi-admin/frontend-admin/site/hyapp-admin-platform` | `/api/*` -> `http://10.2.1.2:13100` |
两个 watcher 都是 5 秒循环,入口脚本分别是:
```bash
/opt/chatapp3-admin/bin/watch-build.sh
/opt/hyapp-admin-platform/bin/watch-build.sh
```
实际部署脚本分别是:
```bash
/opt/chatapp3-admin/bin/deploy-once.sh
/opt/hyapp-admin-platform/bin/deploy-once.sh
```
部署完成标记:
```bash
/opt/chatapp3-admin/.last_built_commit
/opt/hyapp-admin-platform/.deployed_commit
```
排查或变更后必须验证:
```bash
systemctl is-active chatapp3-admin-watch.service hyapp-admin-platform-watch.service
tail -n 100 /opt/chatapp3-admin/logs/deploy.log
tail -n 100 /opt/hyapp-admin-platform/logs/deploy.log
curl -I https://yumiadmin-acc.global-interaction.com/
curl -I https://admin-acc.global-interaction.com/
```
未登录 API 返回 `401` 是正常结果,用来证明反代已经打到后端鉴权层:
```bash
curl -sS https://yumiadmin-acc.global-interaction.com/console/
curl -sS https://admin-acc.global-interaction.com/api/v1/admin/apps
```
### cron-service ### cron-service

View File

@ -42,14 +42,14 @@ npm run deploy:serve
真实接口: 真实接口:
- `GET /deploy/api/health`:只返回凭据是否存在,不返回密钥明文。 - `GET /deploy/api/health`:只返回凭据是否存在,不返回密钥明文。
- `POST /deploy/api/edgeone/purge-url`:提交 EdgeOne URL 刷新任务,必须带 `confirmed=true`;只允许 `EDGEONE_ALLOWED_HOSTS` 中的域名,默认 `h5.global-interaction.com` - `POST /deploy/api/edgeone/purge-url`:提交 EdgeOne URL 刷新任务,必须带 `confirmed=true`;只允许 `EDGEONE_ALLOWED_HOSTS` 中的域名,默认 `h5.global-interaction.com``vivagames.global-interaction.com``asset.global-interaction.com`
- `GET /deploy/api/hyapp/inventory`:读取 HyApp 生产 inventory。 - `GET /deploy/api/hyapp/inventory`:读取 HyApp 生产 inventory。
- `GET /deploy/api/hyapp/plan?services=gateway-service,room-service`:生成发布计划,不执行远端动作。 - `GET /deploy/api/hyapp/plan?services=gateway-service,room-service`:生成发布计划,不执行远端动作。
- `GET /deploy/api/hyapp/discover?services=user-service&remote=1`:通过 Tencent TAT 连接 deploy 机器 `43.164.75.199`,再由 deploy 机器 SSH 到业务机器读取真实实例、systemd、Docker、镜像、资源、端口和 CLB 权重。 - `GET /deploy/api/hyapp/discover?services=user-service&remote=1`:通过 Tencent TAT 连接 deploy 机器 `43.164.75.199`,再由 deploy 机器 SSH 到业务机器读取真实实例、systemd、Docker、镜像、资源、端口和 CLB 权重。
- `GET /deploy/api/hyapp/discover?services=user-service&remote=1&includeConfig=1`:读取详情 YAML密钥字段脱敏。 - `GET /deploy/api/hyapp/discover?services=user-service&remote=1&includeConfig=1`:读取详情 YAML密钥字段脱敏。
- `GET /deploy/api/hyapp/resources?remote=1`数据资源状态。链路TAT -> deploy业务服务器配置 SSH 失败后 TAT 兜底。 - `GET /deploy/api/hyapp/resources?remote=1`数据资源状态。链路TAT -> deploy业务服务器配置 SSH 失败后 TAT 兜底。
- `POST /deploy/api/hyapp/deploy`:真实发布,必须带 `confirmed=true`;测试时使用 `dryRun=true` - `POST /deploy/api/hyapp/deploy`:真实发布,必须带 `confirmed=true`;测试时使用 `dryRun=true`
- `POST /deploy/api/hyapp/admin-server/deploy`历史二进制发布入口,目标是旧广州机器 `134.175.160.86 / 172.16.0.6`,不适用于当前生产 `admin-server`。当前生产 `admin-server``43.164.70.49 / 10.2.1.2`(实例 `ins-p11begwe`)以 Docker 方式运行,应构建并推送 `10.2.1.3:18082/hyapp/admin-server:<tag>`,再通过 TAT 更新 `/etc/hyapp/admin-server/docker.env``IMAGE` 并重启 `hyapp-admin-server` - `POST /deploy/api/hyapp/admin-server/deploy`生产 `admin-server` 固定发布入口,必须带 `confirmed=true`;发布只允许 `origin/main`deploy 机构建并推送 `10.2.1.3:18082/hyapp/admin-server:<tag>`,再通过 Tencent TAT 到 Saudi `admin-platform` 实例 `ins-p11begwe` 更新 `/etc/hyapp/admin-server/docker.env``IMAGE` 并重启 `hyapp-admin-server`,最后验证 `10.2.1.2:13100``healthz/readyz`、Caddy 和公网入口
- `POST /deploy/api/hyapp/restart`:真实重启,必须带 `confirmed=true`;测试时使用 `dryRun=true` - `POST /deploy/api/hyapp/restart`:真实重启,必须带 `confirmed=true`;测试时使用 `dryRun=true`
- `GET /deploy/api/hyapp/testbox/status`:读取测试机 test 分支、timer 和 Compose/systemd 服务状态。 - `GET /deploy/api/hyapp/testbox/status`:读取测试机 test 分支、timer 和 Compose/systemd 服务状态。
- `POST /deploy/api/hyapp/testbox/service`:测试机服务 `update/start/stop/restart`,必须带 `confirmed=true` - `POST /deploy/api/hyapp/testbox/service`:测试机服务 `update/start/stop/restart`,必须带 `confirmed=true`

View File

@ -12,6 +12,7 @@ GRANT ALL PRIVILEGES ON hyapp_admin.* TO 'hyapp'@'%';
GRANT ALL PRIVILEGES ON hyapp_cron.* TO 'hyapp'@'%'; GRANT ALL PRIVILEGES ON hyapp_cron.* TO 'hyapp'@'%';
GRANT ALL PRIVILEGES ON hyapp_game.* TO 'hyapp'@'%'; GRANT ALL PRIVILEGES ON hyapp_game.* TO 'hyapp'@'%';
GRANT ALL PRIVILEGES ON hyapp_notice.* TO 'hyapp'@'%'; GRANT ALL PRIVILEGES ON hyapp_notice.* TO 'hyapp'@'%';
GRANT ALL PRIVILEGES ON hyapp_robot.* TO 'hyapp'@'%';
GRANT ALL PRIVILEGES ON hyapp_statistics.* TO 'hyapp'@'%'; GRANT ALL PRIVILEGES ON hyapp_statistics.* TO 'hyapp'@'%';
FLUSH PRIVILEGES; FLUSH PRIVILEGES;

View File

@ -6,4 +6,7 @@ fileReservedTime = 48
brokerRole = ASYNC_MASTER brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH flushDiskType = ASYNC_FLUSH
autoCreateTopicEnable = true autoCreateTopicEnable = true
# Docker Desktop 上宿主机二进制通过 NameServer 拿到 broker 地址后不能访问容器内网 IP
# 本地调试默认跑宿主机 Go 服务,所以广播 compose 映射到宿主机的 broker 端口。
brokerIP1 = 127.0.0.1
listenPort = 10911 listenPort = 10911

View File

@ -27,7 +27,8 @@ const tencentEnvFile = process.env.TENCENT_ENV_FILE || "";
const credentialFile = resolve(process.env.TENCENT_CREDENTIALS_FILE || join(rootDir, ".env")); const credentialFile = resolve(process.env.TENCENT_CREDENTIALS_FILE || join(rootDir, ".env"));
const deployEnv = { ...process.env }; const deployEnv = { ...process.env };
const edgeoneConfig = { const edgeoneConfig = {
allowedHosts: process.env.EDGEONE_ALLOWED_HOSTS || "h5.global-interaction.com", allowedHosts:
process.env.EDGEONE_ALLOWED_HOSTS || "h5.global-interaction.com,vivagames.global-interaction.com,asset.global-interaction.com",
zoneId: process.env.EDGEONE_ZONE_ID || "zone-3qwew52ihaj2", zoneId: process.env.EDGEONE_ZONE_ID || "zone-3qwew52ihaj2",
zoneName: process.env.EDGEONE_ZONE_NAME || "global-interaction.com", zoneName: process.env.EDGEONE_ZONE_NAME || "global-interaction.com",
}; };

View File

@ -3,7 +3,7 @@ CONTAINER_NAME=hyapp-deploy-platform
STOP_TIMEOUT_SEC=15 STOP_TIMEOUT_SEC=15
EDGEONE_ZONE_ID=zone-3qwew52ihaj2 EDGEONE_ZONE_ID=zone-3qwew52ihaj2
EDGEONE_ZONE_NAME=global-interaction.com EDGEONE_ZONE_NAME=global-interaction.com
EDGEONE_ALLOWED_HOSTS=h5.global-interaction.com EDGEONE_ALLOWED_HOSTS=h5.global-interaction.com,vivagames.global-interaction.com,asset.global-interaction.com
DEPLOY_ADMIN_SERVER_COMMAND_TIMEOUT_MS=1800000 DEPLOY_ADMIN_SERVER_COMMAND_TIMEOUT_MS=1800000
# Tencent Cloud credentials are required for remote TAT queries and EdgeOne refresh. # Tencent Cloud credentials are required for remote TAT queries and EdgeOne refresh.

View File

@ -0,0 +1,7 @@
# luck-gateway is intentionally env-only: it exposes the simplified external lucky gift HTTP entry and delegates all rules to lucky-gift-service.
IMAGE=10.2.1.3:18082/hyapp/luck-gateway:REPLACE_WITH_RELEASE_TAG
CONTAINER_NAME=hyapp-luck-gateway
STOP_TIMEOUT_SEC=60
LUCK_GATEWAY_HTTP_ADDR=:13014
LUCKY_GIFT_GRPC_ADDR=lucky-gift-service.internal:13013
LUCK_GATEWAY_ALLOWED_APPS=aslan,yumi

View File

@ -0,0 +1,5 @@
# Image is immutable per release; TAT deploy rewrites only IMAGE before restarting systemd.
IMAGE=10.2.1.3:18082/hyapp/lucky-gift-service:REPLACE_WITH_RELEASE_TAG
CONTAINER_NAME=hyapp-lucky-gift-service
CONFIG_PATH=/etc/hyapp/lucky-gift-service/config.yaml
STOP_TIMEOUT_SEC=90

View File

@ -0,0 +1,4 @@
IMAGE=10.2.1.3:18082/hyapp/robot-service:REPLACE_WITH_RELEASE_TAG
CONTAINER_NAME=hyapp-robot-service
CONFIG_PATH=/etc/hyapp/robot-service/config.yaml
STOP_TIMEOUT_SEC=60

View File

@ -0,0 +1,23 @@
[Unit]
Description=Hyapp luck-gateway container
Requires=docker.service
After=network-online.target docker.service
Wants=network-online.target
StartLimitIntervalSec=120
StartLimitBurst=3
[Service]
Type=simple
EnvironmentFile=/etc/hyapp/luck-gateway/docker.env
ExecStartPre=-/usr/bin/env docker rm -f ${CONTAINER_NAME}
ExecStart=/usr/bin/env docker run --name=${CONTAINER_NAME} --rm --network=host --init --pull=never --stop-signal=SIGTERM --stop-timeout=${STOP_TIMEOUT_SEC} --env-file=/etc/hyapp/luck-gateway/docker.env --env=TZ=UTC --log-driver=json-file --log-opt=max-size=100m --log-opt=max-file=5 --health-cmd="wget -q -O - http://127.0.0.1:13014/healthz/ready >/dev/null" --health-interval=5s --health-timeout=3s --health-retries=6 --health-start-period=10s ${IMAGE}
ExecStop=/usr/bin/env docker stop --time=${STOP_TIMEOUT_SEC} ${CONTAINER_NAME}
Restart=on-failure
RestartSec=5s
TimeoutStartSec=60
TimeoutStopSec=90
KillMode=none
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,23 @@
[Unit]
Description=Hyapp lucky-gift-service container
Requires=docker.service
After=network-online.target docker.service
Wants=network-online.target
StartLimitIntervalSec=120
StartLimitBurst=3
[Service]
Type=simple
EnvironmentFile=/etc/hyapp/lucky-gift-service/docker.env
ExecStartPre=-/usr/bin/env docker rm -f ${CONTAINER_NAME}
ExecStart=/usr/bin/env docker run --name=${CONTAINER_NAME} --rm --network=host --init --pull=never --stop-signal=SIGTERM --stop-timeout=${STOP_TIMEOUT_SEC} --env=TZ=UTC --log-driver=json-file --log-opt=max-size=100m --log-opt=max-file=5 --mount=type=bind,src=${CONFIG_PATH},dst=/app/config.yaml,readonly --health-cmd="/app/grpc-health-probe -addr=127.0.0.1:13013 -service=lucky-gift-service" --health-interval=5s --health-timeout=3s --health-retries=6 --health-start-period=10s ${IMAGE}
ExecStop=/usr/bin/env docker stop --time=${STOP_TIMEOUT_SEC} ${CONTAINER_NAME}
Restart=on-failure
RestartSec=5s
TimeoutStartSec=60
TimeoutStopSec=120
KillMode=none
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,23 @@
[Unit]
Description=Hyapp robot-service container
Requires=docker.service
After=network-online.target docker.service
Wants=network-online.target
StartLimitIntervalSec=120
StartLimitBurst=3
[Service]
Type=simple
EnvironmentFile=/etc/hyapp/robot-service/docker.env
ExecStartPre=-/usr/bin/env docker rm -f ${CONTAINER_NAME}
ExecStart=/usr/bin/env docker run --name=${CONTAINER_NAME} --rm --network=host --init --pull=never --stop-signal=SIGTERM --stop-timeout=${STOP_TIMEOUT_SEC} --env=TZ=UTC --log-driver=json-file --log-opt=max-size=100m --log-opt=max-file=5 --mount=type=bind,src=${CONFIG_PATH},dst=/app/config.yaml,readonly --health-cmd="wget -q -O - http://127.0.0.1:13111/healthz/ready >/dev/null" --health-interval=5s --health-timeout=3s --health-retries=6 --health-start-period=10s ${IMAGE}
ExecStop=/usr/bin/env docker stop --time=${STOP_TIMEOUT_SEC} ${CONTAINER_NAME}
Restart=on-failure
RestartSec=5s
TimeoutStartSec=60
TimeoutStopSec=90
KillMode=none
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target

View File

@ -148,18 +148,17 @@ python3 deploy/tencent_tat/hyapp_data_resources.py resources \
--inventory deploy/tencent_tat/hyapp-services.inventory.prod.json --inventory deploy/tencent_tat/hyapp-services.inventory.prod.json
``` ```
生产 `admin-server` 当前运行在 Saudi 机器 `43.164.70.49 / 10.2.1.2`(实例 `ins-p11begwe`),以 Docker 容器 `hyapp-admin-server` 运行 生产 `admin-server` 固定从 Gitea `origin/main` 发布,当前运行在 Saudi `admin-platform` 机器 `ins-p11begwe` / `10.2.1.2`,不是广州独立机。发布链路是 deploy 机本机构建镜像并推送 Harbor然后用 Tencent TAT 到 `ins-p11begwe` 更新 Docker env
- systemd`hyapp-admin-server` ```bash
- 镜像:`10.2.1.3:18082/hyapp/admin-server:<tag>` docker build -f server/admin/Dockerfile \
- Docker env`/etc/hyapp/admin-server/docker.env` -t 10.2.1.3:18082/hyapp/admin-server:<tag> .
- 配置:`/etc/hyapp/admin-server/config.yaml` docker push 10.2.1.3:18082/hyapp/admin-server:<tag>
- migrations`/opt/hyapp/admin-server/migrations` ```
- 监听:`10.2.1.2:13100`
发布时先在 deploy 机器 `/opt/deploy/sources/hyapp-server` 拉取 `git@gitea.haiyihy.com:hy/hyapp-server.git``origin/main`,构建并推送 `10.2.1.3:18082/hyapp/admin-server:<tag>`,再通过 TAT 到 `ins-p11begwe` 修改 `/etc/hyapp/admin-server/docker.env` 中的 `IMAGE` 并重启 `hyapp-admin-server`。生产不允许切换到其他分支;发布后必须验证 `systemctl is-active hyapp-admin-server`、容器镜像 tag、`10.2.1.2:13100` 监听、`/healthz``/readyz`。 `admin-platform` 机器已配置 Docker 直拉内网 Harbor`/etc/docker/daemon.json` 包含 insecure registry `10.2.1.3:18082``/root/.docker/config.json` 包含 Harbor 登录凭据。TAT 到 `ins-p11begwe` 后只更新 `/etc/hyapp/admin-server/docker.env``IMAGE`,再执行 `systemctl restart hyapp-admin-server`。
注意:`hyapp_admin_server_deploy.py` 是旧广州机器 `134.175.160.86 / 172.16.0.6` 的二进制发布脚本,不适用于当前生产 `admin-server`。除非先更新脚本目标和部署模型,否则不要用它发布生产 `admin-server` 发布后必须验证 `systemctl is-active hyapp-admin-server`、容器镜像 tag/digest、`10.2.1.2:13100``/healthz``/readyz``yumi-admin-caddy` 容器运行、公网 `https://admin-acc.global-interaction.com/` 返回 `200`、未登录 `https://admin-acc.global-interaction.com/api/v1/auth/me` 返回 `401`。生产不允许切换到其他分支
规则: 规则:

View File

@ -14,7 +14,7 @@ from urllib.parse import urlparse, urlunparse
DEFAULT_EDGEONE_ZONE_ID = "zone-3qwew52ihaj2" DEFAULT_EDGEONE_ZONE_ID = "zone-3qwew52ihaj2"
DEFAULT_EDGEONE_ZONE_NAME = "global-interaction.com" DEFAULT_EDGEONE_ZONE_NAME = "global-interaction.com"
DEFAULT_EDGEONE_ALLOWED_HOSTS = "h5.global-interaction.com" DEFAULT_EDGEONE_ALLOWED_HOSTS = "h5.global-interaction.com,vivagames.global-interaction.com,asset.global-interaction.com"
TERMINAL_STATUSES = {"success", "failed", "timeout", "canceled"} TERMINAL_STATUSES = {"success", "failed", "timeout", "canceled"}

View File

@ -24,6 +24,7 @@
"wallet-service", "wallet-service",
"user-service", "user-service",
"activity-service", "activity-service",
"lucky-gift-service",
"cron-service", "cron-service",
"game-service", "game-service",
"notice-service", "notice-service",
@ -38,6 +39,7 @@
"owner": "后端平台", "owner": "后端平台",
"related_services": [ "related_services": [
"gateway-service", "gateway-service",
"luck-gateway",
"room-service", "room-service",
"user-service" "user-service"
] ]
@ -200,6 +202,37 @@
"drain_seconds": 5 "drain_seconds": 5
} }
}, },
"lucky-gift-service": {
"unit": "hyapp-lucky-gift-service",
"container": "hyapp-lucky-gift-service",
"env_file": "/etc/hyapp/lucky-gift-service/docker.env",
"image_template": "${REGISTRY}/lucky-gift-service:${TAG}",
"target_port": 13013,
"hosts": [
"new-app-activity-1",
"new-app-activity-2"
],
"clb": {
"enabled": false,
"drain_seconds": 0
}
},
"luck-gateway": {
"unit": "hyapp-luck-gateway",
"container": "hyapp-luck-gateway",
"env_file": "/etc/hyapp/luck-gateway/docker.env",
"config_required": false,
"image_template": "${REGISTRY}/luck-gateway:${TAG}",
"target_port": 13014,
"hosts": [
"gateway-1",
"gateway-2"
],
"clb": {
"enabled": false,
"drain_seconds": 0
}
},
"notice-service": { "notice-service": {
"unit": "hyapp-notice-service", "unit": "hyapp-notice-service",
"container": "hyapp-notice-service", "container": "hyapp-notice-service",
@ -238,6 +271,26 @@
"drain_seconds": 5 "drain_seconds": 5
} }
}, },
"robot-service": {
"unit": "hyapp-robot-service",
"container": "hyapp-robot-service",
"env_file": "/etc/hyapp/robot-service/docker.env",
"config_path": "/etc/hyapp/robot-service/config.yaml",
"image_template": "${REGISTRY}/robot-service:${TAG}",
"target_port": 13011,
"hosts": [
"new-app-1",
"new-app-2"
],
"clb": {
"enabled": true,
"load_balancer_id": "lb-epvnr4o0",
"listener_ids": [
"lbl-pd35q9go"
],
"drain_seconds": 5
}
},
"statistics-service": { "statistics-service": {
"unit": "hyapp-statistics-service", "unit": "hyapp-statistics-service",
"container": "hyapp-statistics-service", "container": "hyapp-statistics-service",

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import argparse import argparse
import json import json
import os
import shlex import shlex
import sys import sys
from pathlib import Path from pathlib import Path
@ -15,33 +16,15 @@ from deploy.tencent_tat.hyapp_tat_deploy import build_tat_client, load_env_file,
DEFAULT_REPO_URL = "git@gitea.haiyihy.com:hy/hyapp-server.git" DEFAULT_REPO_URL = "git@gitea.haiyihy.com:hy/hyapp-server.git"
DEFAULT_SOURCE_DIR = "/opt/deploy/sources/hyapp-server" DEFAULT_SOURCE_DIR = "/opt/deploy/sources/hyapp-server"
DEFAULT_BRANCH = "main" DEFAULT_BRANCH = "main"
DEFAULT_ADMIN_REGION = "ap-guangzhou"
DEFAULT_ADMIN_INSTANCE_ID = "ins-0x5mjwmc"
DEFAULT_ADMIN_HOST = "134.175.160.86" DEFAULT_ADMIN_HOST = "134.175.160.86"
DEFAULT_ADMIN_BIND_ADDR = "172.16.0.6" DEFAULT_ADMIN_BIND_ADDR = "172.16.0.6"
DEFAULT_ADMIN_PORT = 13100 DEFAULT_ADMIN_PORT = 13100
def shell_join(items: list[str]) -> str:
return " ".join(shlex.quote(item) for item in items)
def admin_endpoint(host: str, user: str) -> str:
return f"{user}@{host}" if user else host
def build_remote_script(args: argparse.Namespace) -> str: def build_remote_script(args: argparse.Namespace) -> str:
endpoint = admin_endpoint(args.admin_host, args.admin_user)
dry_run = "1" if args.dry_run else "0" dry_run = "1" if args.dry_run else "0"
ssh_option_args = [
"-o",
"BatchMode=yes",
"-o",
"StrictHostKeyChecking=accept-new",
"-o",
f"ConnectTimeout={args.ssh_connect_timeout}",
]
ssh_opts = ["ssh", *ssh_option_args]
scp_cmd = shell_join(["timeout", str(args.transfer_timeout_seconds), "scp", *ssh_option_args])
admin_ssh = shell_join([*ssh_opts, endpoint])
return f"""#!/usr/bin/env bash return f"""#!/usr/bin/env bash
set -euo pipefail set -euo pipefail
@ -50,35 +33,45 @@ REPO_URL={shlex.quote(args.repo_url)}
SOURCE_DIR={shlex.quote(args.source_dir)} SOURCE_DIR={shlex.quote(args.source_dir)}
BRANCH={shlex.quote(args.branch)} BRANCH={shlex.quote(args.branch)}
DRY_RUN={dry_run} DRY_RUN={dry_run}
ADMIN_ENDPOINT={shlex.quote(endpoint)}
ADMIN_BIND_ADDR={shlex.quote(args.admin_bind_addr)} ADMIN_BIND_ADDR={shlex.quote(args.admin_bind_addr)}
ADMIN_PORT={shlex.quote(str(args.admin_port))} ADMIN_PORT={shlex.quote(str(args.admin_port))}
ADMIN_SSH={shlex.quote(admin_ssh)}
export GIT_SSH_COMMAND="ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15" export GIT_SSH_COMMAND="ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15"
export GOPROXY="${{GOPROXY:-https://goproxy.cn|https://proxy.golang.org|direct}}"
export GOSUMDB="${{GOSUMDB:-sum.golang.org}}"
echo "admin_host=$(hostname)"
echo "repo=$REPO_URL" echo "repo=$REPO_URL"
echo "source_dir=$SOURCE_DIR" echo "source_dir=$SOURCE_DIR"
echo "branch=$BRANCH" echo "branch=$BRANCH"
echo "admin_endpoint=$ADMIN_ENDPOINT"
if [ "$DRY_RUN" = "1" ]; then if [ "$DRY_RUN" = "1" ]; then
echo "git=$(git --version 2>/dev/null || true)"
echo "go=$(go version 2>/dev/null || true)"
target_commit="$(git ls-remote "$REPO_URL" "refs/heads/$BRANCH" | awk '{{print $1}}')" target_commit="$(git ls-remote "$REPO_URL" "refs/heads/$BRANCH" | awk '{{print $1}}')"
if [ -z "$target_commit" ]; then if [ -z "$target_commit" ]; then
echo "cannot resolve origin/$BRANCH" >&2 echo "cannot resolve origin/$BRANCH" >&2
exit 12 exit 12
fi fi
echo "target_commit=$target_commit" echo "target_commit=$target_commit"
$ADMIN_SSH "ADMIN_BIND_ADDR='$ADMIN_BIND_ADDR' ADMIN_PORT='$ADMIN_PORT' bash -s" <<'REMOTE'
set -e
echo "admin_active=$(systemctl is-active hyapp-admin-server || true)" echo "admin_active=$(systemctl is-active hyapp-admin-server || true)"
ss -lntp | grep "$ADMIN_PORT" || true ss -lntp | grep "$ADMIN_PORT" || true
curl -fsS -m 3 "http://$ADMIN_BIND_ADDR:$ADMIN_PORT/healthz" || true curl -fsS -m 3 "http://$ADMIN_BIND_ADDR:$ADMIN_PORT/healthz" || true
echo echo
REMOTE
exit 0 exit 0
fi fi
if ! command -v git >/dev/null 2>&1 || ! command -v go >/dev/null 2>&1; then
if command -v dnf >/dev/null 2>&1; then
dnf install -y git golang
elif command -v yum >/dev/null 2>&1; then
yum install -y git golang
else
echo "git/go missing and no supported package manager found" >&2
exit 13
fi
fi
if [ -d "$SOURCE_DIR/.git" ]; then if [ -d "$SOURCE_DIR/.git" ]; then
cd "$SOURCE_DIR" cd "$SOURCE_DIR"
git remote set-url origin "$REPO_URL" git remote set-url origin "$REPO_URL"
@ -95,42 +88,25 @@ git clean -fd
commit="$(git rev-parse HEAD)" commit="$(git rev-parse HEAD)"
short_commit="$(git rev-parse --short HEAD)" short_commit="$(git rev-parse --short HEAD)"
build_id="$(date +%Y%m%d%H%M%S)-$short_commit" build_binary="/tmp/admin-server-$short_commit"
build_image="hyapp/admin-server-build:$build_id"
local_binary="/tmp/admin-server-$build_id"
remote_binary="/tmp/admin-server-$build_id"
echo "commit=$commit" echo "commit=$commit"
echo "build_id=$build_id" echo "go=$(go version)"
docker build -t "$build_image" -f server/admin/Dockerfile . cd "$SOURCE_DIR/server/admin"
container_id="$(docker create "$build_image")" GOWORK=off go mod download
trap 'docker rm -f "$container_id" >/dev/null 2>&1 || true' EXIT GOWORK=off CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o "$build_binary" ./cmd/server
docker cp "$container_id:/app/server" "$local_binary" chmod 0755 "$build_binary"
docker rm "$container_id" >/dev/null binary_sha="$(sha256sum "$build_binary" | awk '{{print $1}}')"
trap - EXIT binary_size="$(stat -c %s "$build_binary")"
chmod 0755 "$local_binary"
binary_sha="$(sha256sum "$local_binary" | awk '{{print $1}}')"
binary_size="$(stat -c %s "$local_binary")"
echo "binary_sha=$binary_sha" echo "binary_sha=$binary_sha"
echo "binary_size=$binary_size" echo "binary_size=$binary_size"
{scp_cmd} "$local_binary" "$ADMIN_ENDPOINT:$remote_binary"
$ADMIN_SSH "REMOTE_BINARY='$remote_binary' EXPECTED_SHA='$binary_sha' ADMIN_BIND_ADDR='$ADMIN_BIND_ADDR' ADMIN_PORT='$ADMIN_PORT' COMMIT='$commit' bash -s" <<'REMOTE'
set -euo pipefail
BIN=/opt/hyapp/admin-server/admin-server BIN=/opt/hyapp/admin-server/admin-server
BACKUP="$BIN.bak.$(date +%Y%m%d%H%M%S)" BACKUP="$BIN.bak.$(date +%Y%m%d%H%M%S)"
actual_sha="$(sha256sum "$REMOTE_BINARY" | awk '{{print $1}}')"
if [ "$actual_sha" != "$EXPECTED_SHA" ]; then
echo "uploaded binary sha mismatch: $actual_sha != $EXPECTED_SHA" >&2
exit 21
fi
cp "$BIN" "$BACKUP" cp "$BIN" "$BACKUP"
install -o root -g root -m 0755 "$REMOTE_BINARY" "$BIN.new" install -o root -g root -m 0755 "$build_binary" "$BIN.new"
mv "$BIN.new" "$BIN" mv "$BIN.new" "$BIN"
systemctl restart hyapp-admin-server systemctl restart hyapp-admin-server
@ -160,17 +136,41 @@ echo "installed_sha=$(sha256sum "$BIN" | awk '{{print $1}}')"
echo "listener=$(ss -lntp | grep "$ADMIN_BIND_ADDR:$ADMIN_PORT" || true)" echo "listener=$(ss -lntp | grep "$ADMIN_BIND_ADDR:$ADMIN_PORT" || true)"
echo "healthz=$(cat /tmp/hyapp-admin-healthz.out)" echo "healthz=$(cat /tmp/hyapp-admin-healthz.out)"
echo "readyz=$(cat /tmp/hyapp-admin-readyz.out)" echo "readyz=$(cat /tmp/hyapp-admin-readyz.out)"
REMOTE
""" """
def run_deploy(args: argparse.Namespace) -> dict[str, Any]: def run_deploy(args: argparse.Namespace) -> dict[str, Any]:
inventory = load_inventory(args.inventory) inventory = load_inventory(args.inventory)
client = build_tat_client(inventory) admin_inventory = {
"region": args.admin_region,
"deploy_server": {"instance_id": args.admin_instance_id},
"hosts": {"admin-server": {"instance_id": args.admin_instance_id, "private_ip": args.admin_bind_addr}},
"services": {
"admin-server": {
"unit": "hyapp-admin-server",
"container": "hyapp-admin-server",
"env_file": "/etc/hyapp/admin-server/config.yaml",
"target_port": args.admin_port,
"hosts": ["admin-server"],
}
},
}
previous_region = None
region_was_set = "TENCENTCLOUD_REGION" in os.environ
if region_was_set:
previous_region = os.environ["TENCENTCLOUD_REGION"]
os.environ["TENCENTCLOUD_REGION"] = args.admin_region
try:
client = build_tat_client(admin_inventory)
finally:
if region_was_set and previous_region is not None:
os.environ["TENCENTCLOUD_REGION"] = previous_region
elif not region_was_set:
os.environ.pop("TENCENTCLOUD_REGION", None)
script = build_remote_script(args) script = build_remote_script(args)
result = run_tat_shell( result = run_tat_shell(
client, client,
instance_id=inventory["deploy_server"]["instance_id"], instance_id=args.admin_instance_id,
script=script, script=script,
command_name="hyapp-admin-server-deploy", command_name="hyapp-admin-server-deploy",
timeout_seconds=args.timeout_seconds, timeout_seconds=args.timeout_seconds,
@ -179,29 +179,30 @@ def run_deploy(args: argparse.Namespace) -> dict[str, Any]:
output = str(result.get("output") or "") output = str(result.get("output") or "")
return { return {
"adminHost": args.admin_host, "adminHost": args.admin_host,
"adminInstanceId": args.admin_instance_id,
"adminRegion": args.admin_region,
"dryRun": args.dry_run, "dryRun": args.dry_run,
"ok": result.get("status") == "SUCCESS", "ok": result.get("status") == "SUCCESS",
"output": output[-12000:], "output": output[-12000:],
"sourceDir": args.source_dir, "sourceDir": args.source_dir,
"status": result.get("status"), "status": result.get("status"),
"target": inventory.get("deploy_server") or {}, "target": admin_inventory.get("deploy_server") or {},
} }
def build_parser() -> argparse.ArgumentParser: def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Deploy production admin-server from Gitea origin/main through the deploy host.") parser = argparse.ArgumentParser(description="Deploy production admin-server by pulling Gitea origin/main on the Guangzhou admin host.")
parser.add_argument("action", choices=["deploy"]) parser.add_argument("action", choices=["deploy"])
parser.add_argument("--env-file", default="") parser.add_argument("--env-file", default="")
parser.add_argument("--inventory", default="deploy/tencent_tat/hyapp-services.inventory.prod.example.json") parser.add_argument("--inventory", default="deploy/tencent_tat/hyapp-services.inventory.prod.example.json")
parser.add_argument("--repo-url", default=DEFAULT_REPO_URL) parser.add_argument("--repo-url", default=DEFAULT_REPO_URL)
parser.add_argument("--source-dir", default=DEFAULT_SOURCE_DIR) parser.add_argument("--source-dir", default=DEFAULT_SOURCE_DIR)
parser.add_argument("--branch", default=DEFAULT_BRANCH) parser.add_argument("--branch", default=DEFAULT_BRANCH)
parser.add_argument("--admin-region", default=DEFAULT_ADMIN_REGION)
parser.add_argument("--admin-instance-id", default=DEFAULT_ADMIN_INSTANCE_ID)
parser.add_argument("--admin-host", default=DEFAULT_ADMIN_HOST) parser.add_argument("--admin-host", default=DEFAULT_ADMIN_HOST)
parser.add_argument("--admin-user", default="")
parser.add_argument("--admin-bind-addr", default=DEFAULT_ADMIN_BIND_ADDR) parser.add_argument("--admin-bind-addr", default=DEFAULT_ADMIN_BIND_ADDR)
parser.add_argument("--admin-port", type=int, default=DEFAULT_ADMIN_PORT) parser.add_argument("--admin-port", type=int, default=DEFAULT_ADMIN_PORT)
parser.add_argument("--ssh-connect-timeout", type=int, default=15)
parser.add_argument("--transfer-timeout-seconds", type=int, default=300)
parser.add_argument("--timeout-seconds", type=int, default=1800) parser.add_argument("--timeout-seconds", type=int, default=1800)
parser.add_argument("--dry-run", action="store_true") parser.add_argument("--dry-run", action="store_true")
parser.add_argument("--yes", action="store_true") parser.add_argument("--yes", action="store_true")

View File

@ -15,21 +15,26 @@ class AdminServerDeployScriptTests(unittest.TestCase):
args = argparse.Namespace( args = argparse.Namespace(
admin_bind_addr="172.16.0.6", admin_bind_addr="172.16.0.6",
admin_host="134.175.160.86", admin_host="134.175.160.86",
admin_instance_id="ins-0x5mjwmc",
admin_port=13100, admin_port=13100,
admin_user="", admin_region="ap-guangzhou",
branch="main", branch="main",
dry_run=False, dry_run=False,
repo_url="git@gitea.haiyihy.com:hy/hyapp-server.git", repo_url="git@gitea.haiyihy.com:hy/hyapp-server.git",
source_dir="/opt/deploy/sources/hyapp-server", source_dir="/opt/deploy/sources/hyapp-server",
ssh_connect_timeout=15,
transfer_timeout_seconds=300,
) )
script = admin_deploy.build_remote_script(args) script = admin_deploy.build_remote_script(args)
self.assertIn("git fetch --prune origin \"$BRANCH\"", script) self.assertIn("git fetch --prune origin \"$BRANCH\"", script)
self.assertIn("git checkout -B \"$BRANCH\" \"origin/$BRANCH\"", script) self.assertIn("git checkout -B \"$BRANCH\" \"origin/$BRANCH\"", script)
self.assertIn("docker build -t \"$build_image\" -f server/admin/Dockerfile .", script) self.assertIn("git clone \"$REPO_URL\" \"$SOURCE_DIR\"", script)
self.assertIn("timeout 300 scp", script) self.assertIn("GOWORK=off go mod download", script)
self.assertIn("GOWORK=off CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o \"$build_binary\" ./cmd/server", script)
self.assertNotIn("docker build", script)
self.assertNotIn("docker push", script)
self.assertNotIn("docker pull", script)
self.assertNotIn("10.2.1.3:18082", script)
self.assertNotIn("timeout 300 scp", script)
self.assertNotIn("rsync -az --partial", script) self.assertNotIn("rsync -az --partial", script)
self.assertIn("systemctl restart hyapp-admin-server", script) self.assertIn("systemctl restart hyapp-admin-server", script)
self.assertIn("http://$ADMIN_BIND_ADDR:$ADMIN_PORT/healthz", script) self.assertIn("http://$ADMIN_BIND_ADDR:$ADMIN_PORT/healthz", script)

View File

@ -72,7 +72,15 @@ def selected_hosts(service: dict[str, Any], raw_hosts: str) -> list[str]:
return requested return requested
def validate_service_templates(hyapp_root: Path, service_name: str) -> list[str]: def service_config_required(service: dict[str, Any]) -> bool:
# 少数边缘入口是 env-only 进程,例如 luck-gateway 只承载 HTTP 转发边界;
# inventory 显式标记后跳过 YAML contract 校验,避免为了校验脚本伪造无效 config。
return bool(service.get("config_required", True))
def validate_service_templates(hyapp_root: Path, service_name: str, service: dict[str, Any]) -> list[str]:
if not service_config_required(service):
return []
errors: list[str] = [] errors: list[str] = []
config_dir = hyapp_root / "services" / service_name / "configs" config_dir = hyapp_root / "services" / service_name / "configs"
configs = {name: load_yaml(config_dir / name) for name in CONFIG_FILENAMES} configs = {name: load_yaml(config_dir / name) for name in CONFIG_FILENAMES}
@ -87,7 +95,9 @@ def validate_service_templates(hyapp_root: Path, service_name: str) -> list[str]
return errors return errors
def validate_rendered_config(hyapp_root: Path, service_name: str, host_name: str, rendered_dir: Path) -> list[str]: def validate_rendered_config(hyapp_root: Path, service_name: str, service: dict[str, Any], host_name: str, rendered_dir: Path) -> list[str]:
if not service_config_required(service):
return []
contract = load_yaml(hyapp_root / "services" / service_name / "configs" / "config.tencent.example.yaml") contract = load_yaml(hyapp_root / "services" / service_name / "configs" / "config.tencent.example.yaml")
rendered_path = rendered_dir / host_name / service_name / "config.yaml" rendered_path = rendered_dir / host_name / service_name / "config.yaml"
rendered = load_yaml(rendered_path) rendered = load_yaml(rendered_path)
@ -117,11 +127,12 @@ def main(argv: list[str]) -> int:
rendered_dir = Path(args.rendered_dir).expanduser().resolve() if args.rendered_dir else None rendered_dir = Path(args.rendered_dir).expanduser().resolve() if args.rendered_dir else None
errors: list[str] = [] errors: list[str] = []
for service_name in selected_service_names(inventory, args.services): for service_name in selected_service_names(inventory, args.services):
errors.extend(validate_service_templates(hyapp_root, service_name)) service = inventory["services"][service_name]
errors.extend(validate_service_templates(hyapp_root, service_name, service))
if rendered_dir is None: if rendered_dir is None:
continue continue
for host_name in selected_hosts(inventory["services"][service_name], args.hosts): for host_name in selected_hosts(service, args.hosts):
errors.extend(validate_rendered_config(hyapp_root, service_name, host_name, rendered_dir)) errors.extend(validate_rendered_config(hyapp_root, service_name, service, host_name, rendered_dir))
if errors: if errors:
print(json.dumps({"ok": False, "errors": errors}, ensure_ascii=False, indent=2), file=sys.stderr) print(json.dumps({"ok": False, "errors": errors}, ensure_ascii=False, indent=2), file=sys.stderr)
return 1 return 1

View File

@ -7,7 +7,7 @@ import { Button } from "../../shared/ui/Button";
import { StatusBadge, type StatusBadgeStatus } from "../../shared/ui/StatusBadge"; import { StatusBadge, type StatusBadgeStatus } from "../../shared/ui/StatusBadge";
import styles from "./CdnRefreshPage.module.css"; import styles from "./CdnRefreshPage.module.css";
const defaultUrlText = "https://h5.global-interaction.com/"; const defaultUrlText = "https://vivagames.global-interaction.com/";
function parseUrls(text: string) { function parseUrls(text: string) {
return Array.from( return Array.from(
@ -148,7 +148,7 @@ export function CdnRefreshPage() {
<CloudLightning size={18} /> <CloudLightning size={18} />
<div> <div>
<strong>EdgeOne</strong> <strong>EdgeOne</strong>
<span>h5.global-interaction.com</span> <span>h5.global-interaction.com, vivagames.global-interaction.com, asset.global-interaction.com</span>
</div> </div>
</div> </div>
</div> </div>