衍水集团营销人员工资管理系统
部署硬件需求说明
================================================================
版本：v1.0
日期：2026-10-21
适用：内部系统部署（数十人并发，无公网访问）
依据：仓库实际运行架构（docker/docker-compose.dev.yml、backend/pyproject.toml、
      backend/app/core/db.py、docs/备份恢复手册.md、ai-aux/AI_CONTEXT.md）


================================================================
一、结论摘要（先看这一节）
================================================================

推荐配置：2 核 CPU / 8 GB 内存 / 100 GB SSD（Linux 单机）
最低配置：2 核 CPU / 4 GB 内存 / 100 GB SSD
演示配置：1 核 CPU / 2 GB 内存 / 40 GB  SSD（仅限试用，不适合正式使用）

一句话：内部系统的瓶颈不在 CPU，而在「内存余量」和「磁盘容量」。
     内存从 4 GB 升到 8 GB 的成本极低，却能一次性消除以后所有内存不足的排查；
     磁盘不要按当前数据量买，要按「备份 + 恢复演练」的双倍空间买。


================================================================
二、负载前提（为什么是这个配置）
================================================================

以下事实全部来自本仓库，不是通用模板：

1) 内部系统、数十人并发
   出处：ai-aux/AI_CONTEXT.md:101「并发极低（内部系统数十人）」
   含义：CPU 核心数不是瓶颈，2 核足以支撑全部并发请求。

2) 全栈同步架构 + 单 worker，重活走线程池
   出处：CLAUDE.md（分层约定）、backend/app/core/db.py:29
   含义：一个请求占用一个线程；绩效批量核算、Excel 导出会短暂吃满一个核，
        因此要留出至少 1 个空闲核给后端，避免请求排队。

3) MySQL 8.0 + Redis 7 双容器，Redis 开启 AOF
   出处：docker/docker-compose.dev.yml:11、:44
   含义：MySQL 是内存消耗大户（默认配置下性能很差，必须调参）；
        Redis 只存验证码、Token 黑名单、登录失败计数，用量极小（<50 MB）。

4) 备份策略：每日全量 gz + binlog 增量，各自保留 30 天
   出处：docs/备份恢复手册.md:11
   含义：磁盘需按「业务数据 + 索引 + 30 天 binlog + 30 份全量备份」计算。

5) 恢复演练需恢复到独立临时库 payroll_drill_*
   出处：docs/备份恢复手册.md:45
   含义：演练瞬间需要约「2 倍数据库体积」的可用空间。
        这是最容易被低估的一项，磁盘卡着下限买会在第一次演练时爆盘。

6) 报表导出用 openpyxl 全量载入内存
   出处：backend/app/services/report_export.py:18
   含义：导出大报表时后端进程内存峰值会翻倍，必须在内存预算里留余量。


================================================================
三、正式生产推荐配置（内部系统，数十人）
================================================================

项目              规格                              说明
----------------------------------------------------------------
CPU              2 核（推荐 4 核）                 2 核给容器与 MySQL，
                                                   1 核给 uvicorn（workers=2 上限），
                                                   1 核余量给系统与备份任务。

内存             8 GB（最低 4 GB）                 8 GB 才有余量承受
                                                   「导出 + 核算 + 备份」同时发生。

磁盘             100 GB SSD（推荐 200 GB）         必须是 SSD。HDD 上 MySQL 的
                                                   ON UPDATE CURRENT_TIMESTAMP
                                                   高频写入会明显拖慢。

网络             内网千兆                          无需公网 IP、域名、SSL 证书、CDN。

操作系统         Linux                             推荐 Ubuntu 22.04 LTS / Rocky 9。
                 （Ubuntu 22.04 / Rocky 9）        备份、恢复、巡检脚本全部是 .sh
                                                   （scripts/ 目录），Linux 为原生路径。
                                                   若必须用 Windows Server，需 2022 +
                                                   WSL2 或 Git Bash 运行脚本。


================================================================
四、内存分配明细（8 GB 的具体切法）
================================================================

组件              建议占用      关键设置与说明
----------------------------------------------------------------
MySQL 8.0         3.5 GB        innodb_buffer_pool_size = 2G
                                ★ 默认仅 128 MB，不调会大量走磁盘，
                                  MySQL 会成为整机最慢的一环。

后端 uvicorn      1.5 GB        workers = 2
                                报表导出时单 worker 峰值可达 600 MB ~ 1 GB
                                （openpyxl 全量载入内存）。

Redis 7           256 MB        maxmemory 256mb
                                实际用量 < 50 MB，设置上限只为兜底防失控。

Nginx + 系统      1 GB          静态资源（前端 dist）+ 反向代理 /api。

余量              1.75 GB       mysqldump 备份进程、binlog 压缩、系统缓存。

----------------------------------------------------------------
4 GB 内存版（最低配置）的分配：
  MySQL 1.5~2 GB（innodb_buffer_pool_size = 1G）
  后端 0.8~1 GB
  Redis 128 MB（maxmemory 128mb）
  Docker + Nginx + 系统 0.6 GB
  余量 ~0.6 GB
  ★ 这是「刚好够用」，一旦「导出报表」与「每日备份」撞在同一时刻，
    OOM Killer 会优先杀 MySQL。这就是推荐 8 GB 的原因。


================================================================
五、磁盘空间算式（100 GB 的来历）
================================================================

项目                                    估算占用
----------------------------------------------------------------
业务数据（数十人，5 年）                 ≈ 1 GB
索引 + binlog 30 天（ROW 格式）          ≈ 5 GB
全量备份 30 份（gz，约数据量 20%）       ≈ 6 GB
恢复演练临时库（峰值 = 2 倍数据量）      ≈ 2 GB
系统 + Docker 镜像 + 应用日志             ≈ 25 GB
----------------------------------------------------------------
合计                                     ≈ 39 GB

结论：100 GB 可支撑 2~3 年不需扩容。
     按 docs/备份恢复手册.md:87 的 80% 磁盘水位告警，100 GB 有充足缓冲。


================================================================
六、内部部署必须调整的四项（现有配置存在隐患）
================================================================

1) 数据库端口必须收敛，不能监听所有网卡
   现状：docker/docker-compose.dev.yml:27 为 "3306:3306"，
        :48 为 "6379:6379"，等价于绑定 0.0.0.0。
   风险：内网任何一台办公电脑都能直连 MySQL 试 root 密码。
   改法（二选一）：
     A. 只允许宿主机访问
        ports:
          - "127.0.0.1:3306:3306"
          - "127.0.0.1:6379:6379"
     B. 后端与数据库在同一 compose 网络内时，直接删掉这两行 ports，
        容器之间通过服务名互访，宿主机只暴露 80/443。

2) 前端必须构建成静态文件 + Nginx 反向代理
   禁止把 vite dev 服务器当正式环境用：
   开发服务器会暴露全部源码，且不提供任何访问控制。
   正式做法：pnpm build 产出 frontend/dist，由 Nginx 托管，
            /api 反向代理到后端 8000 端口。

3) 备份必须异地留一份
   内部系统最常见的「数据全丢」场景就是单机故障。
   在 scripts/backup_full.sh 之后追加一步 rsync 到 NAS 或公司文件服务器。

4) 提前预留反向代理层
   不接公网就不需要 WAF / CDN / SSL。
   但如果公司后续有统一域名单点登录（SSO）规划，
   Nginx 反代层要提前留好，避免到时大改架构。


================================================================
七、三档速查表
================================================================

场景                       CPU      内存            磁盘            备注
--------------------------------------------------------------------------------
试用 / 演示（< 5 人）      1 核     2 GB            40 GB SSD       仅跑小数据量；
                                                                    导出大报表会 OOM

正式内部（数十人）         2 核     4 GB（建议 8 GB）100 GB SSD      推荐方案
【推荐】

内部 + 冗余                4 核 ×2  8 GB ×2         200 GB SSD ×2   含从库与监控；
（从库 + 监控）                                                     工资系统若需
                                                                    审计级可靠性
                                                                    值得投入
--------------------------------------------------------------------------------


================================================================
八、可选加速项（非必须）
================================================================

1) 图表库已本地化
   ECharts 静态文件已放在 docs/design/echarts.min.js，
   前端自托管即可，不依赖外网 CDN，符合「内网加载不可靠」的既定决策。

2) 日志盘分离
   若开启审计日志长期留存，把 backend/logs/ 挂载到独立磁盘，
   避免日志写满系统盘导致 MySQL 无法写入。

3) 备份异地化
   本机备份 + 定时 rsync 到 NAS 或对象存储，构成「本地 + 异地」两份。


================================================================
九、生产环境仍需完成的事项（本文件之外）
================================================================

1) 生成 docker/docker-compose.prod.yml：
   - 端口收敛到 127.0.0.1
   - MySQL innodb_buffer_pool_size 等参数调优
   - 容器 mem_limit / cpus 资源限制
   - Nginx 反向代理静态前端 + /api

2) 后端启动参数由开发模式改为生产模式：
   - 去掉 --reload（生产环境必须去掉）
   - 设置 workers=2

3) backend/.env 中 APP_SECRET_KEY 必须换成强随机值，
   MYSQL_PASSWORD / REDIS_PASSWORD 两处（docker/.env 与 backend/.env）保持一致。

4) 首次部署执行 alembic upgrade head，并创建管理员账号：
   uv run python -m app.cli create-admin --username admin

5) 配置每日定时任务：全量备份 + 增量归档（见 docs/备份恢复手册.md）


================================================================
十、附：数据来源索引
================================================================

ai-aux/AI_CONTEXT.md:101                并发规模判断（数十人）
docker/docker-compose.dev.yml:11        MySQL 8.0 容器
docker/docker-compose.dev.yml:27        MySQL 端口映射（需收敛）
docker/docker-compose.dev.yml:44        Redis 7 容器与 AOF
docker/docker-compose.dev.yml:48        Redis 端口映射（需收敛）
backend/app/core/db.py:29               同步引擎与连接池配置
backend/app/services/report_export.py:18  报表导出（内存峰值来源）
docs/备份恢复手册.md:11                 备份策略
docs/备份恢复手册.md:45                 恢复演练临时库
docs/备份恢复手册.md:87                 磁盘水位 80% 告警阈值
scripts/                                备份 / 恢复 / 巡检脚本（.sh）

================================================================
（文件结束）
