本文档介绍如何使用 Docker Compose 将 Minder AI 部署到服务器。HTTPS 由服务器上已有的全局 Caddy (caddy-proxy) 自动管理。
- Docker >= 20.10
- Docker Compose >= 2.0
- 至少 2GB 可用内存
- 至少 10GB 可用磁盘空间
- 域名已解析到服务器 IP(如
minderai.heywhale.com) - 服务器上已运行全局 Caddy (
caddy-proxy),使用proxy-netDocker 网络
# Ubuntu/Debian
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# CentOS/RHEL
sudo yum install -y docker
sudo systemctl enable docker && sudo systemctl start dockergit clone https://github.com/LHMQ878/MarkAI.git
cd MarkAIchmod +x deploy.sh
./deploy.sh setup编辑 .env 文件,填入你的配置:
# 必须修改
POSTGRES_PASSWORD=your_secure_password_here
JWT_SECRET=your_random_jwt_secret_here
# 推荐配置(至少配置一个 API Key)
SILICONFLOW_API_KEY=your_siliconflow_api_key
QWEN_API_KEY=your_qwen_api_key
# 域名(Caddy 自动申请 HTTPS 证书)
MINDERAI_DOMAIN=minderai.heywhale.com./deploy.sh start./deploy.sh setup-domain此命令会自动将 minderai.heywhale.com 的反向代理配置追加到全局 Caddyfile 并重载。
Caddy 会自动申请 HTTPS 证书(约 30 秒)。
| 服务 | 地址 |
|---|---|
| 前端界面 | https://minderai.heywhale.com |
| API 文档 | https://minderai.heywhale.com/api/docs |
| 健康检查 | https://minderai.heywhale.com/api/health |
| 变量 | 说明 | 默认值 | 必填 |
|---|---|---|---|
POSTGRES_USER |
数据库用户名 | minderai |
否 |
POSTGRES_PASSWORD |
数据库密码 | - | 是 |
POSTGRES_DB |
数据库名称 | minderai |
否 |
JWT_SECRET |
JWT 签名密钥 | - | 是 |
SILICONFLOW_API_KEY |
SiliconFlow API Key | - | 推荐 |
QWEN_API_KEY |
通义千问 API Key | - | 可选 |
MINDERAI_DOMAIN |
域名 | minderai.heywhale.com |
否 |
# 生成随机 JWT Secret
openssl rand -base64 32
# 生成随机数据库密码
openssl rand -base64 24Minder AI 复用服务器上已有的全局 Caddy (caddy-proxy) 作为反向代理,通过 proxy-net Docker 网络连接。
用户浏览器
│
▼ HTTPS:443 (自动证书)
┌──────────────────────────────────────┐
│ caddy-proxy (全局, proxy-net) │ ← 自动 HTTPS + 反向代理
│ 管理多个域名: │
│ metabase.datascience.heywhale.com │
│ minderai.heywhale.com ←── 新增 │
└──────────┬───────────────────────────┘
│ HTTP via proxy-net
▼
┌──────────────────────────────┐
│ minderai-frontend (Nginx) │ ← SPA + API 反代
│ networks: minderai-network │
│ + proxy-net │
│ ↓ /api │
│ minderai-backend (FastAPI) │
│ minderai-postgres │
└──────────────────────────────┘
minderai-frontend 同时连接两个网络:minderai-network(内部通信)和 proxy-net(让全局 Caddy 访问)。
全局 Caddyfile 位于 /data/user/xiao/nginx/Caddyfile,由 caddy-proxy 容器挂载。
运行 ./deploy.sh setup-domain 会自动追加以下配置:
minderai.heywhale.com {
reverse_proxy /* minderai-frontend:80
tls liyy@heywhale.com
encode gzip
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
Strict-Transport-Security "max-age=63072000; includeSubDomains"
}
}修改后重载:./deploy.sh caddy-reload
在腾讯云 DNS 解析控制台添加以下记录:
| 主机记录 | 记录类型 | 记录值 | TTL |
|---|---|---|---|
| minderai | A | <你的服务器公网IP> |
600 |
查看服务器公网 IP:
curl ifconfig.me80 和 443 端口由全局 caddy-proxy 使用,需确保已放行:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp腾讯云安全组也需要放行 80 和 443 端口。
# 检查 HTTPS 是否正常
curl -I https://minderai.heywhale.com
# 检查 HTTP 是否自动跳转 HTTPS
curl -I http://minderai.heywhale.com
# 检查 API
curl https://minderai.heywhale.com/api/health
# 查看全局 Caddy 日志(证书申请状态)
docker logs -f caddy-proxy
# 检查 SSL 证书信息
openssl s_client -connect minderai.heywhale.com:443 -servername minderai.heywhale.com </dev/null 2>/dev/null | openssl x509 -noout -dates# 查看服务状态
./deploy.sh status
# 查看实时日志
./deploy.sh logs -f
# 查看特定服务日志
./deploy.sh logs -f backend
./deploy.sh logs -f frontend
./deploy.sh logs -f postgres
# 查看全局 Caddy 日志
docker logs -f caddy-proxy
# 重启服务
./deploy.sh restart
# 重新构建并启动(代码更新后)
./deploy.sh rebuild
# 停止服务
./deploy.sh stop
# 注册域名到全局 Caddy(首次部署)
./deploy.sh setup-domain
# 重载全局 Caddy 配置
./deploy.sh caddy-reload
# 完全清理(包括数据库数据)
./deploy.sh clean| 容器 | 内部端口 | 外部端口 |
|---|---|---|
| caddy-proxy (全局) | 80, 443 | 80, 443(共享) |
| frontend (Nginx) | 80 | 不暴露,通过 proxy-net 访问 |
| backend (FastAPI) | 8000 | 不暴露,仅内部访问 |
| postgres | 5432 | 不暴露,仅内部访问 |
Docker Compose 已配置 restart: unless-stopped,服务会在崩溃或系统重启后自动恢复。
# 备份数据库
docker exec minderai-postgres pg_dump -U minderai minderai > backup_$(date +%Y%m%d).sql
# 恢复数据库
docker exec -i minderai-postgres psql -U minderai minderai < backup.sql
# 备份上传文件
docker cp minderai-backend:/app/uploads ./uploads_backup# 实时监控所有服务
./deploy.sh logs -f
# 保存日志到文件
./deploy.sh logs > logs_$(date +%Y%m%d).txt如需使用外部 PostgreSQL(如阿里云 RDS),在 .env 中设置:
DATABASE_URL=postgresql+asyncpg://user:password@host:5432/dbname系统将自动跳过本地 PostgreSQL 容器启动。
# 查看全局 Caddy 详细日志
docker logs -f caddy-proxy
# 常见原因:
# 1. 域名未解析到服务器 IP → 检查 DNS
# 2. 防火墙/安全组未放行 80/443 → 放行端口
# 3. 域名未备案(国内服务器)→ 完成 ICP 备案
# 4. 域名未添加到 Caddyfile → 运行 ./deploy.sh setup-domain# 查看详细日志
./deploy.sh logs backend
./deploy.sh logs postgres
# 检查容器状态
docker ps -a | grep minderai# 检查 PostgreSQL 是否正常
docker exec minderai-postgres pg_isready -U minderai
# 进入数据库
docker exec -it minderai-postgres psql -U minderai minderai# 确认全局 Caddy 正在运行
docker ps --filter name=caddy-proxy
# 如果没有运行,需要先启动全局 Caddy# 清理 Docker 未使用资源
docker system prune -a
# 查看 Docker 磁盘使用
docker system df登录服务器后若看到 There are N zombie processes,可按以下步骤排查是否与本项目(Minder AI)相关,并做处理。
1. 列出所有僵尸进程
# 查看状态为 Z (zombie) 的进程
ps aux | awk '$8 ~ /Z/'
# 或更清晰:显示 PID、PPID、状态、命令
ps -eo pid,ppid,stat,cmd | grep -E ' Z| Zs'2. 确认父进程(谁没回收子进程)
僵尸进程的父进程 PID 在 PPID 列。根据父进程可判断来源:
# 根据上面得到的 PPID,查看父进程是什么
ps -p <PPID> -o pid,ppid,stat,cmd
# 若父进程在容器内,可查该 PID 属于哪个容器
docker ps -q | xargs -I {} docker top {} 2>/dev/null | grep -E "PID|PPID"
# 或:在宿主机上
cat /proc/<PPID>/cgroup 2>/dev/null | head -1- 若父进程是 containerd-shim 或 dockerd:多半是某个容器内进程退出后,容器主进程(PID 1)未正确
wait()子进程,导致僵尸留在宿主机。 - 若父进程是 systemd 或 init (PID 1):一般是宿主机上的服务(如 cron、自定义 systemd 服务)未回收子进程;重启对应服务或系统会回收。
3. 判断是否来自 Minder AI 的容器
本项目的容器名为:minderai-backend、minderai-frontend、minderai-postgres。
# 查看本项目容器的主进程 PID(在宿主机上)
docker inspect -f '{{.State.Pid}}' minderai-backend
docker inspect -f '{{.State.Pid}}' minderai-frontend
docker inspect -f '{{.State.Pid}}' minderai-postgres若僵尸进程的 PPID 等于上述某个容器的 State.Pid,或属于该 PID 的子进程树,则可能来自该容器。
后端使用 uvicorn 单进程、前端为 nginx,通常不会产生大量僵尸;若僵尸的父进程不在上述容器内,则来自其他服务或其它 Docker 项目。
4. 处理方式
- 不能直接 kill 僵尸进程:僵尸已退出,
kill无效;只有其父进程执行wait()或父进程退出后,僵尸才会被 init 回收。 - 若父进程属于本项目某容器:重启对应容器,让新进程树替代旧的,僵尸会被回收:
./deploy.sh restart # 或只重启后端 docker restart minderai-backend - 若父进程属于其他服务:重启该服务(如
sudo systemctl restart <服务名>),或安排系统重启(*** System restart required ***时重启会清空僵尸)。 - 长期减少僵尸:确保所有服务用正确方式回收子进程(如用
exec启动主进程、或 PID 1 进程负责 wait);本项目当前镜像中 uvicorn/nginx 作为 PID 1 已符合常见用法。
5. 一键查看僵尸及其父进程(示例)
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /Z/ {print; system("ps -p " $2 " -o pid,ppid,stat,cmd 2>/dev/null")}'根据输出中的父进程命令判断是哪个服务或容器,再按上面步骤处理即可。
# 拉取最新代码
git pull
# 重新构建并启动
./deploy.sh rebuild HTTPS:443 ──────┐
HTTP:80 ────────┤
▼
┌──────────────────────┐
│ caddy-proxy (全局) │ auto HTTPS
│ [proxy-net] │
└────────┬─────────────┘
│ proxy-net
▼
┌──────────────────────┐
│ minderai-frontend │
│ (Nginx) :80 │
│ [proxy-net + │
│ minderai-network] │
└────────┬─────────────┘
│ /api → minderai-network
▼
┌──────────────────────┐
│ minderai-backend │
│ (FastAPI) :8000 │
│ [minderai-network] │
└────────┬─────────────┘
│
▼
┌──────────────────────┐
│ minderai-postgres │
│ :5432 │
│ [minderai-network] │
└──────────────────────┘
| 服务 | CPU | 内存 |
|---|---|---|
| postgres | 0.5 核 | 256MB |
| backend | 1 核 | 512MB |
| frontend | 0.25 核 | 128MB |
总计:约 1.75 核 CPU,896MB 内存(基础运行,不含全局 caddy-proxy)
如有问题,请查看 GitHub Issues 或提交新 Issue。