# ============================================================================= # TIANAI CAPTCHA PLATFORM — 生产环境晋升(手动触发器) # 触发:Gitea Actions 页面手动 workflow_dispatch,confirm 必须输入 yes # 流程:从测试机导出当前 :test 镜像 → scp 到 prod1 → docker load # → 备份旧镜像 → 重建容器 → 健康检查 # 说明:prod1 的 docker daemon 未配置 insecure-registries(改它需要重启 daemon # = 重启全部生产容器),因此不走 registry pull,用 save/scp/load 传递镜像。 # ============================================================================= name: promote-prod on: workflow_dispatch: inputs: confirm: description: '确认部署到生产环境(prod1)? 输入 yes' required: true default: 'no' env: IMAGE: 156.233.226.46:5000/tianai-captcha-platform jobs: promote-prod: runs-on: ubuntu-latest # job 级 env: shell 变量不跨 step, 必须在这里声明才能在所有 step 中使用 env: TEST: ${{ secrets.CAPTCHA_TEST_HOST }} PROD: ${{ secrets.CAPTCHA_PROD_HOST }} if: github.event.inputs.confirm == 'yes' steps: - name: Setup SSH key (from secrets) run: | mkdir -p ~/.ssh printf '%s\n' "${{ secrets.CAPTCHA_DEPLOY_KEY }}" > ~/.ssh/id_ed25519_deploy chmod 600 ~/.ssh/id_ed25519_deploy ssh-keyscan -H ${{ secrets.CAPTCHA_TEST_HOST }} >> ~/.ssh/known_hosts 2>/dev/null ssh-keyscan -H ${{ secrets.CAPTCHA_PROD_HOST }} >> ~/.ssh/known_hosts 2>/dev/null echo "SSH 密钥就绪" - name: Export test image (test → runner) run: | set -e SSH_KEY="$HOME/.ssh/id_ed25519_deploy" ssh -i $SSH_KEY root@$TEST \ "docker pull $IMAGE:test >/dev/null && docker save $IMAGE:test | gzip > /tmp/tianai-promote.tar.gz && ls -la /tmp/tianai-promote.tar.gz" scp -i $SSH_KEY root@$TEST:/tmp/tianai-promote.tar.gz /tmp/tianai-promote.tar.gz echo "镜像已下载到 runner" - name: Transfer to prod1 (runner → prod1) run: | set -e SSH_KEY="$HOME/.ssh/id_ed25519_deploy" scp -i $SSH_KEY /tmp/tianai-promote.tar.gz root@$PROD:/tmp/ ssh -i $SSH_KEY root@$TEST 'rm -f /tmp/tianai-promote.tar.gz' rm -f /tmp/tianai-promote.tar.gz echo "镜像已传输到 prod1" - name: Load & restart container on prod1 run: | set -e SSH_KEY="$HOME/.ssh/id_ed25519_deploy" ssh -i $SSH_KEY root@$PROD ' set -e echo "===== 备份当前生产镜像 =====" docker tag tianai-captcha-platform:latest tianai-captcha-platform:backup-$(date +%Y%m%d-%H%M) 2>/dev/null \ && echo "已备份" || echo "无旧镜像可备份" echo "===== 加载新镜像 =====" docker load < /tmp/tianai-promote.tar.gz docker tag '"$IMAGE"':test tianai-captcha-platform:latest rm -f /tmp/tianai-promote.tar.gz echo "===== 重建容器 =====" docker rm -f tianai-captcha 2>/dev/null || true docker run -d --name tianai-captcha \ --network middleware \ --restart unless-stopped \ -p 18200:18200 \ -v /opt/tianai/config:/app/config \ tianai-captcha-platform:latest sleep 15 docker ps --filter name=tianai-captcha --format "{{.Names}} {{.Status}}" docker logs tianai-captcha 2>&1 | tail -3 ' - name: Health check prod (generate API, 校验 JSON code 而非 HTTP 状态) env: PROD_SITE_KEY: 6796bad8-4369-492f-a4d7-643a93bb07f3 run: | set -e SSH_KEY="$HOME/.ssh/id_ed25519_deploy" OK=0 CODE=000 for i in $(seq 1 24); do CODE=$(ssh -i $SSH_KEY root@$PROD "curl -s -X POST 'http://127.0.0.1:18200/api/challenge/generate' -H 'X-Site-Key: $PROD_SITE_KEY'" | python3 -c "import json,sys; print(json.load(sys.stdin).get('code'))" 2>/dev/null || echo 000) if [ "$CODE" = "200" ]; then OK=1; break; fi sleep 5 done if [ "$OK" != "1" ]; then echo "生产健康检查失败 (code=$CODE)" ssh -i $SSH_KEY root@$PROD 'docker logs tianai-captcha --tail 30' || true echo "如需回滚: ssh prod1 后执行 docker rm -f tianai-captcha 并用 backup-xxx 标签重建容器" exit 1 fi echo "生产环境健康检查 OK (code=$CODE)" - name: Promotion summary if: always() run: | echo "生产访问: prod1 :18200 (middleware 网络: tianai-captcha:18200)" echo "回滚方式: prod1 上 docker tag tianai-captcha-platform:backup-YYYYMMDD-HHMM tianai-captcha-platform:latest 后重建容器"