Appearance
Web 端部署
本文档说明如何将 todo_web(Vue 3 + Vite SPA)打包为 Docker 镜像并部署运行。
镜像基于 todo_web/Dockerfile(多阶段构建:Node 20 编译 → Nginx 1.27 提供静态文件 + /api 反向代理),最终镜像默认监听 容器 80 端口,对外通过 -p 映射。
1. 镜像构成
| 阶段 | 基础镜像 | 作用 |
|---|---|---|
| 构建 | node:20-slim | npm ci + npm run build 生成 dist/ |
| 运行 | nginx:1.27-alpine | 托管静态资源,并把 /api/ 反代到后端 |
关键文件:
Dockerfile— 多阶段构建nginx.conf.template— Nginx 模板,/api/反代地址用${API_PROXY_PASS}占位docker-entrypoint.sh— 容器启动时用envsubst把API_PROXY_PASS注入 Nginx 配置
2. 构建镜像
方式 A:本地构建
bash
cd todo_web
docker build -t itodo-web:latest .方式 B:通过 CNB Cloud Native Build 自动构建
仓库根目录 .cnb.yml 已配置在 main 分支 push 时自动构建并推送两个镜像:
docker.cnb.cool/<组织>/<项目>/api:latestdocker.cnb.cool/<组织>/<项目>/web:latest
(api 端镜像用法见 API 端部署。)
只需 git push 到 main,CNB 会自动完成构建与推送,无需本地构建。
3. 环境变量
| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
API_PROXY_PASS | 否 | http://todo_api:80 | /api/ 反代的后端地址(不含 /api 前缀) |
默认假设后端容器在同一 Docker 网络下、名为
todo_api、监听 80 端口。跨主机 / 域名部署时用API_PROXY_PASS覆盖。
前端应用本身在浏览器运行,无需 Node 运行时;后端地址也可在 Web UI「个人中心 - 服务端配置」中手动填写,优先级高于 Nginx 反代。
4. 部署方式
方式一:docker run(默认反代 todo_api 容器)
bash
docker run -d --name itodo-web \
-p 8080:80 \
itodo-web:latest若后端不在同网络的 todo_api:80,覆盖反代地址:
bash
docker run -d --name itodo-web \
-p 8080:80 \
-e API_PROXY_PASS=http://192.168.1.100:3000 \
itodo-web:latest访问 http://localhost:8080 即可使用 Web 端。
方式二:docker-compose 一键(Web + API + PostgreSQL)
新建 docker-compose.yml(与 API 端部署 中的后端 compose 合并):
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
expose:
- "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
web:
image: itodo-web:latest # 或 CNB 镜像 docker.cnb.cool/<组织>/<项目>/web:latest
restart: unless-stopped
depends_on:
- api
ports:
- "8080:80"
environment:
API_PROXY_PASS: http://api:80
volumes:
pgdata:启动:
bash
docker compose up -dweb 容器通过 API_PROXY_PASS=http://api:80 把 /api/ 转发给 api 服务,浏览器访问 http://localhost:8080 即可完整使用。
5. 验证
bash
# 首页可用
curl -I http://localhost:8080/
# 反代后端健康检查(经 Nginx 转发)
curl http://localhost:8080/api/health浏览器打开 http://localhost:8080,登录后右下角「🤖 AI 助手」可正常使用。
6. 反向代理 / HTTPS(生产建议)
如果需要用域名 + HTTPS 对外暴露,可在 Web 容器前再放一层 Nginx / Caddy,将域名指向容器 80 端口并终止 SSL。示例(Nginx 已内置 /api 反代时,外层只需做 HTTPS 终止并 proxy_pass http://web:80):
nginx
server {
listen 443 ssl;
server_name todo.example.com;
ssl_certificate /path/fullchain.pem;
ssl_certificate_key /path/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
}
}若外层代理已处理
/api转发,可忽略容器内的API_PROXY_PASS,让 Web 仅作为静态资源服务;也可保持容器内反代不变(两层反代亦可工作)。
7. 常见问题
| 现象 | 原因 / 解决 |
|---|---|
| 页面能打开,但请求 404 / 连不上后端 | 检查 API_PROXY_PASS 是否指向正确的后端地址与端口(后端容器默认容器内 80,映射出来常见 3000) |
| 刷新子路由 404 | Nginx 已配置 try_files $uri /index.html(SPA 回退),正常不会;若自行改配置需保留该规则 |
| 跨域 CORS 报错 | 优先使用 Nginx 反代 /api(同源),避免浏览器直连后端产生的 CORS 问题 |
| 推送镜像 403 | 镜像仓库路径/权限问题,确认 CNB 仓库存在且 CI 凭证有写权限 |
升级 / 迁移后旧地址残留(重要)
新版本前端默认走同源 /api(由 Nginx 反代),但浏览器 localStorage 中可能已保存过旧的后端地址(如 http://127.0.0.1:3000/api、http://<IP>:3000/api)。该保存值优先级高于默认配置,会导致前端仍直连旧地址,出现:
net::ERR_CONNECTION_REFUSEDMixed Content(HTTPS 页请求 HTTP)CORS policy: blocked by local address space
解决(任选其一):
- 在 Web UI「个人中心 - 服务端配置」中清空地址框并保存(移除保存值,回退到默认
/api); - 或浏览器开发者工具 → Application → Local Storage → 删除键
itodo_api_base,刷新页面; - 或部署时通过
VITE_API_BASE在构建期固化后端地址,彻底忽略运行时保存值的影响(仍可被 UI 手动覆盖)。
构建期固化示例:
docker build --build-arg VITE_API_BASE=https://api.example.com/api -t itodo-web:latest .(vite.config.js需支持loadEnv读取VITE_API_BASE;若未配置,请直接依赖默认/api+ Nginx 反代。)
8. 与 API 镜像的关系
| 镜像 | 端口 | 职责 |
|---|---|---|
api | 80(容器内,常映射 3000) | Rails JSON API + 数据库迁移 + 任务队列 |
web | 80(容器内,常映射 8080) | 静态前端 + /api 反代 |
Web 镜像不依赖 API 镜像的可用性即可启动;但完整功能需要 API 镜像 + PostgreSQL 同时运行,/api 反代目标需可达。