7.7 KiB
7.7 KiB
P3 备份 + 可观测设计 / Backup & Observability Design
- 状态:设计已定(2026-09-30;三个选型向维护者提问未被应答,按推荐默认拍板并记录 §2,维护者可事后推翻)。
- 路线图:P3(第一波末项),依赖 P1 拓扑已定型。
- 涉及仓库:crearte-server(0.11.1 → 0.12.0)、crearte-deploy(0.5.1 → 0.6.0);crearte 前端零改动。
1. 背景与现状(2026-09-30 实地盘点)
- prod 栈(
db-prodpostgres:17-alpine /minio-prodpgsty/minio /api-prod/web-prod)数据落命名卷pgdata-prod、minio-data-prod,没有任何备份面;api-prod无宿主端口映射(仅web-prod的 8080 反代/api/与静态),MinIO console 宿主端口MINIO_PROD_PORT:-9001。 - server 日志 = 26 处 stdlib
log.Printf散在 12 个文件(handler×7、service×2、api 中间件×2、cmd/serve.go×1 等);无请求日志中间件;无/metrics。gin.New()仅挂Recovery+ CORS。 - 限流器
internal/api/ratelimit.go:进程内map[ip]*windowCounter固定窗口(bundle-key 60/min、login 10/min、register 5/h、upload 10/min、submission 20/h、reaction 30/min),maxTrackedIPs=10000惰性清理——纯单实例语义。 - 依赖基线:gin 1.11 / pgx 5.8 / aws-sdk-go-v2;无 prometheus 依赖。go.mod
go 1.24.1。
2. 选型拍板(默认=推荐项)
| # | 岔路 | 决定 | 理由 / 放弃项 |
|---|---|---|---|
| D1 | /metrics 实现 |
手写极简 exposition(零新依赖,Prometheus 文本格式 v0.0.4) | 单实例小站够用;代码可控可测;延续本仓最小依赖风格。放弃 prometheus/client_golang(依赖链重,Grafana 生态是未来事)与"不做"(ROADMAP 明列交付物)。 |
| D2 | 备份调度 | deploy 仓 backup.sh + 主机 crontab(runbook 给一行安装示例) |
脚本可手跑、可 CI --dry-run 验证、不绑 systemd。放弃栈内 cron sidecar(挂 docker.sock = 攻击面);systemd timer 留作 runbook 备选写法。 |
| D3 | 备份落点 | 主机目录 BACKUP_DIR(默认 ./backups/prod/<UTC 时间戳>/)+ 保留最新 BACKUP_KEEP_DAYS=7 份 |
防误删/防配置炸;同盘不防磁盘坏——3-2-1 与 rclone 异地钩子写进 runbook 作可选步骤,不配不生效(本轮不引入外部 S3 凭据依赖)。 |
3. server 设计(0.12.0)
3.1 结构化日志(slog)
config.Load()新增LOG_FORMAT=text|json(默认text)、LOG_LEVEL=debug|info|warn|error(默认info;非法值报错)。serve.go启动时据此配置slog.Default(输出 stderr;JSON 供 prod/容器采集)。- 26 处
log.Printf全量迁移到slog,机械映射规则:原msg k1=%s k2=%v→slog.<lvl>("<msg 前缀短语>", "k1", v1, "k2", v2);携带error=%v的一律slog.Error(..., "error", err);启动/生命周期一行slog.Info。文案短语与键名不变。 - 新增请求日志中间件(
internal/observability):每请求一行http_request,attrs =method、route(c.FullPath(),未匹配路由记nomatch,基数有界)、status、duration_ms、ip;/healthz整体跳过(不记日志也不计指标,防 scrape 噪声);status >= 500用 Error 级。 gin.Recovery()保留(panic 兜底),不动。
3.2 /metrics(手写)
internal/observability:并发安全 Registry(mutex +map[method|route|status]→{count,sumSeconds})。中间件一次计时同时喂日志与指标。- 指标族:
crearte_http_requests_total{method,route,status}(counter)crearte_http_request_duration_seconds_sum/_count{method,route,status}(counter 语义,够算均值)go_goroutines、go_memstats_alloc_bytes(runtime现值 gauge)crearte_pgpool_*(pgxpool.Stat():max_conns/total_conns/acquired_conns/idle_conns/acquire_count/empty_acquire_countgauge;pool 为 nil 时省略——纯 mock 测试路由无池)
GET /metrics与/healthz同口注册(RouteMetrics="/metrics"),无 auth、无 CORS 例外。- 暴露边界:prod 形态 api-prod 宿主零端口 →
/metrics仅容器网内可达,抓取配方docker compose exec -T api-prod wget -qO- http://127.0.0.1:8080/metrics。外网暴露 + allowlist/basic-auth 明确不做(未来接 Prometheus 抓取器再开 nginx 转发段)。 - 测试路径无池、无真栈:httptest 经真 router 打请求,断言文本行、计数增长、
nomatch桶存在。
3.3 限流单实例权衡(记录,不改造)
server docs/README.md 新增「已知权衡」节:多实例部署时每实例独立计数(有效限额 ≈ N×、重启清零、跨实例不共享);maxTrackedIPs 上限的内存数量级;扩多实例的前置条件是引入 Redis/网关层共享计数。只记录现状语义,不改限流器。
4. deploy 设计(0.6.0)
- compose 结构零改动:备份全走
docker compose exec/run --no-deps一次性容器(db-prod自带 pg_dump/pg_restore/psql,unix socket trust 免口令;minio-prod镜像自带mc,凭据经容器 env 注入而非命令行字面量)。 scripts/backup.sh:--dry-run:不碰 docker,仅校验.env存在、POSTGRES_PASSWORD/MINIO_ROOT_*非空(:?守卫)并打印计划——CI 步骤用它。- 预检:docker 可用、
compose --profile prod config -q、db-prod存活(pg_isready)。 pg_dump --format=custom流式 →$BACKUP_DIR/prod/<UTC 时间戳>/database.dump。mc mirror --overwrite --remove桶(MINIO_BUCKET:-crearte)→ 同目录objects/。- 完整性:
pg_restore --list读 dump 头。 - manifest:全表精确行数(循环
pg_tables逐表count(*))+ 产物体积。 prod/latest软链指向本次目录;按目录名排序保留最新BACKUP_KEEP_DAYS份,只删^\d{8}T\d{6}Z$白名单形态目录。
scripts/restore-drill.sh:latest dump 恢复到 debug 栈db-test(drop/recreate 仅限该测试库),逐表行数与 manifest 对比,输出 PASS/FAIL。绝不连 prod 库。docs/BACKUP-RUNBOOK.md:定时约定(crontab 一行 + systemd timer 备选写法);恢复步骤(恢复到现有 prod 库 / 恢复到全新栈两条路径);对象侧mc mirror反向推送;异地 3-2-1 与 rclone 可选钩子;已知风险(同盘单拷贝、mc mirror非快照一致性——写侧对象上传少可接受、备份窗口内行数漂移 manifest 与 dump 快照的微小差)。.env.example增BACKUP_DIR/BACKUP_KEEP_DAYS注释示例;README prod 节链 runbook;validate.yml增backup.sh --dry-run步骤(CI 里先cp .env.example .env再 append 假值过:?守卫)。- CHANGELOG 0.6.0(英/中连续行)。
5. 验收标准(本机任务 4 实测)
- server:容器四连(gofmt/vet/build/test)+
TEST_DATABASE_URL集成层全绿、0 skip;新单测覆盖指标格式、中间件计数、nomatch桶、日志 JSON 合法(jq)、LOG_*解析。 - 实栈冒烟(prod profile 演练栈):打一轮真实请求 →
docker compose exec抓/metrics计数吻合、JSON 日志行可读;backup.sh全绿产出 dump + objects + manifest;restore-drill.sh对 db-test PASS;连跑两次 +BACKUP_KEEP_DAYS=1验证 latest 软链与修剪。 backup.sh --dry-run无栈可跑(CI 等价);deploy 新 workflow 步骤本机过。- crearte e2e:stack full-loop 不回归(中间件动过路由)。
6. 非目标
Prometheus/Grafana 服务端搭建、/metrics 外网暴露与鉴权、异地同步落地实现、PITR/WAL 归档、卷快照级备份、限流器改造、dev/debug 栈备份(仅 prod profile)。