Skip to content

第 25 章 · 部署与运维

一句话开篇:写完的代码就像做好的菜,部署 = 把菜端上桌、保证每个客人都吃到热乎的、不串味、出 bug 时还能立刻撤回


0. 生活类比(先建立直觉)

把"前端项目部署"想象成开一家连锁奶茶店

真实场景奶茶店
静态部署平台加盟连锁总部包装好的"开店套餐"
Vercel/Netlify高端连锁(VC 加持,一键开店)
Nginx 自建自己租店面、装修、运营
Docker标准化的"集装箱店面",搬到哪都能开
CDN全国连锁分店(用户就近取货)
CI/CD中央厨房 → 自动配送 → 各分店上架
灰度发布新品先在 5 家店试卖,没问题再全国推
A/B 测试A 配方和 B 配方各发一半客人,看谁好评高
回滚客户投诉新品不好喝,立刻换回旧品
监控告警总部仪表盘,哪家店销量异常立刻报警

部署本质就 4 件事:把代码变成产物 → 把产物送到服务器 → 让用户访问 → 出事能撤回


1. 部署方式总览

                     ┌──────────────────────┐
                     │   你的前端代码        │
                     └─────────┬────────────┘
                               │ npm run build

                     ┌──────────────────────┐
                     │   dist/ 静态产物      │
                     └─────────┬────────────┘

        ┌──────────────────────┼──────────────────────┐
        ▼                      ▼                      ▼
┌──────────────┐      ┌──────────────┐      ┌──────────────┐
│ 静态平台      │      │ 自建 Nginx    │      │ Docker 镜像   │
│ Vercel       │      │ + CDN         │      │ + K8s         │
│ Netlify      │      │               │      │ + Serverless  │
│ Cloudflare   │      │               │      │               │
│ GitHub Pages │      │               │      │               │
└──────────────┘      └──────────────┘      └──────────────┘
   零运维 / 一键           中等成本 / 灵活      企业级 / 复杂
   适合:个人 / 小团队     适合:中小项目         适合:大厂

1.1 静态部署平台对比

平台免费额度优势劣势
Vercel100GB/月Next.js 亲爹、最快、Edge Func国内访问慢、商业用要付费
Netlify100GB/月Form / Identity 一站式国内访问慢
Cloudflare Pages无限带宽 ✅全球最快 CDN、WorkersUI 没那么友好
GitHub Pages1GB / 100GB 月免费、和 GitHub 集成仅静态、国内极慢
腾讯 / 阿里 OSS + CDN按量付费国内速度好要自己配 CDN/HTTPS

选型经验

  • 个人作品集/简历项目 → Cloudflare Pages(最快、免费、CN 也能访问)
  • 国内商业项目 → 腾讯 EdgeOne / 阿里云 OSS + CDN
  • 全球 SaaS/Next.js → Vercel

1.2 部署一个项目到 Vercel(手把手)

bash
# 1. 装 Vercel CLI
npm i -g vercel

# 2. 在项目根目录
vercel login
vercel              # 跟着提示走,自动检测框架

# 3. 后续每次部署
vercel --prod

或者最优方案:把代码推到 GitHub → 在 Vercel 后台 Import → 之后每次 git push 自动部署。


2. 自建 Nginx 部署

2.1 Nginx 是什么

Nginx = 高性能 反向代理 / Web 服务器 / 负载均衡器

对前端的核心用途:

  1. 托管静态文件(HTML/CSS/JS/图片)
  2. 反向代理 API(前端请求 /api/* 转发到后端)
  3. gzip / brotli 压缩
  4. HTTPS 终止(前端拿证书,后端纯 HTTP)
  5. SPA history 模式 fallback

2.2 生产可用的完整配置

详见 examples/nginx.conf。核心结构:

nginx
server {
  listen 80;
  server_name app.example.com;
  return 301 https://$host$request_uri;  # HTTP 全部跳 HTTPS
}

server {
  listen 443 ssl http2;
  server_name app.example.com;

  ssl_certificate     /etc/nginx/certs/app.crt;
  ssl_certificate_key /etc/nginx/certs/app.key;

  root /var/www/app/dist;
  index index.html;

  # 关键 1:SPA history 模式 fallback
  location / {
    try_files $uri $uri/ /index.html;
  }

  # 关键 2:带 hash 的静态资源 → 强缓存 1 年
  location ~* \.[a-f0-9]{8,}\.(js|css|png|jpg|webp)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
  }

  # 关键 3:HTML → 不缓存
  location ~* \.html$ {
    add_header Cache-Control "no-cache";
  }

  # 关键 4:API 反向代理
  location /api/ {
    proxy_pass http://backend:8080/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  # 关键 5:gzip
  gzip on;
  gzip_types text/plain text/css application/json application/javascript;
}

2.3 SPA history 模式必须 fallback

SPA 用 history 路由时,URL 是 /about/users/123,但服务器上没有这些文件。直接刷新 → 404。

解决:所有不存在的路径都 fallback 到 index.html,让前端路由接管。

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

⚠️ 这是 SPA 部署最常见的坑,刚学的人 100% 踩。


3. CDN 原理与运营

3.1 工作原理

            ┌──────── 边缘节点(北京)   ◄── 北京用户
源站 ──►   │   ┌──── 边缘节点(上海)   ◄── 上海用户
(你的服务器)│   │ ┌── 边缘节点(广州)   ◄── 广州用户
            │   │ │
            └───┴─┴── 内容同步

用户访问域名 → DNS 解析到最近的边缘节点 → 命中缓存直接返回,未命中回源

3.2 三个核心运营动作

动作含义何时用
预热(Push)主动把资源推到所有边缘节点大型发布前,避免首次回源风暴
刷新(Purge)把指定资源从所有节点驱逐紧急修复(如错误图片)
回源边缘没命中时去源站拉取自动行为

3.3 CDN 缓存策略

CDN 看的是响应头

Cache-Control: public, max-age=31536000, immutable
  • public:CDN 可缓存
  • private:仅浏览器可缓存(CDN 会跳过)
  • s-maxage=N:专给 CDN 的过期时间,覆盖 max-age

3.4 部署顺序的"教科书坑"

正确顺序

1. 先发静态资源到 CDN(带 hash 的 JS/CSS)
2. 等 CDN 同步完
3. 再发 HTML 到源站

错误顺序

1. 先发 HTML(引用了新 hash)
2. 用户立刻请求 → CDN 上还没新 JS → 404

更好的方案:配合 hash 文件名,新旧版本可以共存几小时。


4. Docker 入门

4.1 为什么要用 Docker

经典痛点:"在我电脑上能跑啊"。

Docker = 把"代码 + 依赖 + 操作系统"打成一个集装箱,搬到哪台机器都一样。

4.2 三大核心概念

概念解释类比
Dockerfile食谱(构建说明书)烹饪菜谱
镜像 Image按食谱做出来的成品(不可变)罐头
容器 Container运行中的镜像实例打开罐头吃

4.3 用 Nginx 镜像部署 SPA(标准模板)

Dockerfile(详见 examples/Dockerfile):

dockerfile
# ===== 阶段 1:构建(多阶段构建省镜像体积) =====
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# ===== 阶段 2:用 Nginx 当 web 服务器 =====
FROM nginx:1.25-alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

构建 + 运行:

bash
# 构建镜像
docker build -t my-spa .

# 运行容器(80 → 8080 端口映射)
docker run -d -p 8080:80 --name web my-spa

# 访问 http://localhost:8080

4.4 多阶段构建的好处

单阶段多阶段
1 个 image,包含 node_modules2 个阶段,最终 image 只有 nginx + dist
几百 MB30 MB 左右
包含构建工具,攻击面大干净

5. CI/CD 与 GitHub Actions

5.1 概念

  • CI(Continuous Integration)持续集成:代码 push → 自动跑 lint / test / build
  • CD(Continuous Deployment)持续部署:CI 通过 → 自动部署到环境

5.2 GitHub Actions 入门

仓库下 .github/workflows/deploy.yml,详见 examples/.github/workflows/deploy.yml

简化版:

yaml
name: Deploy
on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with: { node-version: '20', cache: 'npm' }

      - run: npm ci
      - run: npm run lint
      - run: npm run test
      - run: npm run build

      - name: Deploy to S3 / OSS / SCP
        run: |
          # 用 aws-cli / coscmd / scp 上传 dist
          echo "uploading..."

5.3 完整的 CI/CD 流水线

git push origin main


┌──────────────────┐
│ GitHub Actions   │
│  ① checkout 代码  │
│  ② cache 依赖     │
│  ③ npm ci         │
│  ④ lint + test   │
│  ⑤ build          │
│  ⑥ 上传产物      │
└─────────┬────────┘


┌──────────────────┐       ┌──────────────────┐
│ 测试环境部署      │ ────► │ 测试环境冒烟测试  │
└─────────┬────────┘       └─────────┬────────┘
          │                          │ 通过
          │                          ▼
          │                ┌──────────────────┐
          │                │ 等待人工 approve │
          │                └─────────┬────────┘
          │                          │
          │                          ▼
          │                ┌──────────────────┐
          └───────────────►│ 生产环境部署      │
                           │(灰度 → 全量)   │
                           └──────────────────┘

6. 灰度发布与 A/B 测试

6.1 灰度发布(Canary Release)

问题:直接 100% 上线新版本,万一有 bug,全员崩。

方案:先放给 1% 用户 → 观察 → 没问题再 5% → 20% → 100%。

实现方式

维度怎么做
按 IP 哈希Nginx split_clients,按 IP 算落到 A 或 B
按 Cookie网关识别 Cookie 里的"灰度组",路由到对应版本
按地域CDN 边缘节点按用户 IP 地理位置切流
按用户 ID后端按 user_id % 100 < 5 命中灰度

Nginx 灰度示例:

nginx
# 5% 用户去新版本
split_clients "${remote_addr}${http_user_agent}" $version {
    5%   "v2";
    *    "v1";
}

upstream v1 { server 10.0.0.1; }
upstream v2 { server 10.0.0.2; }

server {
    location / {
        proxy_pass http://$version;
    }
}

6.2 A/B 测试

灰度发布是为了"安全发布",A/B 测试是为了"验证假设"。

对比项灰度发布A/B 测试
目的风险控制数据驱动决策
比例慢慢放量到 100%长期 50/50(直到出结论)
关注指标错误率、性能转化率、CTR、留存

工具:Google Optimize(已停)、Optimizely、自建(埋点 + 统计平台)。


7. 监控与回滚

7.1 监控三大支柱

┌────────────────────────────────────────────┐
│  Logs     日志     什么时候出了什么事        │
│  Metrics  指标     PV / 错误率 / 性能 / 资源│
│  Traces   链路追踪 一个请求经过哪些服务      │
└────────────────────────────────────────────┘

前端常用工具:Sentry(错误监控)、SkyWalking/Datadog(链路追踪)、自建埋点 + 数仓

7.2 关键告警

指标阈值含义
5xx 错误率> 1%服务端可能挂了
LCP P75> 4s用户感知到的加载明显变慢
JS Error 率> 0.5%新版有 bug
API 超时率> 0.5%上游慢

7.3 回滚

最快的回滚:保留上一版本的产物,把 CDN/Nginx 指回去

GitHub Actions 配置时,每次发布带版本号

/cdn/dist/v20251018-1023/ ←── 本次发布
/cdn/dist/v20251015-0912/ ←── 上一版本(保留 7 天)

回滚 = 把 /cdn/dist/current 软链指回上一版本。


8. ⚠️ 易踩的坑

  1. SPA 部署没配 fallback:刷新 /about → 404。永远记得 try_files $uri /index.html

  2. HTML 也设强缓存:发了新版本用户死活刷不出来。HTML 必须 no-cache

  3. 部署顺序反了:先发 HTML 再发静态资源 → 用户拿到的 HTML 引用了 CDN 还没同步的 JS → 404。

  4. Docker 镜像里包含 .env:把生产数据库密码打进了镜像,传到了公共仓库。.dockerignore 必加 .env*node_modules

  5. Nginx 没开 gzip:白白浪费 70% 带宽。

  6. HTTPS 证书过期:90 天的 Let's Encrypt 没自动续,凌晨告警。用 certbot --renew --auto 或交给 Vercel/CF 全托管。

  7. CDN 跨域:图片在 CDN 域名上,<canvas> 想读图发现 CORS 报错。crossorigin="anonymous" + CDN 配 Access-Control-Allow-Origin: *

  8. GitHub Actions 没 cache:每次 npm install 跑 5 分钟。开 cache: 'npm'actions/cache@v4

  9. 生产环境 console.log / source-map 全暴露:生产构建务必移除 console、不传 sourcemap 到 CDN(保留私有上传 Sentry)。

  10. CI 里 npm install:会改 lockfile。永远用 npm ci 严格按锁文件装。

  11. 回滚预案没演练:真出事 30 分钟搞不定。每月演练一次"5 分钟回滚"。

  12. 环境变量在前端打包时硬编码VITE_API_URL=https://api.dev.com 打进了生产产物。注意构建时区分环境,或运行时通过 <script>window.__CONFIG__ = ...</script> 注入。


9. 一句话总结

部署 = 把代码变成可访问的服务:静态平台(Vercel/Cloudflare)一键搞定个人项目,Nginx 自建适合中小公司,Docker + CI/CD 是工业标准;CDN 让全球用户都快,灰度发布让上线不翻车,监控 + 回滚是兜底——这一章决定了你写的代码能不能"活"在线上。


10. 延伸阅读