Appearance
API 端部署
本文档说明如何将 todo_api(Rails 8 JSON API)打包为 Docker 镜像并部署运行。
镜像基于官方 Dockerfile(多阶段构建:Ruby 3.4 + Thruster + Puma),最终镜像默认监听 容器 80 端口,对外通过 -p 映射。
1. 前置依赖
| 组件 | 版本要求 | 说明 |
|---|---|---|
| PostgreSQL | 14+ | 主数据库,需可网络访问 |
| Docker | 20.10+ | 运行容器 |
RAILS_MASTER_KEY | — | 生产环境读取加密凭据所需(见 §5) |
默认
SOLID_QUEUE_IN_PUMA: true,后台任务(Solid Queue)随 Web 进程一起运行,无需独立 Redis / Valkey。
2. 构建镜像
方式 A:本地构建
bash
cd todo_api
docker build -t itodo-api:latest .方式 B:通过 CNB Cloud Native Build 自动构建
仓库根目录 .cnb.yml 已配置在 main 分支 push 时自动构建并推送两个镜像:
docker.cnb.cool/<组织>/<项目>/api:latestdocker.cnb.cool/<组织>/<项目>/web:latest
(web 端镜像用法见 Web 端部署。)
只需 git push 到 main,CNB 会自动完成构建与推送,无需本地构建。
3. 环境变量
启动容器时通过 -e 或 .env 文件注入:
| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
RAILS_MASTER_KEY | 是* | — | 解密 config/credentials/production.yml.enc |
DB_HOST | 是 | localhost | PostgreSQL 主机地址 |
DB_PORT | 否 | 5432 | PostgreSQL 端口 |
DB_USERNAME | 否 | postgres | 数据库用户名 |
DB_PASSWORD | 否 | — | 数据库密码 |
DB_NAME | 否 | todo_api_production | 数据库名 |
RAILS_ENV | 否 | production | 容器内已固化,无需覆盖 |
SOLID_QUEUE_IN_PUMA | 否 | true | 在 Puma 内运行任务队列 |
DEEPSEEK_API_KEY | 否 | — | AI 助手(DeepSeek)Key |
DEEPSEEK_BASE_URL | 否 | https://api.deepseek.com | AI 接口地址 |
DEEPSEEK_MODEL | 否 | deepseek-v4-flash | AI 模型 |
* 若生产凭据文件不存在或无需加密凭据,可省略;但 Rails 8 生产模式默认读取
productioncredentials,缺失会启动失败,建议始终提供。
4. 部署方式
方式一:docker run(已有外部 PostgreSQL)
bash
docker run -d --name itodo-api \
-p 3000:80 \
-e RAILS_MASTER_KEY=你的master_key \
-e DB_HOST=你的pg主机 \
-e DB_PORT=5432 \
-e DB_USERNAME=postgres \
-e DB_PASSWORD=你的密码 \
-e DB_NAME=todo_api_production \
itodo-api:latest容器启动时 entrypoint 会自动执行 rails db:prepare(建库 + 执行迁移),首次启动稍慢属正常。
方式二:docker-compose 一键部署(含 PostgreSQL)
新建 docker-compose.yml:
yaml
services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: your_db_password
POSTGRES_DB: todo_api_production
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 10
api:
image: itodo-api:latest # 或 CNB 镜像地址 docker.cnb.cool/<组织>/<项目>/api:latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports:
- "3000:80"
environment:
RAILS_MASTER_KEY: your_master_key
DB_HOST: db
DB_PORT: 5432
DB_USERNAME: postgres
DB_PASSWORD: your_db_password
DB_NAME: todo_api_production
SOLID_QUEUE_IN_PUMA: "true"
volumes:
pgdata:启动:
bash
docker compose up -d方式三:Kamal 部署(已有 config/deploy.yml)
项目已内置 Kamal 配置,适合多机/生产环境发布:
bash
cd todo_api
# 在 .kamal/secrets 中配置 REGISTRY_PASSWORD、RAILS_MASTER_KEY 等
kamal setup # 首次:创建容器、拉镜像、迁移数据库
kamal deploy # 后续发布
kamal logs -f # 查看日志Kamal 会按 config/deploy.yml 将镜像推到 registry.server 并管理容器生命周期。
5. 关于 RAILS_MASTER_KEY
Rails 生产环境通过 master key 解密 config/credentials/production.yml.enc。
- 若你本地有
config/credentials/production.key,其内容即为 master key 的值; - 将其作为环境变量
RAILS_MASTER_KEY传入容器即可; - 若项目未使用加密凭据,可临时用
bin/rails credentials:edit --environment production生成,或确保生产环境不依赖加密 secrets。
6. 健康检查与验证
容器暴露 GET /api/health,部署后验证:
bash
curl http://localhost:3000/api/health预期返回包含服务状态的 JSON(如 {"status":"ok"} 或类似结构)。
其他可用接口(详见 README.md):
GET/POST/PUT/DELETE /api/tasks— 任务 CRUDGET/POST/PUT/DELETE /api/task_lists、/api/tagsPOST /api/mcp— MCP 接口GET/PUT /api/ai/settings、POST /api/ai/chat— AI 助手
7. 与 Web 前端联调
Web 端默认访问 http://localhost:3000/api。两种方式:
- 独立部署 Web 静态站:在浏览器端「个人中心 - 服务端配置」手动填写
http://<api主机>:3000/api。 - CNB 构建的 Web 镜像:Nginx 已内置
/api反代(默认指向http://todo_api:80),同一 docker 网络下可直接用容器名todo_api互通;也可用环境变量API_PROXY_PASS覆盖目标地址。
示例(同网络联调):
bash
docker network create itodo-net
docker run -d --network itodo-net --name todo_api -e RAILS_MASTER_KEY=... -e DB_HOST=db ... itodo-api:latest
docker run -d --network itodo-net --name todo_web -p 8080:80 \
-e API_PROXY_PASS=http://todo_api:80 \
docker.cnb.cool/<组织>/<项目>/web:latest8. 常见问题
| 现象 | 原因 / 解决 |
|---|---|
启动报 Missing RAILS_MASTER_KEY | 生产环境需要解密凭据,传入 RAILS_MASTER_KEY |
| 数据库连不上 | 检查 DB_HOST/DB_PORT/DB_USERNAME/DB_PASSWORD,确认网络可达、PG 已就绪 |
| 首次启动慢 | entrypoint 正在 db:prepare(建库+迁移),等待即可 |
| 跨域 CORS 报错 | 生产环境在 config/initializers/cors.rb 配置允许的 origins |
| 推送镜像 403 | 镜像仓库路径/权限问题,确认 CNB 仓库存在且 CI 凭证有写权限 |
9. 数据备份
PostgreSQL 数据持久化在 pgdata 卷(方式二)或你自建的数据库。备份示例:
bash
docker exec -t <pg容器> pg_dump -U postgres todo_api_production > backup.sql