This commit is contained in:
+1
-1
@@ -9,6 +9,6 @@ coverage/
|
|||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
|
!/miniprogram/.env.uat
|
||||||
project.private.config.json
|
project.private.config.json
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,20 @@ TARO_APP_API_BASE_URL=https://api.example.com pnpm dev
|
|||||||
TARO_APP_API_BASE_URL=https://api.example.com pnpm build
|
TARO_APP_API_BASE_URL=https://api.example.com pnpm build
|
||||||
```
|
```
|
||||||
|
|
||||||
`TARO_APP_API_BASE_URL` 是公开接口地址,不自动读取后端 `.env`;未设置时使用本机默认地址。
|
`TARO_APP_API_BASE_URL` 是公开接口地址,不自动读取后端 `.env`;普通开发和构建未设置时使用本机默认地址。
|
||||||
|
|
||||||
|
### UAT 线上联调
|
||||||
|
|
||||||
|
在 `miniprogram/` 中运行:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm dev:uat # 监听源码变化,连接线上 API
|
||||||
|
pnpm build:uat # 一次性构建线上联调包
|
||||||
|
```
|
||||||
|
|
||||||
|
UAT 通过 Taro 的 `--mode uat` 加载已纳入版本控制的 `.env.uat`,默认接口为 `https://ballet.bcc-life.cn`。配置文件只包含公开接口地址;显式设置的 `TARO_APP_API_BASE_URL` 环境变量可覆盖该地址。普通 `pnpm dev`、`pnpm build` 不加载 `.env.uat`。
|
||||||
|
|
||||||
|
两种模式都输出到 `dist/`,微信开发者工具继续导入 `miniprogram/`。切换模式前停止正在运行的监听命令,重新构建后在开发者工具中编译。UAT 使用真实微信登录并读写线上数据,需在小程序后台将 `https://ballet.bcc-life.cn` 配置为 request 合法域名,线上后端的 `WECHAT_APP_ID` 须与小程序 AppID 一致。会话缓存按接口地址隔离,本地登录会话不会用于线上请求。
|
||||||
|
|
||||||
首次登录原子创建八个可编辑项目,练习数据从零开始。表单支持补记、更正和误记删除;项目改名同步显示于历史,有记录的项目移除时归档。近期列表按最后登记或更正时间排序,使刚保存的补记也能立即看到;日历和统计使用实际练习日期,统一按 Asia/Shanghai 解释,周一开始一周。
|
首次登录原子创建八个可编辑项目,练习数据从零开始。表单支持补记、更正和误记删除;项目改名同步显示于历史,有记录的项目移除时归档。近期列表按最后登记或更正时间排序,使刚保存的补记也能立即看到;日历和统计使用实际练习日期,统一按 Asia/Shanghai 解释,周一开始一周。
|
||||||
|
|
||||||
|
|||||||
+25
-2
@@ -7,7 +7,7 @@
|
|||||||
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 权限,只应给可信仓库和维护者使用。
|
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`,不会打印展开后的密钥。
|
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。跨服务器还需放通应用主机到数据库的连接,并确认数据库允许该用户和来源地址访问。
|
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:8080` 空闲。HTTPS 反向代理将 API 域名转发到该地址,并在微信小程序后台配置 request 合法域名。生产 Compose 仅将 API 绑定到回环地址,不发布数据库端口。
|
4. 确保宿主机 `127.0.0.1:8113` 空闲。HTTPS 反向代理将 API 域名转发到该地址,并在微信小程序后台配置 request 合法域名。生产 Compose 仅将 API 绑定到回环地址,不发布数据库端口。
|
||||||
5. 首次上线前在统一数据库服务器上创建数据库和账号,确定备份位置及恢复步骤。本地 `compose.yaml` 的 `ballet-island_postgres_data` 卷不会被生产 Compose 使用;若数据仍在该卷中,须先迁移到统一数据库服务器。
|
5. 首次上线前在统一数据库服务器上创建数据库和账号,确定备份位置及恢复步骤。本地 `compose.yaml` 的 `ballet-island_postgres_data` 卷不会被生产 Compose 使用;若数据仍在该卷中,须先迁移到统一数据库服务器。
|
||||||
|
|
||||||
## 发布流程
|
## 发布流程
|
||||||
@@ -18,9 +18,32 @@
|
|||||||
|
|
||||||
API 启动时会在事务和数据库锁保护下应用尚未执行的版本迁移,当前没有独立迁移命令。发布前应在统一数据库服务器上备份,未来涉及不兼容 schema 的改动须先制定迁移与回退顺序。工作流不会自动回滚数据库;失败后先检查容器状态和日志,再决定恢复备份或重新部署。
|
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:8080/readyz`,并从外部确认 HTTPS 域名的 `/readyz`。真实微信登录、合法域名及小程序构建产物仍需单独验收;`/readyz` 只证明 API 可连接数据库。
|
发布成功后,从服务器确认 `curl -f http://127.0.0.1:8113/readyz`,并从外部确认 HTTPS 域名的 `/readyz`。真实微信登录、合法域名及小程序构建产物仍需单独验收;`/readyz` 只证明 API 可连接数据库。
|
||||||
|
|
||||||
每个发布镜像保留 `ballet-island-api:<提交短 SHA>` 标签。要回到某个已构建版本,应先确认其镜像仍在目标主机上,并评估数据库迁移是否与旧程序兼容;随后用同一组生产环境变量设置 `IMAGE_TAG=<旧提交短 SHA>`,在 `backend/` 执行:
|
每个发布镜像保留 `ballet-island-api:<提交短 SHA>` 标签。要回到某个已构建版本,应先确认其镜像仍在目标主机上,并评估数据库迁移是否与旧程序兼容;随后用同一组生产环境变量设置 `IMAGE_TAG=<旧提交短 SHA>`,在 `backend/` 执行:
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,16 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
observability.logs: "true"
|
||||||
|
observability.project: "ballet-island"
|
||||||
|
observability.service: "api"
|
||||||
|
observability.env: "production"
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: "20m"
|
||||||
|
max-file: "5"
|
||||||
environment:
|
environment:
|
||||||
HTTP_ADDR: :8080
|
HTTP_ADDR: :8080
|
||||||
DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL to the production PostgreSQL URL}
|
DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL to the production PostgreSQL URL}
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Public endpoint only; server credentials must not be stored here.
|
||||||
|
TARO_APP_API_BASE_URL=https://ballet.bcc-life.cn
|
||||||
@@ -9,7 +9,9 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "taro build --type weapp --watch",
|
"dev": "taro build --type weapp --watch",
|
||||||
|
"dev:uat": "taro build --type weapp --watch --mode uat",
|
||||||
"build": "taro build --type weapp",
|
"build": "taro build --type weapp",
|
||||||
|
"build:uat": "taro build --type weapp --mode uat",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"test:client": "node --test tests/practice.test.cjs"
|
"test:client": "node --test tests/practice.test.cjs"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user