签入版本
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# 11.4 后端部署
|
||||
|
||||
:::tip 概述
|
||||
后端为单二进制部署:go build 产物 + manifest 配置 + resource 静态资源 + ip2region 数据文件。生产用 systemd/supervisor 守护,开启优雅重启。
|
||||
:::
|
||||
|
||||
## 编译
|
||||
|
||||
```sh
|
||||
# 本机编译
|
||||
go build -o bin/admin-server main.go
|
||||
|
||||
# Linux 交叉编译(Windows 开发机部署 Linux 服务器)
|
||||
GOOS=linux GOARCH=amd64 go build -o bin/admin-server main.go
|
||||
|
||||
# 静态编译(无 CGO 依赖,容器友好)
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/admin-server main.go
|
||||
```
|
||||
|
||||
## 部署产物清单
|
||||
|
||||
```text
|
||||
admin-server 二进制
|
||||
manifest/config/config.yaml 生产配置(或环境变量覆盖)
|
||||
manifest/data/ip2region.xdb 归属地库
|
||||
resource/static/ 静态资源(验证码字体等)
|
||||
resource/swagger/ API 文档页面
|
||||
upload/ 上传目录(持久卷,提前建好并授权)
|
||||
```
|
||||
|
||||
## systemd 配置示例
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/admin-server.service
|
||||
[Unit]
|
||||
Description=EasyGoAdmin Server
|
||||
After=network.target mysql.service redis.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/easygoadmin
|
||||
ExecStart=/opt/easygoadmin/admin-server
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
# 生产密钥走环境变量(不写配置文件;前缀必须全大写 GF_,Linux 环境变量大小写敏感)
|
||||
Environment=GF_JWT_SECRET=your-random-secret
|
||||
Environment=GF_APP_ERRORMSG=false
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
```sh
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now admin-server
|
||||
journalctl -u admin-server -f # 日志跟随
|
||||
```
|
||||
|
||||
## 启动验证
|
||||
|
||||
```sh
|
||||
curl http://127.0.0.1:8000/api/health # {"code":0,...,"status":"up"}
|
||||
curl -s http://127.0.0.1:8000/api.json | head # OpenAPI
|
||||
# 启动日志检查:
|
||||
# 无 IdentityLoader/LogSink 告警(接线正常)
|
||||
# Redis 连接正常(无 memory fallback 告警)
|
||||
```
|
||||
|
||||
## 优雅重启(graceful)
|
||||
|
||||
```yaml
|
||||
server:
|
||||
graceful: true # 已默认开启
|
||||
```
|
||||
|
||||
生产发版流程:部署新二进制 → systemctl restart(优雅重启:处理完存量请求后换新进程)→ health 验证。多实例滚动重启实现零停机。
|
||||
|
||||
## 端口与反代
|
||||
|
||||
```text
|
||||
服务监听 :8000(server.address)——生产不直接对外
|
||||
前置 Nginx 终止 HTTPS + 反代:
|
||||
location /api/ → proxy_pass http://127.0.0.1:8000
|
||||
location /upload/ → proxy_pass http://127.0.0.1:8000(或直挂文件服务)
|
||||
location /swagger → proxy_pass(可选:收紧为内网)
|
||||
反代后把 Nginx 地址加入 api.proxyIps(IP 与限流准确性)
|
||||
```
|
||||
|
||||
## 日志与监控
|
||||
|
||||
```text
|
||||
应用日志:logger.path(默认 log/)——轮转交给 logrotate
|
||||
运行监控:/api/server/info(内存/磁盘/DB/缓存/调度器)
|
||||
健康探针:/api/health(负载均衡挂载)
|
||||
告警建议:磁盘 >85%、health 失败、mode=memory、Scheduler 不健康
|
||||
```
|
||||
|
||||
## 常见部署问题
|
||||
|
||||
| 症状 | 排查 |
|
||||
|------|------|
|
||||
| 启动即退出 | 配置文件路径(须从部署目录运行)/ 端口占用 / Go 版本 |
|
||||
| 上传 404 | uploadDir 路径与权限;SetServerRoot 静态映射 |
|
||||
| 归属地显示 "-" | ip2region.xdb 缺失或路径错 |
|
||||
| 限流/IP 不准 | api.proxyIps 未含反代地址 |
|
||||
| 多实例行为不一致 | Redis 不可用(mode=memory) |
|
||||
@@ -0,0 +1,87 @@
|
||||
# 11.9 备份与恢复
|
||||
|
||||
:::tip 概述
|
||||
备份对象:数据库(核心)、上传文件、配置与密钥。恢复演练与备份同等重要——未经验证的备份不算备份。
|
||||
:::
|
||||
|
||||
## 备份对象与策略
|
||||
|
||||
| 对象 | 方式 | 频率 | 保留 |
|
||||
|------|------|------|------|
|
||||
| MySQL | mysqldump / xtrabackup | 全量每日 + binlog | 30 天 + 月度归档 |
|
||||
| 上传文件 upload/ | rsync 快照 / 对象存储同步 | 每日增量 | 30 天 |
|
||||
| 配置与密钥 | 私有仓库/密钥库 | 变更时 | 长期 |
|
||||
| Redis | 不备份(缓存可重建) | — | — |
|
||||
|
||||
::: warning Redis 不备份的边界
|
||||
Redis 存黑名单/在线会话/验证码——不备份可接受(重启后会话重登录,令牌黑名单靠 TTL 自然过期)。若"强退立即生效"是合规要求,接受重启窗口内的令牌复活风险或启用 Redis 持久化(RDB)。
|
||||
:::
|
||||
|
||||
## MySQL 备份脚本示例
|
||||
|
||||
```sh
|
||||
#!/bin/bash
|
||||
# backup.sh — 每日全量
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
DB=easygoadmin.goframe.elevue
|
||||
DIR=/backup/mysql
|
||||
|
||||
mysqldump -uroot -p"$DB_PASS" --single-transaction --routines --triggers \
|
||||
"$DB" | gzip > "$DIR/${DB}_${DATE}.sql.gz"
|
||||
|
||||
# 保留 30 天
|
||||
find "$DIR" -name "*.sql.gz" -mtime +30 -delete
|
||||
```
|
||||
|
||||
crontab:`0 2 * * * /opt/backup/backup.sh`(避开 LogClean 02:30 高峰可调 01:30)。
|
||||
|
||||
## 上传文件备份
|
||||
|
||||
```sh
|
||||
# rsync 增量(upload 目录每日变化小)
|
||||
rsync -a --delete /opt/easygoadmin/upload/ /backup/upload/
|
||||
```
|
||||
|
||||
多实例共享卷场景:备份一份共享卷即可。
|
||||
|
||||
## 恢复流程
|
||||
|
||||
```text
|
||||
数据库恢复:
|
||||
1. 停服务(防写入)
|
||||
2. mysql -uroot -p "$DB" < 解压后的.sql(或 xtrabackup --copy-back)
|
||||
3. 验证:表数量 / admin 账号 / 行数抽查
|
||||
4. 启服务 → health + 冒烟
|
||||
|
||||
上传文件恢复:
|
||||
rsync 回源目录 → 验证图片/文件可访问
|
||||
```
|
||||
|
||||
## 恢复演练(季度)
|
||||
|
||||
```text
|
||||
□ 独立恢复环境拉起(勿在生产演练)
|
||||
□ 全量备份恢复 → 数据完整性核对(表/行数/关键数据)
|
||||
□ 记录恢复耗时(RTO 评估)
|
||||
□ 修正备份脚本缺陷
|
||||
```
|
||||
|
||||
## RPO / RTO 参考
|
||||
|
||||
| 方案 | RPO(最大数据丢失) | RTO(恢复耗时) |
|
||||
|------|--------------------|-----------------|
|
||||
| 每日全量 | ≤24h | 30min~2h(视数据量) |
|
||||
| 全量+binlog 增量 | 近零(秒级) | 1h~4h |
|
||||
| 主从/双机热备 | 秒级 | 分钟级(自动切换) |
|
||||
|
||||
按业务容忍度选择——中后台系统每日全量+binlog 通常足够。
|
||||
|
||||
## 备份安全
|
||||
|
||||
```text
|
||||
□ 备份文件加密存储(含全量数据=敏感资产)
|
||||
□ 异地存储(防机房级故障)
|
||||
□ 访问权限最小化(备份账号只读)
|
||||
□ 传输通道加密(scp/rsync over ssh)
|
||||
□ 备份保留与合规对齐(操作日志保留 90 天为默认下限)
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
# 11.1 部署 Checklist
|
||||
|
||||
:::tip 概述
|
||||
生产上线前逐项核对:安全基线(密钥/万能码/错误信息)、依赖服务(MySQL/Redis)、配置核对、静态资源、验证步骤。本清单可直接用于发布评审。
|
||||
:::
|
||||
|
||||
## 上线前 Checklist
|
||||
|
||||
### 安全基线(必须)
|
||||
|
||||
```text
|
||||
□ jwt.secret 已更换为高强度随机值(与环境隔离)
|
||||
□ captcha.bypassCode 已留空(万能验证码禁止上线)
|
||||
□ app.demo = false
|
||||
□ app.errorMsg = false(不返回详细错误信息)
|
||||
□ server.errorStack = false
|
||||
□ database.default.debug = false
|
||||
□ redis.default.pass 强密码
|
||||
□ database 使用独立账号(非 root,最小权限)
|
||||
□ api.proxyIps 只列真实反代出口
|
||||
□ file.fileExt 按业务收紧(公网上传场景)
|
||||
□ rateLimit.enable = true(authLimit 不调大)
|
||||
```
|
||||
|
||||
### 依赖服务
|
||||
|
||||
```text
|
||||
□ MySQL 已建库并导入 document/mysql/ 定稿脚本(含种子数据)
|
||||
□ 13 个 uk_* 唯一索引齐全(对照 4.9 数据库设计)
|
||||
□ Redis 可用(/api/cache/info mode 应为 redis)
|
||||
□ 数据库/Redis 内网可达,不暴露公网
|
||||
□ 磁盘空间(上传目录/日志/备份分区)
|
||||
□ 服务器时区与 app.timezone 一致
|
||||
```
|
||||
|
||||
### 后端
|
||||
|
||||
```text
|
||||
□ go build 编译通过(目标平台交叉编译注意 GOOS/GOARCH)
|
||||
□ config.yaml 生产配置核对(database/redis/jwt/file/cors)
|
||||
□ file.uploadDir 挂持久卷(容器场景)/独立分区
|
||||
□ manifest/data/ip2region.xdb 存在(归属地功能)
|
||||
□ resource/static、resource/swagger 随二进制部署
|
||||
□ 优雅重启已启用(server.graceful)
|
||||
□ 进程守护(systemd/supervisor)与开机自启
|
||||
□ 日志目录可写(logger.path)
|
||||
```
|
||||
|
||||
### 前端
|
||||
|
||||
```text
|
||||
□ pnpm build 构建产物 dist/
|
||||
□ Nginx 托管静态 + try_files SPA 回退
|
||||
□ /api 与 /upload 反代后端
|
||||
□ VITE 环境变量与后端域名一致(子路径部署核对 PUBLIC_PATH)
|
||||
□ HTTPS 证书(前置代理终止)
|
||||
```
|
||||
|
||||
### 验证
|
||||
|
||||
```text
|
||||
□ GET /api/health → {"code":0,...,"data":{"status":"up"}}
|
||||
□ /swagger 页面可访问(OpenAPI)
|
||||
□ admin 登录成功 → 首页菜单正常
|
||||
□ 登录日志/操作日志正常落库
|
||||
□ /api/cache/info mode=redis
|
||||
□ /api/server/info 全项正常(磁盘/DB/调度器)
|
||||
□ 限流生效(快速刷新触发 429)
|
||||
□ 上传→预览→保存→转正 全链路
|
||||
□ 定时任务挂载(daemonStatus / Scheduler.mounted)
|
||||
```
|
||||
|
||||
## 发布顺序(推荐)
|
||||
|
||||
```text
|
||||
1. 备份数据库与旧版本二进制
|
||||
2. 停旧服务(或优雅重启)
|
||||
3. 部署新二进制 + 配置
|
||||
4. 启动 → health 检查
|
||||
5. 冒烟(登录/核心接口)
|
||||
6. 观察日志(启动告警/错误)
|
||||
7. 出现问题回滚(旧二进制 + 备份库)
|
||||
```
|
||||
|
||||
## 回滚预案
|
||||
|
||||
```text
|
||||
□ 旧二进制可一键切回
|
||||
□ 数据库变更已评估可回滚性(新增表/列可保留;破坏性变更需还原备份)
|
||||
□ 前端旧 dist 保留可切回
|
||||
□ 回滚演练记录
|
||||
```
|
||||
@@ -0,0 +1,110 @@
|
||||
# 11.11 CI/CD 流水线
|
||||
|
||||
:::tip 概述
|
||||
CI 门禁:后端 go vet + gofmt + build + test;前端 vue-tsc + eslint + 构建。CD:镜像/产物构建 → 部署 → 健康检查 → 冒烟。本节给出流水线模板与质量门建议。
|
||||
:::
|
||||
|
||||
## CI 门禁(每次提交)
|
||||
|
||||
```yaml
|
||||
# 流水线示意(GitLab CI / GitHub Actions 通用)
|
||||
stages: [check, build]
|
||||
|
||||
backend-check:
|
||||
stage: check
|
||||
script:
|
||||
- go build ./...
|
||||
- go vet ./...
|
||||
- test -z "$(gofmt -l .)" # 格式检查(有输出即失败)
|
||||
- go test ./... # sqlite 集成 + hooks MySQL 集成
|
||||
|
||||
frontend-check:
|
||||
stage: check
|
||||
working_dir: ui
|
||||
script:
|
||||
- pnpm install --frozen-lockfile
|
||||
- pnpm type:check # vue-tsc
|
||||
- pnpm lint:eslint
|
||||
```
|
||||
|
||||
## 构建与发布(tag/主干)
|
||||
|
||||
```yaml
|
||||
build-push:
|
||||
stage: build
|
||||
script:
|
||||
# 后端镜像(多阶段 Dockerfile,位于 docker/ 目录,上下文为项目根)
|
||||
- docker build -f docker/Dockerfile -t $REGISTRY/easygoadmin:$TAG .
|
||||
- docker push $REGISTRY/easygoadmin:$TAG
|
||||
# 前端产物
|
||||
- cd ui && pnpm build && rsync -az dist/ $SERVER:/opt/ui/dist/
|
||||
only: [main, tags]
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script:
|
||||
- ssh $SERVER "docker pull $REGISTRY/easygoadmin:$TAG && cd docker && docker compose up -d easygoadmin"
|
||||
- ssh $SERVER "curl -sf http://127.0.0.1:8000/api/health" # 健康检查失败即中止
|
||||
# 冒烟:登录 + 核心接口
|
||||
```
|
||||
|
||||
## 质量门建议
|
||||
|
||||
```text
|
||||
必过(阻断合并):
|
||||
go build / go vet / gofmt
|
||||
go test(sqlite 集成 + hooks MySQL 集成)
|
||||
vue-tsc / eslint
|
||||
|
||||
增强(告警或分级):
|
||||
黄金样本回归(hack/golden 接口响应比对——录制参数带 pageSize=3 等)
|
||||
pnpm audit / go vulncheck(依赖漏洞)
|
||||
覆盖率报告(逐步提升)
|
||||
```
|
||||
|
||||
## 黄金样本回归(项目内置能力)
|
||||
|
||||
```text
|
||||
hack/golden/after*/ 接口响应黄金样本(回归比对基线)
|
||||
录制规范:带录制参数(pageSize=3 等)保证响应稳定可比
|
||||
DIFF 归因三类:预期变更(更新样本)/ 非预期回归(修复)/ 环境差异(排除)
|
||||
接入 CI:接口回归 job 对比新旧响应,非零差异阻断
|
||||
```
|
||||
|
||||
## 发布流水线完整链
|
||||
|
||||
```text
|
||||
提交 → CI 门禁 → 合并主干 → 构建镜像/产物 → 部署测试环境
|
||||
→ 冒烟/回归 → 人工确认 → 生产发布(滚动/蓝绿)
|
||||
→ health 验证 → 观察日志 → 完成/回滚
|
||||
```
|
||||
|
||||
## 密钥管理
|
||||
|
||||
```text
|
||||
CI 变量(加密变量/密钥库):
|
||||
REGISTRY 凭据 / 部署 SSH 密钥 / GF_JWT_SECRET 等生产密钥
|
||||
原则:
|
||||
* 密钥不进代码仓库与镜像层
|
||||
* 生产密钥仅 deploy 阶段注入(check 阶段不可见)
|
||||
* 轮换有流程(改密钥 → 滚动重启 → 旧值失效窗口评估)
|
||||
```
|
||||
|
||||
## 分支策略建议
|
||||
|
||||
```text
|
||||
main 可发布主干(保护分支,CI 必过)
|
||||
feature/* 功能分支 → MR → CI 门禁 → review 合并
|
||||
hotfix/* 紧急修复 → 双人确认 → 快速通道发布
|
||||
release/* 版本发布分支(打 tag 触发 CD)
|
||||
```
|
||||
|
||||
## 流水线排障
|
||||
|
||||
| 症状 | 处理 |
|
||||
|------|------|
|
||||
| gofmt 失败 | gofmt -w . 本地修复后提交 |
|
||||
| go test 失败 | 集成测试依赖 MySQL/sqlite——CI 需起测试库服务 |
|
||||
| vue-tsc 失败 | 类型错误本地 pnpm type:check 复现 |
|
||||
| 部署后 health 失败 | 流水线自动中止;查容器日志/配置注入 |
|
||||
| 镜像过大 | 多阶段构建 + alpine + .dockerignore |
|
||||
@@ -0,0 +1,75 @@
|
||||
# 11.3 数据库迁移
|
||||
|
||||
:::tip 概述
|
||||
部署场景的两类迁移:① 全新环境初始化(导入定稿脚本);② 存量环境跨方言迁移(整库迁移器)。迁移器用法详见 [7.6 数据库迁移工具](../tools/db-migrate),本节聚焦部署视角。
|
||||
:::
|
||||
|
||||
## 场景一:全新环境初始化
|
||||
|
||||
```sh
|
||||
# MySQL(推荐基准)
|
||||
mysql -uroot -p -e "CREATE DATABASE \`easygoadmin.goframe.elevue\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
|
||||
mysql -uroot -p easygoadmin.goframe.elevue < document/mysql/easygoadmin.goframe.elevue.sql
|
||||
|
||||
# 验证:25 张表 + admin 账号 + 种子数据(菜单/字典/参数/租户)
|
||||
```
|
||||
|
||||
脚本含超管账号(admin/123456——**上线后立即改密**)、菜单权限树、字典种子、参数种子。
|
||||
|
||||
## 场景二:跨方言整库迁移
|
||||
|
||||
```sh
|
||||
go run ./cmd/migrator \
|
||||
-src-type=mysql -src-host=127.0.0.1 -src-hostport=3306 \
|
||||
-src-database=easygoadmin.goframe.elevue -src-username=root -src-password=root \
|
||||
-dst-type=pgsql -dst-host=127.0.0.1 -dst-hostport=5432 \
|
||||
-dst-database=easygoadmin.goframe.elevue -dst-username=postgres -dst-password=123456 \
|
||||
-drop-target-first
|
||||
```
|
||||
|
||||
部署注意:
|
||||
|
||||
```text
|
||||
□ 目标库确认无他用(-drop-target-first 清空全部表!)
|
||||
□ 迁移前备份源库与目标库
|
||||
□ 先 -tables 白名单迁小表验证方言转换
|
||||
□ 观察五阶段输出与行数校验 PASS/FAIL
|
||||
□ FAIL 表排查后 -tables 单独重迁
|
||||
□ 迁移后切应用配置:database.default.type/link → 重启
|
||||
```
|
||||
|
||||
限制提醒:无外键/视图/存储过程;无主键表拒绝搬运;mssql/oracle 作为源库时二级索引丢失;逐表串行。
|
||||
|
||||
## 场景三:存量环境版本升级(同方言)
|
||||
|
||||
```text
|
||||
本项目无自动 schema 升级工具——增量变更走 ALTER 工作流:
|
||||
|
||||
1. 以 document/mysql/ 定稿为基准整理 ALTER 脚本
|
||||
(含列注释枚举、uk_* 唯一索引)
|
||||
2. 备份 → 执行 ALTER
|
||||
3. gf gen dao 刷新(如本地验证)→ 部署新二进制
|
||||
4. 同步 document/ 其余四方言脚本
|
||||
5. 租户级新表 → hooks.tenantTables 登记(随代码发布)
|
||||
6. 验证:登录/核心接口/新功能冒烟
|
||||
```
|
||||
|
||||
## 版本升级通用纪律
|
||||
|
||||
```text
|
||||
□ 数据库变更先行(向后兼容的加列/加表最安全)
|
||||
□ 应用滚动(新版本兼容旧数据结构)
|
||||
□ 破坏性变更(删列/改枚举值)走双版本过渡:先发兼容版 → 清理旧结构
|
||||
□ 每步可回滚(备份 + 旧二进制)
|
||||
□ 黄金样本接口回归(hack/golden)
|
||||
```
|
||||
|
||||
## 定稿脚本与漂移
|
||||
|
||||
```text
|
||||
五方言脚本位于 document/{mysql,postgresql,sqlserver,oracle,sqlite}/
|
||||
MySQL 版为规范基准,后四份待人工校对
|
||||
|
||||
已知漂移注意:以 resource 定稿逻辑为准核对 uk_* 索引——
|
||||
导入后验证 13 个唯一索引齐全(并发唯一性最终保证)
|
||||
```
|
||||
@@ -0,0 +1,177 @@
|
||||
# 11.6 Docker 生产部署
|
||||
|
||||
:::tip 概述
|
||||
项目已内置 Docker 化文件:`docker/Dockerfile`(多阶段静态编译)、`docker/docker-compose.yml`(后端 + MySQL + Redis 编排)、`docker/.env.example`(变量模板)、根目录 `.dockerignore`。开发环境快速体验见 [2.7 Docker 快速体验](../quickstart/docker),本节聚焦生产环境的加固与运维要点。
|
||||
:::
|
||||
|
||||
## 基线:内置文件的生产就绪点
|
||||
|
||||
`docker/Dockerfile` 已包含的生产就绪设计(注释同步自源文件):
|
||||
|
||||
```text
|
||||
□ CGO_ENABLED=0 静态编译(sqlite 驱动为 modernc 系,无 CGO 依赖)
|
||||
□ -trimpath -ldflags "-s -w":去除本地路径与调试符号,二进制更小、不泄露构建路径
|
||||
□ 运行时资产四件独立层:manifest/config、manifest/data(ip2region.xdb)、
|
||||
resource(字体/swagger)、templates(生成器模板)——业务代码变动不破坏资产层缓存
|
||||
□ 非 root 运行(USER app,uid 1000)
|
||||
□ tzdata + Asia/Shanghai(日志与 gcron 定时任务依赖系统时区)
|
||||
□ HEALTHCHECK 打 /api/health(公开白名单路径,不带 /v1)
|
||||
□ 镜像内无源码、无密钥(仅二进制 + 资产)
|
||||
```
|
||||
|
||||
`docker/docker-compose.yml` 已包含:
|
||||
|
||||
```text
|
||||
□ 敏感配置全部 GF_ 前缀环境变量注入,镜像内 config.yaml 保持默认
|
||||
(同一镜像可直接部署 dev/prod,无需为环境单独打镜像)
|
||||
□ mysql/redis healthcheck + depends_on condition: service_healthy(后端等依赖就绪再启动)
|
||||
□ upload/log 宿主卷持久化(../upload、../log,相对 compose 文件目录需 ../ 回项目根)
|
||||
□ 定稿 SQL 挂 docker-entrypoint-initdb.d(仅空数据卷导入一次)
|
||||
□ 日志驱动 json-file + max-size 500m + max-file 10 轮转
|
||||
□ restart: always
|
||||
```
|
||||
|
||||
## 生产环境必改项
|
||||
|
||||
```sh
|
||||
cd docker
|
||||
copy .env.example .env
|
||||
```
|
||||
|
||||
```bash
|
||||
# docker/.env —— 生产清单
|
||||
# JWT_SECRET 生成(≥32 字节密码学随机,各环境独立、勿复用):
|
||||
# Linux: openssl rand -hex 32
|
||||
# Windows: [Convert]::ToBase64String([Security.Cryptography.RandomNumberGenerator]::GetBytes(32))
|
||||
# 轮换此值后所有已签发令牌立即失效,用户需重新登录(安排发版窗口执行)
|
||||
JWT_SECRET=<生成的随机串> # 令牌签名密钥,生产必改
|
||||
MYSQL_PASSWORD=<强密码> # MySQL 密码
|
||||
REDIS_PASSWORD=<强密码> # Redis 密码
|
||||
|
||||
# 连云数据库/Redis(RDS 等)时同时改主机与端口,compose 自带的 mysql/redis
|
||||
# 服务可保留(不依赖即可)或整段删除后去掉后端的 depends_on
|
||||
MYSQL_HOST=rm-xxx.mysql.rds.aliyuncs.com
|
||||
MYSQL_PORT=3306
|
||||
MYSQL_USER=<云库账号>
|
||||
REDIS_HOST=r-xxx.redis.rds.aliyuncs.com
|
||||
REDIS_PORT=6379
|
||||
# 容器内 127.0.0.1 指向容器自身,连宿主机请用 host.docker.internal
|
||||
```
|
||||
|
||||
镜像内 `manifest/config/config.yaml` 另有两处默认值偏开发,compose 已用环境变量覆盖,若用 `docker run` 部署需自行带上:
|
||||
|
||||
```sh
|
||||
# 生产覆盖(compose 已内置,docker run 场景勿遗漏)
|
||||
GF_CAPTCHA_BYPASSCODE="" # 清空万能验证码(默认 618618 仅开发用)
|
||||
GF_APP_ERRORMSG="false" # 关闭响应中的详细错误堆栈
|
||||
```
|
||||
|
||||
::: warning 定稿 SQL 与数据卷
|
||||
initdb.d 挂载**仅在 MySQL 数据卷为空时执行一次**。后续表结构变更需手动执行 SQL 或 `docker volume rm` 清卷重建——**生产环境严禁清卷**,变更走 [11.3 数据库迁移](./db-migrate) 的手工变更流程。
|
||||
:::
|
||||
|
||||
## 云服务替代(推荐拓扑)
|
||||
|
||||
生产推荐 MySQL/Redis 用云服务,compose 只编排后端:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
easygoadmin:
|
||||
image: easygoadmin:latest # 或私有仓库地址
|
||||
restart: unless-stopped # 比 always 更适合手工维护场景
|
||||
ports: ["8000:8000"]
|
||||
environment:
|
||||
GF_DATABASE_DEFAULT_LINK: "mysql:user:pass@tcp(rm-xxx.mysql.rds.aliyuncs.com:3306)/easygoadmin?charset=utf8mb4&parseTime=true&loc=Local"
|
||||
GF_REDIS_DEFAULT_ADDRESS: "r-xxx.redis.rds.aliyuncs.com:6379"
|
||||
GF_REDIS_DEFAULT_PASS: "<云 Redis 密码>"
|
||||
GF_FILE_UPLOADDIR: "/data/upload"
|
||||
GF_JWT_SECRET: "${JWT_SECRET}"
|
||||
GF_CAPTCHA_BYPASSCODE: ""
|
||||
GF_APP_ERRORMSG: "false"
|
||||
volumes:
|
||||
- /data/easygoadmin/upload:/data/upload # 云盘/NFS 绝对路径
|
||||
mem_limit: 1g # 防 OOM 拖垮宿主机
|
||||
logging:
|
||||
driver: json-file
|
||||
options: { max-size: "50m", max-file: "3" }
|
||||
```
|
||||
|
||||
## 前端 Nginx 托管与反代
|
||||
|
||||
`docker/` 只覆盖后端;前端 `ui/` 独立构建后由 Nginx 托管:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# 前端路由回退
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# 接口与静态上传反代后端
|
||||
location /api/ { proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }
|
||||
location /upload/ { proxy_pass http://backend:8000; }
|
||||
location /api.json { proxy_pass http://backend:8000; }
|
||||
}
|
||||
```
|
||||
|
||||
::: tip 受信代理与真实 IP(CDN / Nginx / Docker)
|
||||
后端取真实客户端 IP 的规则:**TCP 直连方**在 `api.proxyIps`(支持 CIDR)内才信任 `X-Forwarded-For` 首段 / `X-Real-IP`,否则直接记录直连方 IP。三层要点:
|
||||
|
||||
1. **Docker 端口映射模式下,容器内 TCP 对端恒为网桥网关**(如 172.19.0.1)——CDN/宿主机 Nginx 的回源经 NAT 后都是它。配置默认已含 `172.16.0.0/12`(Docker 网桥默认网段);自定义 docker 网桥子网时按实际网段补充(生产用 `GF_API_PROXYIPS` 覆盖)。
|
||||
2. **CDN 侧**须在回源请求中携带 `X-Forwarded-For: <真实客户端IP>`,且采用**覆盖**而非追加模式——若客户端自带伪造 XFF 被保留在首段,取首段会误判。
|
||||
3. **宿主机 Nginx 反代**场景:Nginx 配 `proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for`,并把 Nginx 网段加入 `GF_API_PROXYIPS`。
|
||||
|
||||
症状对照:登录/操作日志记成 172.19.0.1 → 第 1 层网段缺失;记成 CDN 回源 IP → 第 2/3 层 XFF 未传递。**切勿配置 0.0.0.0/0**(等于允许任意来源伪造 XFF)。
|
||||
:::
|
||||
|
||||
::: tip 公开路径勿加 /v1
|
||||
`/api/login`、`/api/register`、`/api/captcha`、`/api/oauth2/token`、`/api/health` 为网关免版本路径,反代与白名单三处须一致,Nginx 无需特殊处理(`location /api/` 前缀已覆盖)。
|
||||
:::
|
||||
|
||||
## 数据持久化
|
||||
|
||||
| 数据 | 容器内位置 | 持久化方式 |
|
||||
|------|-----------|-----------|
|
||||
| 上传文件 | /data/upload | 宿主目录卷 / NFS / 对象存储改造(多实例必须共享) |
|
||||
| 运行日志 | /opt/easygoadmin/log + stdout | stdout 走容器日志驱动轮转;文件卷可选 |
|
||||
| ip2region.xdb | manifest/data | 随镜像(只读),升级镜像即更新 |
|
||||
| 数据库 | MySQL 容器 / 云 RDS | 云优先,定时备份见 [11.9 备份与恢复](./backup) |
|
||||
| Redis | Redis 容器 / 云 Redis | 缓存可丢;JWT 黑名单与在线会话会重置(用户需重新登录) |
|
||||
|
||||
## 零停机发布
|
||||
|
||||
```text
|
||||
单实例:docker compose up -d --build(滚动间隙数秒)
|
||||
多实例:
|
||||
1. 起新容器(新版本)→ HEALTHCHECK 通过
|
||||
2. 摘旧容器流量(Nginx upstream / compose scale 滚动)
|
||||
3. 停旧容器
|
||||
约束:
|
||||
- 定时任务经 utility/cache 的 job:lock:{id} 执行锁(Redis SET NX),多实例不双跑
|
||||
- 上传目录必须共享卷,否则文件访问 404
|
||||
```
|
||||
|
||||
## k8s 要点
|
||||
|
||||
```text
|
||||
Deployment(replicas≥2)+ Service + Ingress(HTTPS)
|
||||
ConfigMap 挂配置 / Secret 挂密钥(GF_ 覆盖,勿改镜像内 config.yaml)
|
||||
PVC 挂 /data/upload(RWX 或对象存储改造)
|
||||
liveness/readinessProbe → /api/health(Dockerfile 已内置 HEALTHCHECK,探针复用同路径)
|
||||
HPA 按 CPU/内存(会话状态在 Redis,无状态可水平扩)
|
||||
```
|
||||
|
||||
## 镜像运维
|
||||
|
||||
```text
|
||||
□ 定期重建(基础镜像 CVE 修复)
|
||||
□ 私有仓库 + 拉取审计;镜像打版本 tag,避免仅用 latest
|
||||
□ 构建在项目根执行:docker build -f docker/Dockerfile -t easygoadmin:<tag> .
|
||||
□ .dockerignore 已排除 ui/bin/log/upload/document/hack/.env,镜像不含源码与密钥
|
||||
□ 容器内不使用代码生成器(产物写容器文件系统,重建即丢)
|
||||
```
|
||||
@@ -0,0 +1,68 @@
|
||||
# 11.2 环境规划
|
||||
|
||||
:::tip 概述
|
||||
推荐三套环境(开发/测试/生产)+ 环境差异经 GF_ 环境变量覆盖(不改配置文件)。本节给出环境矩阵、资源配置参考与拓扑。
|
||||
:::
|
||||
|
||||
## 环境矩阵
|
||||
|
||||
| 环境 | 用途 | 后端 | 数据库 | Redis |
|
||||
|------|------|------|--------|-------|
|
||||
| 开发 dev | 本地开发 | go run / bin | 本地 MySQL | 本地 Redis |
|
||||
| 测试 test | 联调/冒烟 | 独立部署 | 独立库 | 独立实例/db |
|
||||
| 生产 prod | 对外服务 | 多实例可选 | 主从/云 RDS | 集群/云 Redis |
|
||||
|
||||
## 部署拓扑
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────┐
|
||||
│ 生产网络(内网) │
|
||||
用户 ──443──▶ │ nginx/前置代理(HTTPS 终止 + 静态 + 反代) │
|
||||
│ │ │
|
||||
│ ├── admin-server ×N(:8000,优雅重启) │
|
||||
│ ├── MySQL 主从(或云 RDS) │
|
||||
│ └── Redis(集群/云,密码强记) │
|
||||
└──────────────────────────────────────────┘
|
||||
文件:uploadDir 独立持久卷/共享存储(多实例需共享或对象存储改造)
|
||||
归属地:manifest/data/ip2region.xdb 随二进制分发
|
||||
```
|
||||
|
||||
## 环境差异管理(GF_ 覆盖)
|
||||
|
||||
```sh
|
||||
# 容器/进程管理器注入环境变量,不改配置文件
|
||||
GF_DATABASE_DEFAULT_LINK="mysql:user:secret@tcp(db:3306)/easygoadmin?charset=utf8mb4&parseTime=true"
|
||||
GF_REDIS_DEFAULT_ADDRESS="redis:6379"
|
||||
GF_JWT_SECRET="prod-only-random-secret"
|
||||
GF_APP_ERRORMSG="false"
|
||||
```
|
||||
|
||||
原则:**同份二进制 + 不同环境变量 = 不同环境**;密钥类只走环境变量(不进配置文件/镜像层)。
|
||||
|
||||
## 资源配置参考
|
||||
|
||||
| 环境 | CPU | 内存 | 磁盘 | 说明 |
|
||||
|------|-----|------|------|------|
|
||||
| 开发 | 2 核 | 4 GB | 50 GB | 本机 MySQL/Redis 共享 |
|
||||
| 测试 | 2 核 | 4 GB | 100 GB | 独立服务 |
|
||||
| 生产(中小) | 4 核 | 8 GB | 200 GB+ | 单实例 + 数据库独立 |
|
||||
| 生产(多实例) | 2 核 × N | 4 GB × N | — | Redis 必须可用(限流/锁共享) |
|
||||
|
||||
## 多实例注意事项
|
||||
|
||||
```text
|
||||
必须共享 Redis(否则以下能力退化单机语义):
|
||||
限流计数 / 任务执行锁(job:lock)/ JWT 黑名单 / 在线用户 / 验证码 / 字典缓存
|
||||
|
||||
上传目录:多实例需共享存储(NFS/OSS 改造)——两段式 Rename 在共享卷语义下正常
|
||||
定时任务:策略 2/3 经 Redis 锁互斥(各实例都会 tick,抢锁失败放弃)
|
||||
数据库连接池:按实例数 × pool 配置核算数据库 max_connections
|
||||
```
|
||||
|
||||
## 时区与时间
|
||||
|
||||
```text
|
||||
app.timezone: "Asia/Shanghai" 应用时区
|
||||
数据库连接 loc=Local / 服务器时区保持一致
|
||||
定时任务 cron 按应用时区调度——跨时区部署注意
|
||||
```
|
||||
@@ -0,0 +1,114 @@
|
||||
# 11.5 前端部署
|
||||
|
||||
:::tip 概述
|
||||
前端构建为静态产物(dist/),生产由 Nginx 托管:静态文件 + SPA 路由回退 + /api 与 /upload 反代后端。支持根路径与子路径两种部署。
|
||||
:::
|
||||
|
||||
## 构建
|
||||
|
||||
```sh
|
||||
cd ui
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build # production 模式
|
||||
# 产物:ui/dist/
|
||||
```
|
||||
|
||||
多环境构建:`pnpm build:test / build:uat / build:prod`(对应 .env.* 文件)。
|
||||
|
||||
## Nginx 配置(根路径)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name example.com;
|
||||
root /opt/easygoadmin/ui/dist;
|
||||
index index.html;
|
||||
|
||||
# gzip
|
||||
gzip on;
|
||||
gzip_types text/css application/javascript application/json image/svg+xml;
|
||||
|
||||
# SPA 路由回退
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# 接口反代
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
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_read_timeout 60s;
|
||||
client_max_body_size 20m; # 对齐 file.maxSize(含表单 multipart)
|
||||
}
|
||||
|
||||
# 上传静态文件(或直挂文件服务/CDN)
|
||||
location /upload/ {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
}
|
||||
|
||||
# API 文档(可选:收紧为内网/Basic Auth)
|
||||
location /swagger { proxy_pass http://127.0.0.1:8000; }
|
||||
location /api.json { proxy_pass http://127.0.0.1:8000; }
|
||||
|
||||
# 静态资源缓存(带 hash 的资源长缓存)
|
||||
location /assets/ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 子路径部署
|
||||
|
||||
```text
|
||||
1. 构建:.env 配 VITE_PUBLIC_PATH=/admin/(部署公网路径)
|
||||
2. Nginx:
|
||||
location /admin/ { alias /opt/dist/; try_files $uri $uri/ /admin/index.html; }
|
||||
3. 接口仍走 /api(baseURL 与后端一致)——注意 Nginx location 匹配
|
||||
```
|
||||
|
||||
## HTTPS
|
||||
|
||||
```text
|
||||
HTTPS 由前置代理/Nginx 终止:
|
||||
listen 443 ssl http2;
|
||||
ssl_certificate /etc/ssl/example.pem;
|
||||
ssl_certificate_key /etc/ssl/example.key;
|
||||
# HTTP → HTTPS 重定向
|
||||
后端服务本身 HTTP 内网(:8000 不对外)
|
||||
```
|
||||
|
||||
## 部署验证
|
||||
|
||||
```text
|
||||
□ 首页加载(无白屏)
|
||||
□ 登录 → 首页菜单/仪表盘正常
|
||||
□ 刷新子路由不 404(SPA 回退生效)
|
||||
□ 接口经 /api 反代正常(Network 面板核对)
|
||||
□ 上传/图片预览正常(/upload 反代或 CDN)
|
||||
□ 403/401 页面(exception/403)可达
|
||||
□ 构建资源长缓存生效(二次访问 assets 命中缓存)
|
||||
```
|
||||
|
||||
## 发布流程
|
||||
|
||||
```text
|
||||
1. pnpm build(CI 或本地)
|
||||
2. dist 上传服务器(rsync/scp,保留旧版本目录)
|
||||
3. Nginx root 切换或软链切换(原子切换)
|
||||
4. 验证 → 出错回滚软链
|
||||
index.html 不缓存(no-cache),静态 assets 长缓存——发布即生效
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
| 症状 | 排查 |
|
||||
|------|------|
|
||||
| 刷新 404 | try_files 回退缺失或子路径 /admin/index.html 写错 |
|
||||
| 接口 404/502 | /api 反代地址/端口;后端未启动 |
|
||||
| 跨域报错 | 应为同域反代——检查是否前端直连了后端地址 |
|
||||
| 上传失败(413) | Nginx client_max_body_size 小于 file.maxSize |
|
||||
| 登录后回登录页 | access_token 读取/存储;后端 401(令牌/时钟) |
|
||||
| 老页面缓存 | index.html 被缓存——加 no-cache 头 |
|
||||
@@ -0,0 +1,98 @@
|
||||
# 11.7 性能调优
|
||||
|
||||
:::tip 概述
|
||||
性能调优按层次展开:数据库(连接池/索引/大表)、Redis(连接池/内存)、应用(缓存策略/日志异步)、前端(构建/缓存)。本节给出调优点与观测方法。
|
||||
:::
|
||||
|
||||
## 数据库层
|
||||
|
||||
```text
|
||||
连接池(config.yaml database.default):
|
||||
maxIdle/maxActive/寿命等 gdb 池参数按并发核算
|
||||
多实例部署:实例数 × 池上限 ≤ 数据库 max_connections
|
||||
|
||||
索引:
|
||||
筛选列(LikeFields/EqFields 命中列)建 idx_
|
||||
唯一列 uk_*(同时是并发唯一性保证)
|
||||
city 大表:查询必须走 parent_code/pid/area_code 索引路径
|
||||
|
||||
大表:
|
||||
city(78万+):禁全表 List;级联懒加载
|
||||
日志三表:LogClean 按 job.logRetentionDays(默认90天)分批清理
|
||||
长表分页深翻页:管理后台低频可接受;高频报表走导出/离线
|
||||
|
||||
debug 开关:database.default.debug 生产必须 false(SQL 日志开销大)
|
||||
```
|
||||
|
||||
## Redis 层
|
||||
|
||||
```text
|
||||
连接池:redis.default idle/maxActive 按并发调
|
||||
内存:maxmemory + 淘汰策略(evictedKeys 持续增长 = 容量不足)
|
||||
观测:GET /api/cache/info(usedMemory/hitRate/connectedClients/fragRatio)
|
||||
|
||||
缓存命中率治理(hitRate 偏低时):
|
||||
对照 4.8 TTL 设计原则检查各键 TTL
|
||||
字典类可适当加长(有 RefreshCache 兜底)
|
||||
状态判定类宁短勿长
|
||||
```
|
||||
|
||||
## 应用层
|
||||
|
||||
```text
|
||||
异步化(已内置):
|
||||
操作日志异步批量落库(通道2048+worker,不阻塞业务)
|
||||
定时任务 is_sync=0 异步执行
|
||||
|
||||
缓存(内置三层):
|
||||
进程内(dictTextCache/反射计划)→ Redis(身份/租户/字典/黑名单)→ DB
|
||||
新增缓存遵循 TTL 原则(4.8)
|
||||
|
||||
分批(内置):
|
||||
导出 ChunkQuery 500/页防 OOM
|
||||
LogClean 单批 5000 行、单表单次 20 万行上限
|
||||
|
||||
限流:rateLimit 按压测调参(防误伤;authLimit 不建议调大)
|
||||
|
||||
panic 兜底 + errorStack=false(生产减少堆栈序列化开销)
|
||||
```
|
||||
|
||||
## 前端层
|
||||
|
||||
```text
|
||||
构建:
|
||||
Vite 代码分割(路由级懒加载——动态路由天然按页面拆分)
|
||||
gzip/brotli(Nginx gzip on + 静态预压缩可选)
|
||||
assets 长缓存(带 content hash)
|
||||
|
||||
运行:
|
||||
列表虚拟滚动(超大列表场景,Element Plus virtualized)
|
||||
图片懒加载/压缩(上传侧控制尺寸)
|
||||
keep-alive 控制缓存页面数量(内存 vs 体验平衡)
|
||||
```
|
||||
|
||||
## 压测与观测
|
||||
|
||||
```text
|
||||
压测前准备:
|
||||
临时调大 rateLimit.limit 或按压测 IP 绕过(防误判 429)
|
||||
app.errorMsg=false(减少错误序列化差异)
|
||||
独立压测环境(勿压生产)
|
||||
|
||||
观测指标:
|
||||
后端:/api/server/info(goroutines/内存/gc)
|
||||
DB:慢查询日志(开启 query_time 阈值)
|
||||
Redis:hitRate/connectedClients/evictedKeys
|
||||
系统:CPU/内存/磁盘 IO/网络
|
||||
```
|
||||
|
||||
## 常见瓶颈定位
|
||||
|
||||
| 症状 | 方向 |
|
||||
|------|------|
|
||||
| 接口慢且稳定慢 | 索引缺失/慢查询(开 debug 看 SQL);N+1 查询 |
|
||||
| 越用越慢 | 内存泄漏(goroutines/heapObjects 增长);连接泄漏 |
|
||||
| 并发上不去 | 数据库连接池打满;Redis 单点 |
|
||||
| 偶发卡顿 | GC(调内存);磁盘 IO(日志/上传) |
|
||||
| 大列表超时 | city 类大表全量查询;改级联/分页 |
|
||||
| 峰值 429 多 | 限流阈值偏低或前端轮询过频 |
|
||||
@@ -0,0 +1,85 @@
|
||||
# 11.8 安全加固清单
|
||||
|
||||
:::tip 概述
|
||||
安全加固按层次组织:配置层(密钥/开关)、网络层(暴露面/代理)、数据层(账号/备份)、应用层(上传/权限)。威胁→控制全景见 [4.10 安全架构设计](../architecture/security)。
|
||||
:::
|
||||
|
||||
## 配置层加固(上线必须)
|
||||
|
||||
```text
|
||||
□ jwt.secret 高强度随机(≥32 字节随机串),与环境隔离
|
||||
□ captcha.bypassCode = ""(万能验证码清空)
|
||||
□ app.demo = false
|
||||
□ app.errorMsg = false(不暴露内部错误)
|
||||
□ server.errorStack = false
|
||||
□ database.default.debug = false
|
||||
□ redis.default.pass 强密码(非 123456)
|
||||
□ file.fileExt 按业务收紧(公网上传移除 js/css/sql)
|
||||
□ cors.allowOrigin 收敛为具体域名(非同域部署时)
|
||||
```
|
||||
|
||||
## 网络层加固
|
||||
|
||||
```text
|
||||
□ 数据库/Redis 内网访问(安全组/防火墙仅放应用网段)
|
||||
□ 后端 :8000 不直接对外(仅 Nginx 反代)
|
||||
□ HTTPS 全站(前置代理终止;HSTS 可选)
|
||||
□ api.proxyIps 只列真实反代出口(防 XFF 伪造)
|
||||
□ /swagger 与 /api.json 收紧(内网/Basic Auth/关闭暴露)
|
||||
□ 管理后台可加 IP 白名单(Nginx allow/deny)
|
||||
```
|
||||
|
||||
## 数据层加固
|
||||
|
||||
```text
|
||||
□ 数据库独立账号(非 root;仅目标库 DML 权限)
|
||||
□ 定期备份(见 11.9 备份与恢复)+ 恢复演练
|
||||
□ 初始 admin 强密码(导入种子后立即改)
|
||||
□ 敏感配置不进 git(.env 注入;私有仓库)
|
||||
□ 日志目录权限最小化(含操作日志脱敏后仍属敏感)
|
||||
```
|
||||
|
||||
## 应用层加固
|
||||
|
||||
```text
|
||||
□ 限流开启(authLimit 10/分钟/IP 不调大——防暴力破解)
|
||||
□ 权限最小化授予(新接口必须声明 permission;定期审计角色授权)
|
||||
□ 超管账号专人管理(通配权限 *:*:*)
|
||||
□ 在线用户定期巡检(异常会话强退——双令牌拉黑)
|
||||
□ 操作日志定期审计(登录日志看异常 IP/归属地)
|
||||
□ 上传目录禁止执行权限(Nginx 对 /upload 不解析脚本)
|
||||
□ 富文本外链不本地化已防 SSRF(保持该行为,勿自行"增强")
|
||||
```
|
||||
|
||||
## 运维层加固
|
||||
|
||||
```text
|
||||
□ 服务器最小化安装(关无用端口/服务)
|
||||
□ SSH 密钥登录、禁 root 直登
|
||||
□ 系统安全补丁定期更新
|
||||
□ 容器场景:非 root 运行、镜像扫描
|
||||
□ 日志采集与告警(错误激增/登录失败激增)
|
||||
□ 应急预案:禁用账号/封禁 IP/回滚流程演练
|
||||
```
|
||||
|
||||
## 定期审计项(月度)
|
||||
|
||||
```text
|
||||
1. 角色权限盘点:长期不用的角色/权限回收
|
||||
2. 在线用户:异地/异常会话清理
|
||||
3. 登录日志:失败尝试 Top IP(配合限流阈值评估)
|
||||
4. 操作日志:高危操作(删除/导出)追溯
|
||||
5. 备份可用性:随机抽恢复演练
|
||||
6. 依赖漏洞:go mod / pnpm audit
|
||||
7. /api.json 权限点核对(防新接口漏配 permission)
|
||||
```
|
||||
|
||||
## 应急响应
|
||||
|
||||
| 事件 | 动作 |
|
||||
|------|------|
|
||||
| 账号泄露 | 禁用账号 + 在线用户强退(双令牌拉黑)+ 改密 + 审计日志 |
|
||||
| 令牌泄露 | RemoveToken 撤销指定令牌;必要时改 jwt.secret 全量失效 |
|
||||
| 数据库泄露 | 改库密码/轮换密钥;评估备份与日志暴露面 |
|
||||
| 恶意上传 | 收紧 fileExt;清理上传目录;审计上传记录 |
|
||||
| DDoS/刷接口 | 调小 rateLimit 阈值;前置 WAF/CDN;封禁 IP 段 |
|
||||
@@ -0,0 +1,83 @@
|
||||
# 11.10 故障排查手册
|
||||
|
||||
:::tip 概述
|
||||
按故障类别速查:启动失败 / 认证授权 / 数据异常 / 性能 / 缓存 / 定时任务 / 文件上传。每类给出症状→定位→处理。
|
||||
:::
|
||||
|
||||
## 启动类
|
||||
|
||||
| 症状 | 定位 | 处理 |
|
||||
|------|------|------|
|
||||
| 启动即退出 | 端口占用 / 配置缺失 / Go 版本 | netstat 查端口;从部署目录运行;核对 go 版本 |
|
||||
| 数据库连接失败 | link 串/网络/账号 | telnet 端口;核对 database.default |
|
||||
| Redis 告警降级 | 地址/密码/网络 | 修复后自动恢复(运行期探测);/api/cache/info 验证 |
|
||||
| IdentityLoader 告警 | 依赖接线缺失 | 查 internal/cmd 空白导入与 logic init |
|
||||
| panic 堆栈 | 看日志堆栈 | 定位代码 bug;errorStack=false 时看服务端日志 |
|
||||
|
||||
## 认证授权类
|
||||
|
||||
| 症状 | 定位 | 处理 |
|
||||
|------|------|------|
|
||||
| 全部 401 | jwt.secret 变更 / 时钟 | 改密钥后需重新登录;核对服务器时间 |
|
||||
| 登录后单接口 401 | 令牌过期/黑名单 | 刷新令牌;查是否被强退拉黑 |
|
||||
| 403 | 权限链四环 | gmeta permission ↔ 菜单节点 ↔ 角色勾选 ↔ 身份缓存(DEL identity 键) |
|
||||
| 权限改了不生效 | 身份缓存 300s | SavePermission 应主动清;查清理链;等过期 |
|
||||
| 租户 1005/1006 | 租户过期/禁用 | 改租户状态/过期;等 60s 或 DEL tenant:info |
|
||||
|
||||
## 数据类
|
||||
|
||||
| 症状 | 定位 | 处理 |
|
||||
|------|------|------|
|
||||
| 租户串数据 | tenantTables 漏登记 | 补登记 + 复查查询是否绕过 dao.Ctx |
|
||||
| 已删除数据可见 | 绕过 dao.Ctx / 手写条件 | 修复为 dao.Ctx 唯一入口 |
|
||||
| 唯一冲突 1062 裸报错 | 无预检(设计内兜底) | 补 UniqueFields(Group=索引范围) |
|
||||
| 新字段不返回 | entity/Req/Res/ToData 四处 | 逐一核对(见 3.3 加字段工作流) |
|
||||
| 字典文本不显示 | SerializeMaps/Text 字段 | spec 声明 + 结构体 {Field}Text |
|
||||
|
||||
## 性能类
|
||||
|
||||
| 症状 | 定位 | 处理 |
|
||||
|------|------|------|
|
||||
| 接口慢 | 慢查询 / N+1 | debug 开 SQL 日志;查 userExpandNames 类批查是否缺失 |
|
||||
| 内存增长 | 泄漏 | /api/server/info heapObjects/goroutines 趋势 |
|
||||
| 并发瓶颈 | 连接池 | DB 池/Redis 池参数核算 |
|
||||
| 磁盘满 | 日志/上传/备份 | LogClean 是否运行;upload 清理;扩容 |
|
||||
| 大表超时 | city 全表查询 | 改级联路径(parent_code 索引) |
|
||||
|
||||
## 缓存类
|
||||
|
||||
| 症状 | 定位 | 处理 |
|
||||
|------|------|------|
|
||||
| mode=memory | Redis 不可用 | 修复 Redis;多实例期间限流/锁退化单机 |
|
||||
| 键值陈旧 | TTL/失效点 | 对照 4.8 键全集;DEL 指定键 |
|
||||
| 多实例不一致 | Redis 未共享 | 确认 mode=redis |
|
||||
| 命中率低 | TTL 过短 | /api/cache/info hitRate;调 TTL |
|
||||
|
||||
## 定时任务类
|
||||
|
||||
| 症状 | 定位 | 处理 |
|
||||
|------|------|------|
|
||||
| 任务不执行 | 未发布/cron 无效/未挂载 | status=1?daemonStatus?Scheduler.mounted vs expected |
|
||||
| 重复执行 | 锁失效 | Redis 可用性(job:lock 依赖);execute_policy 核对 |
|
||||
| 执行失败 | 看 job_log exception | HTTP 超时(30s)/handler panic/数据库错误 |
|
||||
| 任务日志堆积 | LogClean 未跑 | 检查 LogClean 任务与 retentionDays |
|
||||
|
||||
## 文件上传类
|
||||
|
||||
| 症状 | 定位 | 处理 |
|
||||
|------|------|------|
|
||||
| 上传 413/拒绝 | maxSize/白名单/嗅探 | 对照 file.* 配置 |
|
||||
| 图片 404 | domainUrl/转正失败 | 库中相对路径 vs GetFileUrl;查 temp 是否被清 |
|
||||
| 富文本图片不显示 | 读取绕过基座 | 走 Detail/List(自动 Expand) |
|
||||
| temp 堆积 | CleanTemp 未生效 | gcron 注册/tempExpireDays>0 |
|
||||
|
||||
## 排障通用路径
|
||||
|
||||
```text
|
||||
1. /api/health 服务活着吗
|
||||
2. /api/cache/info 缓存模式与健康
|
||||
3. /api/server/info 全景体检(DB/磁盘/调度器)
|
||||
4. 启动日志 告警项逐一确认
|
||||
5. /api.json 接口与文档一致性
|
||||
6. 操作/登录日志 异常行为追溯
|
||||
```
|
||||
Reference in New Issue
Block a user