部署与发布¶
x-admin 是「前端静态资源 + 后端单二进制 + MySQL + Redis」的分离部署。推荐 Docker Compose;也可以手动部署到已有 Nginx。
1. 部署拓扑¶
┌──────────────┐
:80/:443 │ Nginx │
───────────►│ SPA + 反代 │
└──────┬───────┘
│ /admin/v1 /uploads /swagger /healthz
┌──────▼───────┐ ┌──────────┐
│ backend │───────►│ MySQL │
│ Go 单二进制 │───────►│ Redis │
└──────┬───────┘ └──────────┘
│
data/uploads(卷/对象存储)
2. Docker Compose(推荐)¶
cp .env.example .env
# 必填:JWT_SECRET=<32位以上随机值>
# 生产:WEBAUTHN_RP_ID / WEBAUTHN_RP_ORIGINS / CORS 改成真实域名
make compose-up
Compose 服务:
| 服务 | 镜像/构建 | 端口 | 说明 |
|---|---|---|---|
mysql |
mysql:8.4 |
内部 3306 | utf8mb4,数据卷 mysql-data |
redis |
redis:7.4-alpine |
内部 6379 | 数据卷 redis-data |
backend |
deploy/Dockerfile.backend |
8080 | 非 root 用户,健康检查 /healthz |
frontend |
deploy/Dockerfile.frontend |
80 | Nginx 托管 SPA 并反代 |
卷:
mysql-data:数据库redis-data:缓存(可丢,但会丢失会话)uploads:上传文件(必须持久化/备份)
常用命令:
make compose-logs # 跟踪日志
docker compose -f deploy/docker-compose.yml ps
docker compose -f deploy/docker-compose.yml restart backend
make compose-down
3. 手动部署¶
3.1 构建后端¶
把 x-admin、configs/config.yaml(或环境变量)、data/uploads 放到服务器:
建议用 systemd 管理:
[Unit]
Description=x-admin
After=network.target mysql.service redis.service
[Service]
User=xadmin
WorkingDirectory=/opt/x-admin
ExecStart=/opt/x-admin/x-admin -config /opt/x-admin/config.yaml
Restart=always
Environment=TZ=Asia/Shanghai
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
3.2 构建前端¶
如果前端与后端不同源,构建时指定:
同时把后端 cors.origins 配成前端域名。
3.3 Nginx¶
deploy/nginx.conf 是现成模板,核心规则:
client_max_body_size 64m;
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
location /admin/v1/ {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
}
# 生成 client/merchant 等自定义 API 面后,再补充对应反代:
# location /client/v1/ { proxy_pass http://127.0.0.1:8080; }
# location /merchant/v1/ { proxy_pass http://127.0.0.1:8080; }
location /uploads/ {
proxy_pass http://127.0.0.1:8080;
# 同样透传 Host / X-Real-IP / X-Forwarded-For
}
location /swagger { proxy_pass http://127.0.0.1:8080; }
location = /healthz { proxy_pass http://127.0.0.1:8080; }
location / {
try_files $uri $uri/ /index.html;
}
注意:
client_max_body_size要 ≥ 后端上传上限(默认 64MB 硬上限)。/uploads必须反代到后端,不能直接alias到磁盘目录,否则绕过鉴权与安全头。- SPA 刷新 404 必须靠
try_files ... /index.html。 - 生产必须有 HTTPS;Passkey 与 Secure Cookie 都依赖 HTTPS。
4. HTTPS 与域名¶
以 Certbot 为例:
然后更新:
app:
env: production
cors:
origins: ["https://example.com"]
webauthn:
rp_id: example.com
rp_origins: ["https://example.com"]
app.env=production 时:
- Gin 关闭 debug。
- 日志改为 JSON。
- 文件访问 Cookie 在 HTTPS 下加
Secure。 - 启动拒绝示例 JWT 密钥。
5. 生产检查清单¶
-
JWT_SECRET是随机值(≥32 字符),不是示例值 -
app.env=production - MySQL / Redis 使用强密码,且不暴露公网端口
-
server.trusted_proxies只包含实际代理网段 - HTTPS 已启用,HTTP 自动跳转
-
cors.origins是明确域名,不是* -
webauthn.rp_id/rp_origins与域名一致 -
data/uploads使用持久卷并纳入备份 - 数据库迁移已备份;
auto_migrate策略明确 -
/healthz已接入探针/监控 - 日志接入采集(JSON stdout)
- 限流依赖 Redis,未使用 memory
- 定时任务使用 Redis 锁(多副本)
- 默认
admin密码已修改,或已删除演示数据
6. 多副本与扩容¶
- 无状态:backend 不存本地状态,会话在 Redis,文件在共享存储。
- 文件:多副本必须共享卷(NFS/云盘)或换对象存储;本地磁盘会读不到别的副本写的文件。
- 定时任务:使用 Redis 后
job:lock:<id>保证同一任务只执行一次;memory 缓存只能单副本。 - 在线会话/监控:每个副本只统计自己的会话/指标;全局视图需要外部采集。
- 导出:worker 在各自进程内,产物写共享存储;任务表在 MySQL,天然共享。
- Nginx:可以水平扩展,但需保证 sticky 不必要(JWT 无状态)。
7. 备份与恢复¶
# 数据库
mysqldump --single-transaction --routines --triggers xadmin > xadmin.sql
# 上传文件
tar -czf uploads.tar.gz data/uploads
# 恢复
mysql xadmin < xadmin.sql
tar -xzf uploads.tar.gz
- 升级前备份数据库 + 上传目录。
- Redis 不是权威数据源:丢了只需重新登录,不需要恢复。
- 定期验证恢复流程,不要只备份不演练。
8. 升级流程¶
- 备份数据库与上传目录。
- 拉取新版本,构建后端/前端。
- 停旧进程(或滚动更新)。
- 启动新二进制,观察迁移日志与
/healthz。 - 验证登录、文件、导出、定时任务。
- 出问题回滚二进制 + 数据库备份(迁移一般不可逆,回滚前先看 down 脚本)。
9. CI / 发布工作流¶
| 工作流 | 触发 | 内容 |
|---|---|---|
.github/workflows/ci.yml |
push main / PR / 手动 | 后端 vet + test + coverage,前端 lint + test + build |
.github/workflows/release.yml |
Release published / 手动 | 交叉编译 linux/darwin/windows,打包配置与 README,构建前端 zip,附到 Release |
.github/workflows/docs.yml |
docs / mkdocs 变更 | 构建 MkDocs 并发布 GitHub Pages |
发布:
发布前建议把根目录 FRAMEWORK_VERSION 更新到本次 tag。它不参与二进制运行,只用于下游源码项目的升级基准;复制源码后新 git init、没有上游 git 历史的项目会优先读取它。.x-admin/framework.json 是下游本地缓存,不需要维护者处理。
10. 文档站¶
本站由 MkDocs Material 构建,发布在 GitHub Pages:
- 源文件在
docs/,导航在mkdocs.yml。 - 推送到
main且改动docs/**、SPEC.md、mkdocs.yml时自动构建。 - 本地预览:
11. 常见部署问题¶
| 现象 | 排查 |
|---|---|
| 前端刷新 404 | Nginx 缺 try_files $uri $uri/ /index.html |
| 接口 502 | 后端未启动/端口不对;看 Nginx error log 与 backend 日志 |
| 上传 413 | Nginx client_max_body_size 太小 |
| 图片 401 | /uploads 未反代到后端,或 Cookie 域/路径不对 |
| 登录后立刻退出 | JWT_SECRET 多副本不一致;或 Redis 不共享 |
| Passkey 注册失败 | RP ID/Origin 与访问地址不一致 |
| CORS 报错 | cors.origins 没包含前端来源;或代理丢了 Origin |
| 时间差 8 小时 | 容器 TZ 未设置,DSN loc 不一致 |
| 迁移启动失败 | 数据库版本高于内置迁移,或处于 dirty;按报错处理 |