🍉 菜瓜记账 & MyBill
Docker 多项目部署文档

🐕 二狗迁移实录

🕐 2026-04-24 · 阿里云 2C2G · Asia/Shanghai

一、整体架构

4 个 Docker 容器在同一台服务器运行,通过 shared_net 网络互通。Nginx 统一入口,按 URL 路径分发到各后端。

infra_mysql healthy
MySQL 8.0
127.0.0.1:3306
ml_backend healthy
FastAPI
内部 8000
infra_nginx up
Nginx 1.27
80 / 443
ml_mybill healthy
MyBill API
内部 3000

架构图

  ┌─ 用户 ──────────────────────────────────┐
  │  https://cmetamind.cn/xxx                │
  └─────────────────┬───────────────────────-┘
                    ▼
  ┌──────────────────────────────────────────┐
  │     Nginx listen 443 SSL                 │
  │     HTTP 80 → 301 HTTPS                 │
  └────┬──────────┬───────────┬─────────────-┘
       │          │           │
       ▼          ▼           ▼
   ┌──────┐  ┌────────┐  ┌──────────┐
   │  /   │  │ /mb/   │  │ /mb-api  │→ FastAPI
   │文档  │  │前端     │  │ /bill-api│→ MyBill
   └──────┘  └────────┘  └────┬─────┘
                               │
                               ▼
                        ┌──────────────┐
                        │  MySQL 8.0   │
                        │ melon_ledger │
                        │ bill         │
                        └──────────────┘
📌 缺失服务:Redis 未部署(后端自动降级为内存缓存 60s)、Neo4j 未部署,功能不受影响。

访问路径

路径说明
/本文档静态首页
/mb/菜瓜记账前端 uni-app H5
/mb-api/菜瓜记账后端 API(路由 /api/xxx)
/bill-api/MyBill 账单 API(路由 /users, /types 等)

二、目录结构

根目录 /home/deploy/docker-new/

├── infrastructure/        ← 基础设施
│   ├── docker-compose.yml ← 编排文件
│   ├── .env              ← MySQL 密码
│   ├── mysql/
│   │   ├── conf/my.cnf
│   │   └── init/
│   │       ├── 01-melon-ledger.sql  ← 建库+用户
│   │       └── 02-mybill.sql
│   ├── nginx/
│   │   ├── nginx.conf
│   │   └── conf.d/
│   │       └── melon-ledger.conf ← 全部路由
│   └── ssl/
│       ├── fullchain.pem   ← SSL 证书
│       └── privkey.pem     ← SSL 私钥
├── melon-ledger/          ← 菜瓜记账
│   ├── backend/
│   │   ├── Dockerfile
│   │   ├── requirements.txt
│   │   ├── .env
│   │   ├── main.py
│   │   ├── app/           ← 业务代码
│   │   └── migrations/    ← Alembic 迁移
│   └── frontend/dist/     ← 构建产物
├── mybill/                ← MyBill
│   ├── Dockerfile
│   ├── index.js
│   ├── bill.js
│   └── package.json
├── static/                ← 根路径静态文件
│   └── index.html         ← 本文件
└── scripts/               ← 备份
注意:旧单体项目 /home/deploy/melon-ledger/ 已清理删除。

三、环境配置

文件列表及关键配置(密码用占位符替代):

infrastructure/.env

MYSQL_ROOT_PASSWORD=<MYSQL_ROOT_PASSWORD>

melon-ledger/backend/.env

MYSQL_ROOT_PASSWORD=<MYSQL_ROOT_PASSWORD>
MYSQL_USER=melon
MYSQL_PASSWORD=<MELON_DB_PASSWORD>
MYSQL_DATABASE=melon_ledger
JWT_SECRET_KEY=<JWT_SECRET_KEY>
# Redis 未部署,自动降级
# Neo4j 未部署,自动跳过
DEEPSEEK_API_KEY=<DEEPSEEK_API_KEY>
DEEPSEEK_MODEL=deepseek-chat

mybill/.env

MYBILL_DB_PASSWORD=<MYBILL_DB_PASSWORD>

建表说明

MySQL init SQL 只建库和用户,不建表

四、Nginx 路由说明

单配置文件 infrastructure/nginx/conf.d/melon-ledger.conf

路径处理方式目标
/静态文件/usr/share/nginx/html/
/mb/alias/usr/share/nginx/mb/
/assets/alias/usr/share/nginx/mb/assets/(前端资源缓存)
/mb-api/rewrite + proxyhttp://ml_backend:8000
/bill-api/rewrite + proxyhttp://ml_mybill:3000

关键配置片段

# melon-ledger API (路由前缀 /api/xxx)
location /mb-api/ {
    rewrite ^/mb-api(/.*)$ $1 break;
    proxy_pass http://ml_backend;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

# mybill API (路由 /users, /types 等)
location /bill-api/ {
    set $mybill_upstream "http://ml_mybill:3000";
    rewrite ^/bill-api(/.*)$ $1 break;
    proxy_pass $mybill_upstream;
}
⚠️ 注意:set 必须在 rewrite 之前,否则变量未初始化报 500。

SSL 证书

当前覆盖 cmetamind.cnwww.cmetamind.cn。如需添加 bill.cmetamind.cn,替换 ssl/fullchain.pemssl/privkey.pem 后重启 nginx。

五、日常运维操作

以下命令在 ~/docker-new/infrastructure/ 下执行。

# 查看所有容器
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

# 查看日志
docker logs -f ml_backend   # 实时日志
docker logs -f ml_mybill    # mybill 日志
docker logs --tail 50 ml_backend  # 最近 50 行

# 重启
docker compose restart nginx
docker compose restart ml_backend

# 重新构建+启动(代码更新后)
docker compose up -d --build ml_backend

# 热重载 Nginx(不中断连接)
docker exec infra_nginx nginx -t     # 语法检查
docker exec infra_nginx nginx -s reload

# 数据库
docker exec infra_mysql mysql -uroot -p melon_ledger -e "SHOW TABLES;"
docker exec ml_backend alembic upgrade head  # 迁移

# 前端更新(本地构建后上传 scp,无需重启 nginx)
scp -r dist/* user@server:/home/deploy/docker-new/melon-ledger/frontend/dist/

六、添加新项目到 Docker

  1. 写 Dockerfile — 项目根目录创建,参考 mybill/Dockerfile
  2. 加 docker-compose — 在 infrastructure/docker-compose.yml 添加 service 配置,加上 networks: - shared_net
  3. 加 Nginx 路由 — 在 melon-ledger.conf 的 server block 添加新的 location /xxx/
  4. 启动docker compose up -d --build 服务名 + docker compose restart nginx
⚠️ 前提条件:
  • 服务必须加入 shared_net 网络
  • 2C2G 内存有限,控制镜像和运行时内存
  • 务必加 healthcheck,避免 nginx 路由到不可用服务

七、踩坑记录 🐛

1. 前端构建 OOM
uni-app 构建工具峰值超 3GB,2GB 服务器被 kill。解决:本地打包,scp 上传。
2. aiomysql 编译 OOM
依赖 gcc 编译 default-libmysqlclient-dev 时内存爆。解决:替换为纯 Python 的 asyncmy。
3. Nginx resolver
proxy_pass 用变量时必须加 resolver 做 DNS 解析,否则 502。解决:加 resolver 127.0.0.11 ipv6=off valid=10s;
4. mysql 包废弃
旧 mybill 用 mysql 包,连 MySQL 8 握手失败。解决:改成 mysql2。
5. SSL 证书域名不全
证书不含 bill.cmetamind.cn。解决:bill 通过 /bill-api/ 路径发布,统一用主域名证书。
6. 列名前导空格
family_info 表的列 family_name 前有空格,ORM 报错。解决:ALTER TABLE 改名。
7. set 在 rewrite 之后
proxy_pass 变量未初始化报 500。解决:set 放在 rewrite 之前。
8. root 权限文件难删除
dist 目录有 root 文件无法 rm。解决:docker run --rm -v /home/deploy:/host alpine rm -rf /host/xxx
✅ Redis 优雅降级
后端打印 Redis 不可用,降级为内存缓存 60s,无需 Redis 也正常工作。

八、常用命令速查表

操作命令
完整启动cd ~/docker-new/infrastructure && docker compose up -d
完整停止docker compose down
重建某服务docker compose up -d --build ml_backend
重启 Nginxdocker compose restart nginx
热重载 Nginxdocker exec infra_nginx nginx -s reload
查看日志docker logs -f ml_backend
查看表docker exec infra_mysql mysql -uroot -p melon_ledger -e "SHOW TABLES"
数据库迁移docker exec ml_backend alembic upgrade head
进入容器docker exec -it ml_backend sh
清理不用的镜像docker image prune -a
磁盘占用docker system df
前端更新本地打包后 scp 到 dist 目录(无需重启)
证书更新替换 ssl/*.pem 后 docker compose restart nginx