全栈工程课 · 一个相册系统的上线之路
课程平台Chapter 9 · 正式上线

CHAPTER 9 · PRODUCTION DELIVERY

09

Vite Proxy 只存在于开发环境,正式上线后谁来转发 /api

本地能运行,只说明开发链路打通了。正式上线时,前端、后端、数据库、配置、镜像和发布流程会变成另一套工程问题。

开发环境不等于正式环境

正式环境通常不会运行 npm run dev

开发环境

浏览器

Vite 开发服务器
├─ 返回 React 页面
└─ Proxy /api → Go 后端

正式环境

浏览器

Nginx 或统一网关
├─ 返回静态前端文件
└─ /api → Go 后端

React 项目上线后,运行的到底是什么?

Go 项目上线后,运行的又是什么?

前端改一个按钮,是否必须重新发布后端?

后端改一个接口,是否必须重新构建前端?

开发环境的运行方式 正式环境的部署方式

项目组织

代码放在同一个目录,不代表上线时是一个应用。

练习项目

album-system/
├── frontend/
└── backend/

方便一次打开完整代码,便于本地联调,也便于前后端接口对照。

正式项目常见形态

album-web
独立前端仓库

album-api
独立后端仓库

前端和后端可以拥有不同构建流程、发布节奏和回滚方式。

前后端是否放在同一个仓库,要看团队规模和工程管理方式。关键不在“目录是否分开”,而在它们应该被视为两个独立构建、独立运行、独立发布的应用。

构建产物

前端产物和后端产物不是一类东西。

React 前端

静态文件

TypeScript + React 源码
↓ npm run build
dist/index.html
dist/assets/*.js
dist/assets/*.css

构建后通常不再需要 Node.js 执行业务代码,而是由 Web 服务器提供静态文件。

Go 后端

服务程序

Go 源码
↓ go build
可执行文件
↓ 持续运行
监听端口并处理 /api

后端产物是一个持续运行的服务,需要数据库、环境配置、日志和健康检查。

正式环境请求链路

Nginx 提供前端文件,也转发后端 API。

用户浏览器Nginx
静态前端文件HTML / CSS / JS
/api 请求Go 服务 → MySQL
nginx.conf
server {
    listen 80;
    server_name album.example.com;

    root /usr/share/nginx/html;
    index index.html;

    location /api/ {
        proxy_pass http://album-api:8080;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}

root

指定 React 构建产物所在目录。

location /api/

将 API 请求转发给 Go 后端。

try_files

解决 React 单页应用刷新子页面时出现 404 的问题。

Nginx 不是用来替代 Go 后端,而是作为静态资源服务器和统一请求入口。浏览器看到同一个域名,内部请求由 Nginx 分发。

先把 Nginx 跑起来

用 Docker 一条命令启动一个 Nginx。

先不急着写 Dockerfile。先用现成的 Nginx 镜像,把“静态文件目录”和“Nginx 配置”挂进去,让浏览器能访问到前端构建产物。

run-nginx.sh
docker run -d \
  --name album-nginx \
  -p 8081:80 \
  -v ./frontend/dist:/usr/share/nginx/html:ro \
  -v ./nginx.conf:/etc/nginx/conf.d/default.conf:ro \
  nginx:1.27-alpine
参数作用说明

-p 8081:80

端口映射

浏览器访问本机 8081,Docker 转发到容器里的 Nginx 80 端口。冒号左边是本机端口,右边是容器端口。

-v ./frontend/dist:/usr/share/nginx/html:ro

挂载静态文件

把前端构建产物挂到 Nginx 默认静态目录。:ro 表示容器只读。

-v ./nginx.conf:/etc/nginx/conf.d/default.conf:ro

挂载配置

把本地 Nginx 配置挂进容器,用来控制前端路由和 /api 转发。

这个命令适合理解 Nginx 的运行方式,但它不是最终交付形态。正式发布时,更常见的做法是把构建产物和配置打进镜像,形成可版本化、可发布、可回滚的前端镜像。

Docker 镜像

为什么还需要 Dockerfile?

Dockerfile 是把“怎么构建、怎么运行”写成可重复的说明书。

刚才的 docker run 适合临时启动一个 Nginx,但正式项目不能靠手工记命令。Dockerfile 把安装依赖、构建产物、复制配置、选择运行环境这些步骤固定下来,CI/CD 才能每次构建出一致的镜像。

它回答的不是“这段代码怎么写”,而是:这个应用最终应该以什么环境、什么文件、什么命令运行。AI 可以生成 Dockerfile,但你必须能看懂它把哪些东西带进了最终镜像。

frontend/Dockerfile点击关键行

单阶段镜像可能包含

编译器
包管理工具
源代码
构建缓存
开发依赖

多阶段最终镜像只保留

前端静态文件 + Nginx
或后端可执行文件
必要运行环境

审查 Dockerfile

AI 可以生成配置,但你要知道最终镜像里有什么。

前端镜像

是否使用多阶段构建?是否生成 dist?最终镜像是否仍运行 npm run dev?Nginx 是否处理 /api 和前端路由刷新?

后端镜像

是否先复制 go.modgo.sum?最终镜像是否包含 Go 编译器、源码、.env 或开发工具?

错误信号

前端正式镜像执行 npm run dev,说明它仍依赖开发服务器,不适合作为正式运行方式。

基础镜像版本

示例版本不能盲目复制,应根据项目实际锁定并维护基础镜像版本。

配置与镜像分离

同一个镜像,使用不同环境配置。

测试环境和生产环境数据库地址不同,不应该分别构建两个后端镜像。镜像负责“应用是什么”,运行时配置负责“应用连接哪里”。

内容能否进入前端产物说明

/api 路径、应用公开名称

可以

这是公开信息,可以出现在前端构建产物中。

JWT Secret、MySQL 密码

不可以

前端产物会被浏览器下载,不能包含任何服务端秘密。

OSS AccessKeySecret、服务端密钥

不可以

这类配置应由后端运行环境或密钥系统提供。

React 的 VITE_ 环境变量通常会在构建时写入前端产物,因此不能包含秘密。

独立发布

前后端分离的关键,是可以独立构建、发布和回滚。

修改前端按钮颜色

构建前端

生成新前端镜像

发布前端

通常不需要重新构建 Go 后端。

修复后端查询逻辑

构建后端

生成新后端镜像

发布后端

只要接口契约兼容,通常不需要重新构建前端。

独立发布也会带来兼容问题

删除字段、修改字段含义、修改数据类型、改变错误码,都可能影响尚未更新的前端。

更安全的方式是新增字段而不是立即删除旧字段,新旧版本短期兼容,接口变更经过评审,并明确发布顺序。

CI/CD

发布不是执行几条命令,而是一条可重复的链路。

前端 CI/CD

提交前端代码

安装依赖

TypeScript 检查

前端构建

构建并推送镜像

部署前端

后端 CI/CD

提交后端代码

Go 格式和静态检查

单元测试与编译

构建并推送镜像

数据库变更审批

部署后端并健康检查

浏览器统一域名Nginx
前端静态资源
/api → Go 后端

数据库变更边界

CI/CD 不能在服务启动时执行 GORM AutoMigrate。

正式服务禁止依赖 db.AutoMigrate(...) 修改表结构。

数据库变更必须显式、可审查、可回滚,不能隐藏在应用容器启动过程中。

开发者提交显式 SQL代码评审测试环境验证数据库变更审核独立执行变更再发布应用

Docker 镜像成功构建,不代表已经可以上线。上线还要考虑镜像版本、环境配置、数据库变更、健康检查、日志、回滚、密钥注入和前后端兼容。

给 AI 的工程约束

不要只说“生成一个 Dockerfile”。

工程配置任务描述
请为当前项目生成正式环境构建方案。

项目情况:
- 前端使用 React、TypeScript 和 Vite
- 后端使用 Go 和 Gin
- 前端和后端分别构建 Docker 镜像
- 前端由 Nginx 提供静态资源
- Nginx 将 /api 转发到后端服务
- 浏览器不能直接访问后端内部端口

工程要求:
1. 前后端都使用多阶段 Docker 构建。
2. 尽量利用 Docker 构建缓存。
3. 最终镜像中不保留源码和编译工具。
4. 不把 .env、密码或 AccessKey 打入镜像。
5. 前端不能运行 Vite 开发服务器。
6. Nginx 需要处理 React 路由刷新。
7. Go 后端不使用 AutoMigrate。
8. 输出 Dockerfile、.dockerignore 和 Nginx 配置。
9. 解释每个关键工程决策。

本章总结

  1. 练习项目同目录是为了本地开发方便,正式项目可以拆成独立仓库。
  2. 前端构建后得到静态文件,后端构建后得到可执行服务程序。
  3. 正式环境不运行 Vite 开发服务器。
  4. Nginx 可以提供前端静态文件并转发 /api
  5. 前端和后端应该独立构建、发布和回滚。
  6. Dockerfile 应使用多阶段构建并减少最终镜像内容。
  7. 敏感配置不能写入前端、代码仓库或 Docker 镜像。
  8. CI/CD 将检查、测试、构建和部署过程自动化。
  9. 数据库变更必须显式、可审查,并经过审核。
  10. 正式服务禁止依赖 GORM AutoMigrate 修改表结构。
  11. AI 可以生成工程配置,但开发者必须审查架构、安全和发布边界。
服务已经可以发布了,真正上线后如何判断它是否健康?
下一章进入日志、健康检查、监控与故障定位。