diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 649d7217e..6ef2899d3 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -104,6 +104,43 @@ Staging 当前可以保持 no-op;Production 开启前必须先验证 SMTP/Redi --- +## Staging 服务器 Docker 凭证配置 + +Staging 服务器(116.62.226.203)需要配置 ACR 和 Gitea Registry 凭证,否则 docker pull 和 Watchtower 自动更新会失败。 + +### 凭证文件位置 +- Docker 配置文件:`/root/.docker/config.json` +- 包含两个 registry 的认证信息: + - `xiaoxia-registry.cn-hangzhou.cr.aliyuncs.com`(阿里云 ACR) + - `git.xiaoxiajianji.com`(Gitea 容器镜像仓库) + +### 服务器迁移后恢复步骤 +```bash +# 1. 登录 ACR +docker login xiaoxia-registry.cn-hangzhou.cr.aliyuncs.com -u + +# 2. 登录 Gitea Registry +docker login git.xiaoxiajianji.com -u xiaoxia -p + +# 3. 重启 Watchtower(确保挂载最新 config.json) +docker restart watchtower +``` + +### Watchtower 配置 +- 容器名:`watchtower` +- 检查间隔:300 秒(5 分钟) +- 监控容器:`xiaoxia-api-staging`、`xiaoxia-worker-staging`、`xiaoxia-web-staging` +- 必须挂载 `-v /root/.docker/config.json:/config.json` 才能拉取私有镜像 +- 必须挂载 `-v /var/run/docker.sock:/var/run/docker.sock` 才能管理容器 +- 容器使用 `:dev` 稳定 tag,Watchtower 通过检测 `:dev` tag 的 digest 变化来发现更新 + +### 镜像 Tag 策略 +- CI 每次构建推送三种 tag:`${GITHUB_SHA}`(精确版本)、`${GITHUB_REF_NAME}`(分支名)、`:dev`(滚动 tag,仅 develop 分支) +- Staging 容器统一使用 `:dev` tag 启动,确保 Watchtower 能自动发现新版本 +- Migration(alembic)使用 commit SHA tag 执行,不依赖 Watchtower + +--- + ## Gitea Actions 约定 - `develop` 分支触发 staging 部署。 diff --git a/scripts/ci_staging_deploy.sh b/scripts/ci_staging_deploy.sh index 42a1d4625..0edd5cda3 100755 --- a/scripts/ci_staging_deploy.sh +++ b/scripts/ci_staging_deploy.sh @@ -368,6 +368,21 @@ fi echo "All images pulled." +# ====== 打稳定 tag(:dev),供 Watchtower 监控 ====== +# Watchtower 只能检测同一个 tag 的 digest 变化。 +# commit SHA tag 每次构建都不同,Watchtower 无法感知更新。 +# 因此每次部署都将最新镜像 tag 为 :dev,容器统一使用 :dev 启动。 +DEV_API="${REGISTRY}/xiaoxia-saas-api:dev" +DEV_WORKER="${REGISTRY}/xiaoxia-saas-worker:dev" +DEV_WEB="${REGISTRY}/xiaoxia-saas-web:dev" +docker tag "$REGISTRY_API" "$DEV_API" +docker tag "$REGISTRY_WORKER" "$DEV_WORKER" +docker tag "$REGISTRY_WEB" "$DEV_WEB" +echo "✅ Tagged images as :dev for Watchtower monitoring" +echo " API: $DEV_API" +echo " Worker: $DEV_WORKER" +echo " Web: $DEV_WEB" + # ====== 镜像内容校验 ====== echo "" echo "==========================================" @@ -533,7 +548,7 @@ docker run -d \ --health-retries 3 \ --health-start-period 40s \ $LOG_OPTS \ - "$REGISTRY_API" & + "$DEV_API" & PID_API_START=$! # ── Worker: 通过 compose 启动(单一事实来源)── @@ -541,7 +556,7 @@ PID_API_START=$! # healthcheck 匹配 'celery.*worker'(不把 beat 算活)、资源限制 4C/8G。 # WORKER_IMAGE 通过环境变量覆盖镜像 tag(compose.yml 默认 :dev)。 echo "Starting worker via docker compose (from $INFRA_DOCKER_DIR)..." -WORKER_IMAGE="$REGISTRY_WORKER" APP_VERSION="$IMAGE_TAG" compose up -d --no-deps worker & +WORKER_IMAGE="$DEV_WORKER" APP_VERSION="$IMAGE_TAG" compose up -d --no-deps worker & PID_WORKER_START=$! # ── Web: 暂保留 docker run(TODO: 后续收敛到 compose)── @@ -557,7 +572,7 @@ docker run -d \ --health-timeout 5s \ --health-retries 3 \ $LOG_OPTS \ - "$REGISTRY_WEB" & + "$DEV_WEB" & PID_WEB_START=$! wait $PID_API_START $PID_WORKER_START $PID_WEB_START @@ -688,5 +703,5 @@ echo "=== Staging deployment complete ===" echo "API: http://127.0.0.1:8000" echo "Web: http://127.0.0.1:3001" echo "Worker: managed by docker compose (project=$COMPOSE_PROJECT)" -echo "Version: $IMAGE_TAG" +echo "Version: $IMAGE_TAG (running as :dev for Watchtower)" docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep staging