手把手系列之——前端的Nginx 与 Docker 部署实践

前言

Vue、React等现代前端工程在本地跑通后,总要面对「怎么上线、在哪跑、谁提供静态资源」这些问题。把打包好的 dist 丢到服务器只是第一步,选对部署方式,能少踩环境不一致、路由 404、重复构建慢等深坑。

目前常见的做法大致分为两种:一、在服务器上直接装 Nginx,用主机上的 Nginx 提供静态文件 (直装部署);二、用 Docker 跑 Nginx 或把「构建 + Nginx」打成一个镜像 (容器化部署)。

前者配置简单、和现有 Nginx 环境兼容好,适合单机、内网或快速验证;后者环境一致、易做 CI/CD 和版本回滚,适合多环境、团队协作或离线交付场景。我们团队就是先上了直装,后期devops引入流水线时才迁到容器化的。

如果你正在给 Vue 前端选部署方案,或打算从直装迁到 Docker,希望这篇文章能手把手带你解决问题。


一、直装部署

直装部署顾名思义就是指在宿主机上直接安装 Nginx,将前端构建产物(如 dist)放到指定目录,由主机上的 Nginx 提供静态资源。适合单机、快速验证或已有 Nginx 环境的场景。


1.1 流程概览与安装

流程

flowchart TD Start([开始部署]) --> Install[apt install nginx] Install --> Config[配置 site 与 nginx.conf] Config --> Test[nginx -t 校验] Test --> Host[配置本机 /etc/hosts] Host --> Upload[上传 dist 到站点目录] Upload --> Reload[systemctl reload nginx] Reload --> End([访问站点域名])

安装与基础配置

在 Ubuntu 上安装 Nginx 并确认服务正常:

bash 复制代码
apt update
apt install nginx -y
systemctl status nginx

查找主配置文件位置(一般为 /etc/nginx/nginx.conf):

bash 复制代码
find . -name "nginx.conf"
# 编辑主配置(如需)
vi /etc/nginx/nginx.conf

1.2 站点配置与访问验证

站点配置(如 ababa.conf)

conf.d 下新增站点配置:

nginx 复制代码
server {
    listen 80;
    server_name ababa.com;

    location / {
        root /home/website/vue-project;
        index index.html;
    }
}

要点:

  • root 指向前端构建产物所在的目录(后续把 dist 内容放到该目录)。
  • 若为 SPA,需保证 Nginx 对前端路由做 fallback(见「SPA 路由回退」)

校验并重载:

bash 复制代码
nginx -t
systemctl reload nginx

本机 hosts 与访问

在开发/测试机上把域名指到服务器 IP:

bash 复制代码
sudo vi /etc/hosts
# 新增一行(IP 改为你的服务器地址)
# 10.211.55.4 ababa.com

将前端项目的 dist 上传到 /home/website/vue-project,访问 http://ababa.com 即可。

SPA 路由回退(可选)

Vue Router 使用 history 模式时,需让 Nginx 把未匹配到文件的请求都返回 index.html,配置如下:

nginx 复制代码
location / {
    root /home/website/vue-project;
    index index.html;
    try_files $uri $uri/ /index.html;
}

1.3 简易实现一个自动化部署:Husky + rsync

在直装方案下,若希望每次 git commit 时自动构建并同步到服务器,可用 Husky 触发部署脚本(内部执行 build + rsync)。

流程

flowchart TD Dev["开发者 git commit"] --> Husky["Husky pre-commit 钩子"] Husky --> Build["执行 deploy.sh: npm run build"] Build --> Rsync["rsync 同步 dist 到服务器"] Rsync --> Dir["服务器目录
/home/website/vue-project"] Dir --> Nginx["Nginx 提供静态文件"] Nginx --> End(["用户访问"])

实现步骤

安装 rsync(Mac)与 Husky:

bash 复制代码
brew install rsync
pnpm i husky -D
npx husky install

部署脚本 .husky/deploy.sh

bash 复制代码
#!/bin/sh
# 构建
npm run build
# 上传部署(按需改端口、用户、路径)
rsync -avz --delete -e "ssh -p 22" ./dist/ root@ubuntu:/home/website/vue-project

pre-commit 钩子.husky/pre-commit):

bash 复制代码
#!/usr/bin/env sh
sh $(dirname -- "$0")/deploy.sh

赋予执行权限:

bash 复制代码
chmod +x .husky/deploy.sh

之后每次 git commit 会先执行构建再 rsync 到服务器,适合小团队快速更新测试/演示环境;生产环境仍建议走 CI/CD 与审批流程。


二、容器化部署

容器化部署是将 Nginx 与静态资源(或构建过程)封装在 Docker 镜像/容器中,通过镜像与编排实现环境一致、一键启停和版本化的部署。


2.1 总体流程

flowchart LR subgraph 构建 Dockerfile[Dockerfile] --> Build[docker build] Build --> Image[镜像] end subgraph 运行 Image --> Run[docker run / compose up] Run --> Container[Nginx 容器] Container --> Port[端口映射 宿主机:容器] end

容器内 Nginx 与宿主机上的 Nginx 互不干扰,可同时存在。

使用 docker-compose 挂载已有目录

若宿主机上已有构建好的 dist(如放在 /home/website/vue-project),可用 compose 只起一个 Nginx 容器并挂载该目录:

yaml 复制代码
services:
  nginx:
    container_name: nginx
    restart: always
    image: nginx
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "8082:80"
    volumes:
      - /home/website/vue-project:/usr/share/nginx/html

启动与查看日志:

bash 复制代码
docker compose up -d
docker logs -f nginx

访问 http://宿主机IP:8082 即可。宿主机 Nginx 停掉时,容器内 Nginx 仍可独立对外提供页面。

挂载宿主机 Nginx 配置

若希望容器使用与直装一致的 Nginx 配置(多站点、自定义 server 等),可挂载配置与站点目录:

yaml 复制代码
services:
  nginx:
    container_name: nginx
    restart: always
    image: nginx
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "8082:80"
    volumes:
      - /etc/nginx/conf.d:/etc/nginx/conf.d/
      - /etc/nginx/nginx.conf:/etc/nginx/nginx.conf
      - /home/website/:/home/website

使用自定义 compose 文件时:

bash 复制代码
docker compose -f docker-compose.nginx.yml up -d

便于从直装平滑迁移到容器。


2.2 Dockerfile 多阶段构建

在容器化方案中,除了「宿主机先构建再挂载」,还可以在镜像内完成构建,得到「构建 + Nginx」一体的镜像。

构建与运行流程

flowchart TD subgraph 构建阶段 S1[Stage1: node 镜像] --> S1_1[COPY package*.json] S1_1 --> S1_2[pnpm install] S1_2 --> S1_3[COPY 源码] S1_3 --> S1_4[npm run build] S1_4 --> S2[Stage2: nginx 镜像] S2 --> S2_1[COPY dist 到 /usr/share/nginx/html] S2_1 --> S2_2[EXPOSE 80] end S2_2 --> Image[最终镜像] Image --> Run[docker run -p 18080:80]

多阶段构建好处:第一阶段用 Node 安装依赖并构建,第二阶段只保留 dist + Nginx,最终镜像体积小、无源码与 node_modules。

单 Dockerfile 示例

dockerfile 复制代码
# build stage(使用国内镜像,避免 Docker Hub 超时)
FROM docker.1ms.run/library/node:20-alpine AS build-stage
WORKDIR /app
COPY package*.json ./
COPY pnpm*.yaml ./

RUN npm install -g pnpm --registry=https://registry.npmmirror.com && \
    npm config set registry=https://registry.npmmirror.com && \
    pnpm install

COPY . .
ENV NODE_OPTIONS="--experimental-require-module"
RUN npm run build

# production stage
FROM docker.1ms.run/library/nginx:stable-alpine AS production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

构建与运行:

bash 复制代码
docker build -t project:1.0 .
docker run -itd --name test -p 18088:80 fproject:1.0

访问 http://localhost:18088/ 即可看到前端页面。


2.3 构建优化与镜像迁移

构建优化:依赖层与产物层分离

当依赖体积较大时,可把「安装依赖」和「拷贝源码并构建」拆开,避免每次改代码都重新 pnpm install

flowchart LR DockerfileDev[Dockerfile-dev] --> DevImage[dev:1.0 镜像] DevImage --> DockerfileProd[Dockerfile-prod] DockerfileProd --> ProdImage[project:2.0]

Dockerfile-dev(只装依赖,可反复复用):

dockerfile 复制代码
FROM docker.1ms.run/library/node:20-alpine AS build-stage
WORKDIR /app
COPY package*.json ./
COPY pnpm*.yaml ./

RUN npm install -g pnpm --registry=https://registry.npmmirror.com && \
    npm config set registry=https://registry.npmmirror.com && \
    pnpm install
bash 复制代码
docker build -f Dockerfile-dev -t dev:1.0 .

Dockerfile-prod(基于 dev 镜像,只做构建与 Nginx):

dockerfile 复制代码
FROM dev:1.0 AS build-stage
WORKDIR /app
COPY . .
ENV NODE_OPTIONS="--experimental-require-module"
RUN npm run build

FROM docker.1ms.run/library/nginx:stable-alpine AS production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
bash 复制代码
docker build -f Dockerfile-prod -t project:2.0 .
docker run -itd --name test1 -p 18088:80 project:2.0

依赖变更时才重打 dev:1.0,日常改代码只需重打 project:2.0,加快迭代。

镜像导出与恢复

有时在税局封闭开发,只能使用内网,那么在内网或无外网环境中,也可通过「导出 → 拷贝 → 导入」迁移镜像:

sequenceDiagram participant 构建机 as 构建机 participant 文件 as tar 文件 participant 目标机 as 目标服务器 构建机->>文件: docker save -o dev.1.0.tar dev:1.0 构建机->>目标机: 上传 dev.1.0.tar(scp/ U 盘等) 目标机->>目标机: docker load -i dev.1.0.tar
bash 复制代码
# 导出
docker save -o dev.1.0.tar dev:1.0

# 在目标机器导入
docker load -i dev.1.0.tar

只适用于离线环境或需要固定版本交付的场景。


三、总结与对比

3.1 两种方式对比

维度 直装部署 容器化部署
环境一致性 依赖主机系统,易受系统环境影响 镜像内环境一致,可跨机器复现
运维成本 需单独安装 Nginx、配置站点、管理进程 一条命令起停,配置可挂载或打进镜像
构建与发布 本地/CI 构建后上传 dist 可在镜像内构建,或先构建再挂载
自动化 易与 Husky + rsync 结合做 commit 部署 易与 CI/CD 流水线结合,镜像 tag 即版本
适用场景 单机、快速验证、已有 Nginx 主机 多环境、CI/CD、需版本化、离线交付

3.2 流程对比示意

flowchart LR subgraph 直装部署 A1[源码构建] --> A2[上传 dist] A2 --> A3[主机 Nginx] A3 --> A4[浏览器] end subgraph 容器化部署 B1[Dockerfile / 构建] --> B2[镜像] B2 --> B3[容器 Nginx] B3 --> B4[浏览器] end

3.3 小结

  • 直装部署 :适合单机、已有 Nginx、快速验证的场景;配置好 serverroottry_files 即可。
  • 容器化部署:适合要求多环境一致、CI/CD、版本化部署等场景;可用 compose 挂载目录或配置,也可用 Dockerfile 打出「构建 + Nginx」一体的镜像

按「直装 → 容器化(compose 挂载 → Dockerfile 多阶段 → 优化与迁移)」这条线动手实践,基本就可以覆盖从本机 Nginx 到容器化与自动化的大部分场景啦,掘友们快来试试吧!

相关推荐
小锋学长生活大爆炸8 小时前
【教程】免Root在Termux上安装Docker
运维·docker·容器
进击切图仔8 小时前
常用 Docker 命令备份
运维·docker·容器
德育处主任12 小时前
『NAS』将魂斗罗马里奥塞进NAS里
前端·javascript·docker
Mr.小海13 小时前
Docker 底层解析与生产环境实战指南
java·docker·eureka
流氓也是种气质 _Cookie15 小时前
Linux上安装Docker
linux·redis·docker
程序员洪志道15 小时前
封装复杂性:一个反复生效的架构手法
nginx·程序员
小锋学长生活大爆炸16 小时前
【教程】查看docker容器的TCP连接和带宽使用情况
tcp/ip·docker·容器
ccino .16 小时前
【Drupal文件上传导致跨站脚本执行(CVE-2019-6341)】
运维·网络安全·docker·容器
江湖有缘17 小时前
自托管RSS解决方案:Docker化Fusion安装教程
java·jvm·docker
sun032218 小时前
【Docker】构建镜像时使用的 Dockerfile ,以及其中的 MicroDNF
运维·docker·容器