- Dockerfile 单阶段 Alpine 镜像,使用国内镜像(npmmirror / Prisma 引擎 / apk)加速 - entrypoint 首次复制内置图章素材到 uploads volume,自动执行 prisma migrate deploy - docker-compose.yml 绑定 127.0.0.1:3001,强制走外部 Nginx 反代 - Express 在 production 下同时托管 packages/web/dist 及 SPA fallback - Prisma schema 增加 linux-musl 二进制目标,支持 Alpine 运行 - 新增 DEPLOY.md 部署指南,含 .env 模板与 Nginx 反代示例 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.5 KiB
4.5 KiB
部署指南
基于 Docker Compose 的单容器部署方案。容器内 Express 同时托管:
/api/*— API/uploads/*— 图章静态资源- 其余路径 — React SPA(
packages/web/dist)
外部通过宿主机 Nginx 反向代理到容器的 3000 端口(仅绑定 127.0.0.1,不直接暴露)。
1. 前置条件
- Linux 服务器(Docker ≥ 24、Docker Compose v2)
- 已解析到服务器的域名(例
stamp.example.com) - 已签发的 SSL 证书(Let's Encrypt / acme.sh 等任选)
2. 拉取代码并配置环境变量
git clone <repo-url> citywalk-stamp
cd citywalk-stamp
cp .env.production.example .env
vim .env # 填入 JWT_SECRET / ADMIN_API_KEY / SITE_URL
必填项:
| 变量 | 说明 |
|---|---|
JWT_SECRET |
用户 JWT 签名密钥,建议 openssl rand -hex 32 生成 |
ADMIN_API_KEY |
管理后台访问密钥,同上 |
SITE_URL |
对外域名,例 https://stamp.example.com(影响 QR 码链接) |
3. 启动容器
docker compose up -d --build
首次启动会自动完成:
- Prisma migrate deploy(建表)
- 把内置的 16 枚图章素材复制到
./uploads/stamps/
查看日志确认启动成功:
docker compose logs -f app
# 看到 "Server running on http://localhost:3000" 即 OK
4. 首次导入图章数据
容器默认不自动 seed 数据库(避免覆盖后续运营数据)。首次部署需手动执行一次:
docker compose exec app pnpm db:seed
再次执行会清空并重建所有图章,同时级联删除用户的收集记录。生产环境请勿随意重跑。
5. 配置 Nginx 反向代理
在宿主机上配置 Nginx:
server {
listen 80;
server_name stamp.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name stamp.example.com;
ssl_certificate /etc/letsencrypt/live/stamp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stamp.example.com/privkey.pem;
# 单入口反代:容器里 Express 自己处理 /api、/uploads、SPA 路由
location / {
proxy_pass http://127.0.0.1:3001;
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_set_header X-Forwarded-Proto $scheme;
}
# 静态资源缓存(可选)
location ^~ /uploads/ {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
expires 7d;
add_header Cache-Control "public, max-age=604800";
}
# 上传体积(管理后台上传图章)
client_max_body_size 10m;
}
测试并 reload:
sudo nginx -t && sudo systemctl reload nginx
访问 https://stamp.example.com 即可看到首页。
6. 数据持久化与备份
容器两个 bind mount:
./data/— SQLite 数据库(prod.db)./uploads/— 图章图片(含首次填充的 16 枚 + 后续管理后台上传的)
备份:
# 停机备份最稳妥(SQLite 文件锁)
docker compose stop app
tar czf backup-$(date +%F).tgz data/ uploads/ .env
docker compose start app
7. 常用运维命令
# 查看日志
docker compose logs -f app
# 进入容器
docker compose exec app sh
# 重启
docker compose restart app
# 升级(拉新代码后重建)
git pull
docker compose up -d --build
# 查看当前图章数
docker compose exec app sh -c 'echo "SELECT COUNT(*) FROM Stamp;" | sqlite3 /app/data/prod.db' \
|| docker compose exec app node -e "
import('@prisma/client').then(async ({PrismaClient}) => {
const p = new PrismaClient();
console.log(await p.stamp.count());
await p.\$disconnect();
});
"
# 完全重置(会删除所有用户和收集数据,谨慎)
docker compose down
rm -rf data/ uploads/
docker compose up -d --build
docker compose exec app pnpm db:seed
8. 故障排查
- 容器启动失败
PrismaClientInitializationError:检查./data/目录权限、DATABASE_URL值 - 图章图片 404:进入容器
ls /app/packages/server/uploads/stamps看是否被 seed 进来;若空,手动执行cp /app/stamps-seed/*.jpg /app/packages/server/uploads/stamps/ - QR 码扫出来是
localhost:.env里SITE_URL没改对,修正后docker compose restart app - Nginx 502:确认容器跑着
docker compose ps,端口ss -tlnp | grep 3000