185 lines
6.3 KiB
Markdown
185 lines
6.3 KiB
Markdown
# 生产部署说明
|
||
|
||
目标访问地址:
|
||
|
||
```text
|
||
https://主站域名 # CADDY_DOMAIN,哈夫币系统
|
||
https://选号网域名 # CADDY_SHOW_DOMAIN(可选),hfb_show 静态选号站
|
||
https://monitor.主站域名 # Beszel 监控
|
||
```
|
||
|
||
生产部署使用 Caddy 作为公网入口,只暴露宿主机 `80`、`443` 端口。Caddy 会自动申请和续签 HTTPS 证书,并直接托管前端静态资源(主站 `dist` 与可选的选号网 `show-dist` 在构建 Caddy 镜像时打入);后端、MySQL、Redis、MinIO 均通过 Docker 内网通信。选号网与主站**共用同一 backend**,浏览器访问选号网域名下的 `/api/*` 由 Caddy 反代到本机 backend,**不会直连数据库**。
|
||
|
||
部署前请确认:
|
||
|
||
- 主站域名 A 记录已解析到服务器公网 IP;启用选号网时,选号网域名也要解析到同一 IP。
|
||
- 服务器安全组或防火墙已放行 `80/tcp` 和 `443/tcp`。
|
||
- 服务器上没有其他服务占用 `80`、`443`。
|
||
- 国内服务器如需对外访问,请先确认域名备案和云厂商限制。
|
||
|
||
## 1. 准备环境变量
|
||
|
||
```bash
|
||
cp backend/.env.prod.example backend/.env
|
||
```
|
||
|
||
编辑 `backend/.env`,至少替换以下占位值:
|
||
|
||
```text
|
||
CADDY_DOMAIN
|
||
CADDY_EMAIL
|
||
MYSQL_ROOT_PASSWORD
|
||
MYSQL_PASSWORD
|
||
MYSQL_DSN
|
||
JWT_SECRET
|
||
MINIO_ROOT_USER
|
||
MINIO_ROOT_PASSWORD
|
||
STORAGE_ACCESS_KEY_ID
|
||
STORAGE_SECRET_ACCESS_KEY
|
||
BACKEND_UID
|
||
BACKEND_GID
|
||
```
|
||
|
||
`BACKEND_UID`、`BACKEND_GID` 填写服务器业务用户的数值 ID,例如:
|
||
|
||
```bash
|
||
id -u yml
|
||
id -g yml
|
||
```
|
||
|
||
后端容器会使用该 UID/GID 运行。部署脚本由 root 执行时会自动把已有日志修正为该属主;非 root 执行且发现旧的 root 日志时,会给出需要执行的 `chown` 命令后停止。
|
||
|
||
### 迁移对象存储到阿里云 OSS
|
||
|
||
创建私有 Bucket 后,先保持 `STORAGE_*` 指向当前 MinIO,在 `backend/.env` 配置 OSS 镜像端:
|
||
|
||
```text
|
||
STORAGE_MIRROR_ENDPOINT=https://oss-cn-hangzhou-internal.aliyuncs.com
|
||
STORAGE_MIRROR_BUCKET=hfb-sys-assets
|
||
STORAGE_MIRROR_ACCESS_KEY_ID=change-oss-access-key-id
|
||
STORAGE_MIRROR_SECRET_ACCESS_KEY=change-oss-access-key-secret
|
||
STORAGE_MIRROR_REGION=cn-hangzhou
|
||
STORAGE_MIRROR_BUCKET_LOOKUP=dns
|
||
```
|
||
|
||
RAM 凭证只授予该 Bucket 的列举、读取、写入对象权限,不要使用阿里云主账号 AccessKey。镜像端启用后,新上传文件会同步写入 MinIO 和 OSS;任一镜像写入失败时上传会失败,避免数据库引用未同步文件。
|
||
|
||
部署支持镜像配置的镜像后,执行以下命令同步历史对象;命令可重复执行,不会删除 MinIO 数据:
|
||
|
||
```bash
|
||
./scripts/migrate-minio-to-oss.sh
|
||
./scripts/migrate-minio-to-oss.sh --verify
|
||
```
|
||
|
||
校验通过后,将 `STORAGE_*` 改为同一组 OSS 配置,再清空所有 `STORAGE_MIRROR_*` 配置并重新部署。杭州 ECS 必须使用 `oss-cn-hangzhou-internal.aliyuncs.com`,避免跨公网访问。MinIO 保留至少 30 天后再考虑下线。
|
||
|
||
### 同机部署选号网(hfb_show,可选)
|
||
|
||
在 `backend/.env` 增加(域名不要带 `https://`):
|
||
|
||
```text
|
||
CADDY_SHOW_DOMAIN=show.example.com
|
||
HFB_SHOW_DIR=../hfb_show
|
||
```
|
||
|
||
要求:
|
||
|
||
1. 服务器上 `hfb_show` 与 `hfb_sys` 同级目录(或 `HFB_SHOW_DIR` 指向真实路径)。
|
||
2. 部署机有 Docker 即可;**不必安装 Node/npm**(无 npm 时脚本用 `node:24-alpine` 容器构建选号网)。
|
||
3. 选号网域名 DNS 已指向本机。
|
||
|
||
一键脚本会自动:
|
||
|
||
- `npm ci && npm run build` 选号网;
|
||
- 将 `dist` 同步到 `deploy/caddy/show-dist`;
|
||
- 生成 Caddy 站点配置 `deploy/caddy/conf.d/show.caddy`;
|
||
- 构建 Caddy 镜像时打入主站 + 选号网静态资源;
|
||
- 为选号网域名申请 HTTPS,并把 `/api/*` 反代到 `backend:8080`。
|
||
|
||
不配置 `CADDY_SHOW_DOMAIN` 则行为与原来一致,仅部署主站。
|
||
|
||
如果正式对外运营,还需要配置真实短信和实名服务,避免继续使用测试配置。
|
||
|
||
## 2. 一键部署
|
||
|
||
```bash
|
||
./scripts/deploy-prod.sh
|
||
```
|
||
|
||
脚本会自动完成:
|
||
|
||
- 检查 `backend/.env` 是否仍指向 `127.0.0.1`、`localhost` 或占位值。
|
||
- 使用内置 MinIO 时,检查 `STORAGE_ACCESS_KEY_ID` / `STORAGE_SECRET_ACCESS_KEY` 是否和 `MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD` 一致。
|
||
- 构建并启动生产容器。
|
||
- 使用配置的非 root UID/GID 运行后端,并检查日志目录属主。
|
||
- 通过 Caddy 自动申请或续签 HTTPS 证书。
|
||
- 等待 MySQL、Redis、MinIO 就绪。
|
||
- 按顺序执行尚未应用的数据库迁移。
|
||
- 重启后端并检查 `https://你的域名/api/health`。
|
||
|
||
如需部署完成后直接跟随查看后端日志:
|
||
|
||
```bash
|
||
./scripts/deploy-prod.sh --logs
|
||
```
|
||
|
||
后端运行日志会写入宿主机:
|
||
|
||
```text
|
||
backend/logs/app-YYYY-MM-DD.log
|
||
```
|
||
|
||
测试阶段需要清空并重建数据库时:
|
||
|
||
```bash
|
||
./scripts/deploy-prod.sh --reset-db
|
||
```
|
||
|
||
## 3. 手动启动服务
|
||
|
||
```bash
|
||
set -a
|
||
source backend/.env
|
||
set +a
|
||
docker compose -f deploy/docker-compose.prod.yml up -d --build
|
||
```
|
||
|
||
手动执行 `docker compose` 时需要先导出 `CADDY_DOMAIN` 和 `CADDY_EMAIL`,否则 Caddy 无法读取证书域名配置。优先推荐使用一键部署脚本,它会自动从 `backend/.env` 导出这两个变量。
|
||
|
||
## 4. 手动执行数据库迁移
|
||
|
||
通常直接使用一键部署脚本即可。确实需要手动迁移时,使用后端镜像内置的 goose:
|
||
|
||
```bash
|
||
MYSQL_DSN="$(awk -F= '$1=="MYSQL_DSN"{sub(/^[^=]*=/,""); print; exit}' backend/.env)"
|
||
case "$MYSQL_DSN" in
|
||
*multiStatements=*) GOOSE_DSN="$MYSQL_DSN" ;;
|
||
*\?*) GOOSE_DSN="${MYSQL_DSN}&multiStatements=true" ;;
|
||
*) GOOSE_DSN="${MYSQL_DSN}?multiStatements=true" ;;
|
||
esac
|
||
|
||
docker compose -f deploy/docker-compose.prod.yml run --rm --no-deps backend \
|
||
/app/goose -dir /app/migrations mysql "$GOOSE_DSN" up
|
||
```
|
||
|
||
## 5. 验证
|
||
|
||
```bash
|
||
curl https://主站域名/health
|
||
curl https://主站域名/api/health
|
||
# 若启用了选号网:
|
||
curl -I https://选号网域名/
|
||
curl https://选号网域名/api/health
|
||
curl "https://选号网域名/api/listings?page=1&page_size=1"
|
||
```
|
||
|
||
浏览器访问:
|
||
|
||
```text
|
||
https://主站域名
|
||
https://主站域名/你配置的后台入口/login
|
||
https://选号网域名 # 选号网首页
|
||
```
|
||
|
||
Caddy 证书和 ACME 账号数据保存在 Docker 卷 `caddy_data`、`caddy_config` 中。不要随意删除这两个卷,否则 Caddy 会重新申请证书,频繁重建可能触发证书签发频率限制。
|