Skip to content

Web 端部署

本文档说明如何将 todo_web(Vue 3 + Vite SPA)打包为 Docker 镜像并部署运行。

镜像基于 todo_web/Dockerfile(多阶段构建:Node 20 编译 → Nginx 1.27 提供静态文件 + /api 反向代理),最终镜像默认监听 容器 80 端口,对外通过 -p 映射。


1. 镜像构成

阶段基础镜像作用
构建node:20-slimnpm ci + npm run build 生成 dist/
运行nginx:1.27-alpine托管静态资源,并把 /api/ 反代到后端

关键文件:

  • Dockerfile — 多阶段构建
  • nginx.conf.template — Nginx 模板,/api/ 反代地址用 ${API_PROXY_PASS} 占位
  • docker-entrypoint.sh — 容器启动时用 envsubstAPI_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:latest
  • docker.cnb.cool/<组织>/<项目>/web:latest

(api 端镜像用法见 API 端部署。)

只需 git pushmain,CNB 会自动完成构建与推送,无需本地构建。


3. 环境变量

变量必填默认值说明
API_PROXY_PASShttp://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 -d

web 容器通过 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)
刷新子路由 404Nginx 已配置 try_files $uri /index.html(SPA 回退),正常不会;若自行改配置需保留该规则
跨域 CORS 报错优先使用 Nginx 反代 /api(同源),避免浏览器直连后端产生的 CORS 问题
推送镜像 403镜像仓库路径/权限问题,确认 CNB 仓库存在且 CI 凭证有写权限

升级 / 迁移后旧地址残留(重要)

新版本前端默认走同源 /api(由 Nginx 反代),但浏览器 localStorage 中可能已保存过旧的后端地址(如 http://127.0.0.1:3000/apihttp://<IP>:3000/api)。该保存值优先级高于默认配置,会导致前端仍直连旧地址,出现:

  • net::ERR_CONNECTION_REFUSED
  • Mixed Content(HTTPS 页请求 HTTP)
  • CORS policy: blocked by local address space

解决(任选其一)

  1. 在 Web UI「个人中心 - 服务端配置」中清空地址框并保存(移除保存值,回退到默认 /api);
  2. 或浏览器开发者工具 → Application → Local Storage → 删除键 itodo_api_base,刷新页面;
  3. 或部署时通过 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 镜像的关系

镜像端口职责
api80(容器内,常映射 3000)Rails JSON API + 数据库迁移 + 任务队列
web80(容器内,常映射 8080)静态前端 + /api 反代

Web 镜像不依赖 API 镜像的可用性即可启动;但完整功能需要 API 镜像 + PostgreSQL 同时运行,/api 反代目标需可达。

基于 VitePress 构建