This commit is contained in:
@@ -6,8 +6,8 @@ on:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
# Register a dedicated runner on the target host with label wq-production:host.
|
||||
runs-on: wq-production
|
||||
# Reuse the runner that deploys zhixing-system to the same Docker host.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Gitea 生产部署
|
||||
|
||||
本方案在目标 Linux 服务器上由 Gitea host Runner 构建并启动 Docker Compose,复用已有 PostgreSQL。入口绑定 `127.0.0.1:8112`,由宿主机反向代理提供公网 HTTPS。现有 `compose.yaml`、`compose.public.yaml` 保持独立,不与生产文件叠加。
|
||||
本方案在目标 Linux 服务器上由已有 Gitea Runner 构建并启动 Docker Compose,复用已有 PostgreSQL。入口绑定 `127.0.0.1:8112`,由宿主机反向代理提供公网 HTTPS。现有 `compose.yaml`、`compose.public.yaml` 保持独立,不与生产文件叠加。
|
||||
|
||||
## 1. 填写配置和数据库账号密码
|
||||
|
||||
@@ -41,11 +41,13 @@ WorldQuant 凭据由环境变量管理,启动时加密写入数据库;页面
|
||||
|
||||
## 2. 配置 Gitea Runner
|
||||
|
||||
在目标 Docker 宿主机直接运行专用 Runner,注册标签 **`wq-production:host`**,工作流的 `runs-on` 对应 `wq-production`。建议专用 Runner 配置 `runner.capacity: 1`。不要使用指向其他机器的 Docker context;这里的 host 模式也不能被当成“容器化 Runner 自动进入宿主机”。
|
||||
沿用已成功部署 `zhixing-system` 的运行器标签 **`ubuntu-latest`**,无需注册新的 `wq-production` 运行器。该运行器的执行环境须能通过 Docker CLI/Compose 操作同一台 1Panel 服务器的 Docker daemon;可以复用现有 Docker 连接方式,不强制 host 模式。
|
||||
|
||||
Runner 用户需要 Docker 权限,以及写入预先创建的 `/opt/wq-alpha` 目录的权限。该目录仅保存锁文件和版本记录,不保存凭据。宿主机需具备 Git、Bash、Node.js 20(checkout v4)、`flock`(通常由 util-linux 提供)和支持 `up --wait --wait-timeout` 的 Docker Compose v2 或 v5。镜像使用锁文件构建,服务器需能访问 GitHub checkout action、基础镜像仓库和依赖源。本地验证环境为 Docker Engine 29.6.2、Compose 5.3.1;你的 Gitea/Runner 版本需在首次运行核验。
|
||||
执行环境需要 Git、Bash、Node.js 20(checkout v4)以及支持 `up --wait --wait-timeout` 的 Docker Compose。本流程不需要 `flock` 或预建 `/opt/wq-alpha`。服务器需能访问 checkout action、基础镜像仓库和依赖源。
|
||||
|
||||
在 Gitea 仓库启用 Actions,提交并推送这些配置后,`main` 的 push 或手动运行会部署。Runner 应只接收可信仓库的任务,因为它具备生产主机 Docker 权限。凭据通过上述 Secrets 注入,脚本使用 `--env-file /dev/null` 防止误读检出目录的开发 `.env`。部署脚本用 `/opt/wq-alpha/deploy.lock` 实现跨 checkout 的互斥,冲突部署直接失败,可稍后重新运行。
|
||||
在仓库启用 Actions,`main` push 或手动运行会部署。Secrets 通过部署步骤的环境变量注入,`--env-file /dev/null` 防止误读开发 `.env`。构建可并行;构建完成后,以 Docker 唯一容器名 `wq-alpha-production-deploy-lock` 互斥保护预检、迁移和服务切换。锁容器不启动、不携带凭据,正常结束或失败时删除;竞争失败的任务不会删除其他任务的锁。
|
||||
|
||||
如果 Runner 被强制终止或 Docker 断连,可能留下锁容器。先确认没有本项目部署正在执行,再手动运行 `docker rm wq-alpha-production-deploy-lock` 后重试。不要在部署进行时删除锁。
|
||||
|
||||
## 3. 配置反向代理并首次运行
|
||||
|
||||
@@ -65,7 +67,7 @@ bash scripts/deploy-production.sh
|
||||
|
||||
升级包含停机窗口;提前结束或暂停长时间任务。每次发布前通过现有数据库管理工具备份专用库,并在独立安全位置备份加密密钥及必要配置,先在独立库验证恢复。此工作流不会自动备份或自动恢复数据库。
|
||||
|
||||
构建及预检失败时旧服务继续运行。停止服务后的迁移或健康检查失败需要人工处理;不要对可能已变更的 schema 直接自动降级。脚本将切换前的镜像 ID/标签记录到 `/opt/wq-alpha/previous-images.txt`,最后成功的提交记录到 `current-release.txt`,保留旧镜像且不执行 prune。失败重试前另存这些记录,避免后续尝试覆盖回退参考。
|
||||
构建及预检失败时旧服务继续运行。停止服务后的迁移或健康检查失败需要人工处理;不要对可能已变更的 schema 直接自动降级。脚本在 Actions 日志输出切换前的镜像 ID/标签,成功后输出当前提交标识;保留旧镜像且不执行 prune。请保留部署日志作为回退参考,不再依赖宿主机版本记录文件。
|
||||
|
||||
如旧代码与当前 schema 兼容,可检出旧提交并指定其镜像标签启动;否则先停止应用,使用经过验证的备份恢复数据库,再用原加密密钥和对应旧版本启动。数据库恢复会丢失备份后的写入,必须人工确认后执行。本配置没有自动数据库降级,也不承诺无停机升级。
|
||||
|
||||
|
||||
@@ -12,22 +12,33 @@ for key in WQ_EMAIL WQ_PASSWORD DATABASE_URL ADMIN_PASSWORD ENCRYPTION_KEY DATAB
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
state_dir="${DEPLOY_STATE_DIR:-/opt/wq-alpha}"
|
||||
if [[ "$state_dir" != /* || ! -d "$state_dir" || ! -w "$state_dir" ]]; then
|
||||
echo 'DEPLOY_STATE_DIR must be an existing writable absolute directory.' >&2
|
||||
exit 1
|
||||
fi
|
||||
command -v flock >/dev/null
|
||||
# Stable across checkouts; stores only a lock and release metadata, never credentials.
|
||||
exec 9>"$state_dir/deploy.lock"
|
||||
flock -n 9 || { echo 'Another production deployment is running.' >&2; exit 1; }
|
||||
export DEPLOY_TAG="${DEPLOY_TAG:-$(git rev-parse HEAD)}"
|
||||
compose=(docker compose --env-file /dev/null -p wq-alpha-production -f compose.production.yaml)
|
||||
trap 'rc=$?; "${compose[@]}" --profile jobs ps -a || true; exit "$rc"' EXIT
|
||||
lock_id=""
|
||||
cleanup() {
|
||||
local rc=$?
|
||||
"${compose[@]}" --profile jobs ps -a || true
|
||||
# Remove only the lock acquired by this process, never another deployment's lock.
|
||||
if [[ -n "$lock_id" ]]; then
|
||||
docker rm "$lock_id" >/dev/null || true
|
||||
fi
|
||||
exit "$rc"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
|
||||
"${compose[@]}" config --quiet
|
||||
"${compose[@]}" --profile jobs config --quiet
|
||||
"${compose[@]}" build backend web
|
||||
# Docker enforces unique container names across runner jobs and checkout paths.
|
||||
# The lock container is never started and receives no deployment credentials.
|
||||
if ! lock_id=$(docker create --name wq-alpha-production-deploy-lock \
|
||||
--label "wq.deploy.commit=$DEPLOY_TAG" \
|
||||
--network none --entrypoint /bin/true "wq-alpha-production-backend:$DEPLOY_TAG"); then
|
||||
echo 'Cannot acquire deployment lock; check Docker and other active deployments.' >&2
|
||||
exit 1
|
||||
fi
|
||||
# Validate secrets and DB connectivity before interrupting the running version.
|
||||
"${compose[@]}" run --rm --no-deps backend python -c '
|
||||
import asyncio
|
||||
@@ -51,10 +62,10 @@ except Exception:
|
||||
# Record immutable image references before switching; do not prune old images.
|
||||
previous_images=$("${compose[@]}" images --quiet)
|
||||
if [[ -n "$previous_images" ]]; then
|
||||
docker image inspect --format '{{.Id}} {{json .RepoTags}}' $previous_images > "$state_dir/previous-images.txt"
|
||||
echo "Previous deployment images (retain for rollback):"
|
||||
docker image inspect --format '{{.Id}} {{json .RepoTags}}' $previous_images
|
||||
fi
|
||||
"${compose[@]}" stop web backend
|
||||
"${compose[@]}" --profile jobs run --rm --no-deps migrate
|
||||
"${compose[@]}" up -d --no-build --remove-orphans --wait --wait-timeout 180 backend web
|
||||
printf '%s\n' "$DEPLOY_TAG" > "$state_dir/current-release.txt"
|
||||
echo "Production is healthy; release $DEPLOY_TAG"
|
||||
|
||||
Reference in New Issue
Block a user