Files
ballet-server/backend/DEPLOYMENT.md
T
yuxuanhui 36d6fe123f
Deploy backend / deploy (push) Successful in 29s
feat: 更新部署文档,添加 UAT 环境配置和日志管理设置
2026-09-30 11:31:36 +08:00

55 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gitea + Docker 生产部署
生产 Compose 只部署 Go API,连接已由其他服务管理的 PostgreSQL;数据库可以在同一主机,也可以在另一台服务器。本地开发继续使用包含 PostgreSQL 的 `compose.yaml`。小程序仍由微信开发者工具发布,构建时的 `TARO_APP_API_BASE_URL` 应指向此 API 的 HTTPS 域名。
## 首次准备
1. 在目标 Linux 主机安装 Docker Engine、Docker Compose、Git、Bash、`flock` 和 `curl`。按照 [Gitea Runner 文档](https://docs.gitea.com/usage/actions/act-runner/)在这台主机上注册仓库专用的 `act_runner`,为它配置 `tencent-prod:host` 标签,并确保 Runner 用户可以执行 Docker 命令和 `actions/checkout@v4`。此工作流在宿主机执行代码且拥有 Docker 权限,只应给可信仓库和维护者使用。
2. 在 Gitea 仓库启用 Actions,并按[密钥文档](https://docs.gitea.com/usage/actions/secrets/)设置 `DATABASE_URL`、`WECHAT_APP_ID`、`WECHAT_APP_SECRET`。数据库连接只使用一个 `DATABASE_URL` 密钥;`WECHAT_APP_ID` 必须与小程序 AppID 一致。缺少密钥会在 Compose 校验时失败;校验使用 `config --quiet`,不会打印展开后的密钥。
3. `DATABASE_URL` 使用 `postgresql://用户名:已编码密码@数据库主机:端口/数据库名?sslmode=模式`,例如 `postgresql://ballet_island:<encoded-password>@db.example.com:5432/ballet_island?sslmode=verify-full`。数据库主机必须从 **API 容器内部**可解析、可访问,不能把容器内的 `127.0.0.1` 当作另一台主机。密码中的 `@`、`:`、`/`、`?`、`#`、`%` 等 URL 特殊字符须按 [PostgreSQL 连接 URI 规则](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING)做百分号编码。`sslmode` 按数据库实际 TLS 配置填写:服务器未启用 TLS 时用 `disable`;有可验证证书时优先用 `verify-full`,使用私有 CA 时还需让 API 容器信任该 CA。跨服务器还需放通应用主机到数据库的连接,并确认数据库允许该用户和来源地址访问。
4. 确保宿主机 `127.0.0.1:8113` 空闲。HTTPS 反向代理将 API 域名转发到该地址,并在微信小程序后台配置 request 合法域名。生产 Compose 仅将 API 绑定到回环地址,不发布数据库端口。
5. 首次上线前在统一数据库服务器上创建数据库和账号,确定备份位置及恢复步骤。本地 `compose.yaml` 的 `ballet-island_postgres_data` 卷不会被生产 Compose 使用;若数据仍在该卷中,须先迁移到统一数据库服务器。
## 发布流程
推送 `main` 中的 `backend/**` 或工作流变更会触发部署,也可在 Gitea Actions 中手动运行。Runner 从 Gitea 检出代码,在目标主机取得部署锁,并确认检出的提交仍是远端 `main` 最新提交。然后校验生产 Compose、以提交短 SHA 构建 API 镜像、更新 `ballet-island` Compose 项目,等待 API 的数据库就绪检查通过,再访问宿主机 `/readyz`。工作流结束时输出该项目的容器状态。
生产配置通过 `--env-file /dev/null` 忽略开发 `.env`,数据库连接完全由 Gitea 的 `DATABASE_URL` 指定。后端在未设置 `DATABASE_URL` 时仍使用本地开发的 `PG*` 环境变量。生产 Compose 不创建 PostgreSQL 容器,也不要求与数据库容器共享 Docker 网络;如果数据库仅通过另一 Compose 项目的内部服务名开放,须先提供 API 容器可达的网络和地址。工作流未使用 `--remove-orphans`,切换前若已有同名项目中的旧 PostgreSQL 容器,不会自动停止或清理它。
API 启动时会在事务和数据库锁保护下应用尚未执行的版本迁移,当前没有独立迁移命令。发布前应在统一数据库服务器上备份,未来涉及不兼容 schema 的改动须先制定迁移与回退顺序。工作流不会自动回滚数据库;失败后先检查容器状态和日志,再决定恢复备份或重新部署。
## 集中日志(Alloy → Loki)
生产 API 已在服务级配置共享日志平台使用的 Docker 标签:`observability.logs=true`、`observability.project=ballet-island`、`observability.service=api`、`observability.env=production`。后端使用 `slog` 将 JSON 日志输出到 stdout;Docker 使用 `json-file` 驱动,按 `max-size: "20m"`、`max-file: "5"` 轮转。这是容器本地日志轮转配置,Loki 的保留时间由共享平台单独管理。[Docker 日志轮转说明](https://docs.docker.com/engine/logging/drivers/json-file/)
接入前提是 API 所在主机已有 Alloy,能够通过该主机的 Docker API 读取容器日志,按 `observability.logs=true` 发现容器,将其余三个标签映射为 Loki 的 `project`、`service`、`env`,并发送到可达的 Loki。`host` 标签由 Alloy 按实际主机设置。该采集方式不要求 API 加入日志平台网络或在业务镜像内安装 Alloy。本仓库只配置业务容器,Alloy、Loki 和 Grafana 由共享平台管理。[Alloy Docker 日志采集说明](https://grafana.com/docs/alloy/latest/reference/components/loki/loki.source.docker/)
沿用上述发布流程,`docker compose up` 会重新创建配置发生变化的 API 容器以应用标签和日志驱动;仅执行 `restart` 不会应用这些变更。部署后在应用主机检查实际容器:
```sh
docker ps \
--filter label=com.docker.compose.project=ballet-island \
--filter label=com.docker.compose.service=api \
--format '{{.Names}} logs={{.Label "observability.logs"}} project={{.Label "observability.project"}} service={{.Label "observability.service"}} env={{.Label "observability.env"}}'
```
预期标签为 `logs=true project=ballet-island service=api env=production`。随后在 Grafana Explore 选择 Loki,时间范围选最近一小时,查询:
```logql
{project="ballet-island", service="api", env="production"} |= "server starting"
```
核对查询结果包含本次部署产生的新启动日志,时间和主机正确,才算日志链路验收通过。容器健康或 Alloy/Loki 就绪不能代替此项验收。本配置仅接入 API 日志,外部 PostgreSQL 日志、主机指标和告警由各自部署单独配置。
## 验收与回退
发布成功后,从服务器确认 `curl -f http://127.0.0.1:8113/readyz`,并从外部确认 HTTPS 域名的 `/readyz`。真实微信登录、合法域名及小程序构建产物仍需单独验收;`/readyz` 只证明 API 可连接数据库。
每个发布镜像保留 `ballet-island-api:<提交短 SHA>` 标签。要回到某个已构建版本,应先确认其镜像仍在目标主机上,并评估数据库迁移是否与旧程序兼容;随后用同一组生产环境变量设置 `IMAGE_TAG=<旧提交短 SHA>`,在 `backend/` 执行:
```sh
docker compose --env-file /dev/null -f compose.prod.yaml up -d --wait --wait-timeout 180 --no-build
```
回退镜像不会回退数据库迁移。修改 Gitea 中 `DATABASE_URL` 的密码也不会修改统一数据库服务器里的账号密码,两处需要按数据库运维流程同步更新。