Docker + Nginx + HTTPS 部署 Vue3 项目


上一篇《前端工程化与 CI/CD 落地实践》里,我讲了 GitHub Actions 怎么把”代码 → 镜像 → 服务器”串起来。这篇回答大家问得最多的一个问题:“我有了一台服务器、一个域名,具体怎么把 Vue 项目真正跑起来、带 HTTPS 那种?” 本文用一台 CentOS 服务器为例,跑通完整链路。

一、整体架构

用户浏览器
   │  https://blog.shanky.cn
   ▼
Nginx(宿主 443/80 端口,容器内对外唯一入口)
   ├── 443  HTTPS + HTTP/2  →  反代到 Vue 静态容器(8080)
   └── 80   HTTP → 301 跳转 HTTPS(配合 certbot 签发/续期)
   │
   ▼
Vue3 前端容器(Nginx 托管 dist,SPA 路由回退)

核心思路:Vue 的 dist 用 Nginx 容器托管,宿主机的 Nginx 只做”HTTPS 卸载 + 反代”。这样前端容器可以随时重建、换版本,证书和路由规则都收在宿主机一层,职责清晰。

二、第一步:服务器基础环境

# 1. 安装 Docker(CentOS 7+,方法同我之前写的《Centos 安装 docker-compose》前置)
curl -fsSL https://get.docker.com | bash -s docker
systemctl enable --now docker

# 2. 安装 docker-compose 插件(可选,多容器编排更方便)
yum install -y docker-compose-plugin

# 3. 放行端口(云厂商安全组 + 本机 firewalld 都要放)
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload

新手最常踩的坑:云厂商安全组系统防火墙是两套东西,都要放行 80/443,否则从外网永远打不开,但本机 curl 却正常。

三、第二步:构建 Vue3 镜像

前端项目结构(接上一篇的 CI/CD 工程):

# Dockerfile —— 多阶段构建
# 阶段1:node 构建(带缓存优化,package-lock 不变就不重装依赖)
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 阶段2:nginx 托管静态产物
FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
# nginx.conf —— 容器内配置:SPA 回退 + gzip + 静态资源长缓存
server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;

    gzip on;
    gzip_types text/plain text/css application/javascript application/json image/svg+xml;

    location / {
        try_files $uri $uri/ /index.html;   # SPA 路由回退
    }

    location /assets/ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

本地构建并起容器验证:

docker build -t my-vue-app:1.0.0 .
# 先在本机 8080 验证,容器内映射到 80
docker run -d --name my-vue-app -p 8080:80 my-vue-app:1.0.0
curl http://localhost:8080   # 能返回 index.html 即成功

SPA 路由回退(try_files ... /index.html)的坑我早在《vue-cli 项目在 nginx 下子页面刷新404》写过,忘了的回去复习:刷新 /about 时 Nginx 找不到对应文件,必须回退到 index.html 交给前端路由。

四、第三步:宿主机 Nginx 做 HTTPS 卸载

前端容器跑在 8080,但用户只认 443。在宿主机装 Nginx,做HTTPS 终结 + 反代

yum install -y nginx
# /etc/nginx/conf.d/vue-app.conf
server {
    listen 443 ssl http2;
    server_name blog.shanky.cn;

    # 证书路径(certbot 生成,见下节)
    ssl_certificate     /etc/letsencrypt/live/blog.shanky.cn/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/blog.shanky.cn/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;

    # 反代到 Vue 容器
    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-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

# 80 端口统一跳 HTTPS(顺便让 certbot 的验证请求通过)
server {
    listen 80;
    server_name blog.shanky.cn;
    location /.well-known/acme-challenge/ { root /var/www/certbot; }
    location / { return 301 https://$host$request_uri; }
}

为什么用”宿主机 Nginx + 容器内 Nginx”两层?一层职责是证书/反代/跳转,一层职责是托管静态资源。你也可以用一个 Nginx 容器把两个职责合并,但单独一层宿主机 Nginx 在以后挂多个站点、多个后端时更灵活——这也是”前端也要懂点服务器”的价值所在。

五、第四步:Let’s Encrypt 免费证书 + 自动续期

用 certbot 签发免费 HTTPS 证书:

yum install -y certbot python3-certbot-nginx

# 非交互签发(nginx 插件会自动改配置,但我们已经手写了 conf,用 webroot 方式更稳)
certbot certonly --webroot -w /var/www/certbot \
  -d blog.shanky.cn \
  --email your@email.com --agree-tos --no-eff-email

证书有效期 90 天,必须自动续期。加定时任务:

# 每月1号凌晨 3 点续期 + 重载 nginx
echo "0 3 1 * * root certbot renew --quiet && systemctl reload nginx" >> /etc/crontab

线上 HTTPS 最关键的不是”怎么签”,而是”怎么续”。很多站点证书过期打不开,就是因为只配了签发、忘了配自动续期。certbot renew 只会对距到期不足 30 天的证书真正续期,平时运行几乎零开销,放心挂 cron。

六、第五步:接上 CI/CD 自动发布

有了容器化基础,接上一篇的 GitHub Actions 就水到渠成:CI 里 docker build 打镜像推送到镜像仓库,CD 阶段 SSH 到服务器 docker pull + docker run

# .github/workflows/deploy.yml 的关键步骤
      - name: 服务器部署
        uses: appleboy/ssh-action@v1
        with:
          host: $
          username: root
          key: $
          script: |
            docker pull my-vue-app:$
            docker stop my-vue-app || true
            docker rm my-vue-app || true
            docker run -d --name my-vue-app -p 8080:80 my-vue-app:$
# 服务器上验证 8080 容器 + 443 外层是否都正常
curl -sI https://blog.shanky.cn | head -n 5
# HTTP/2 200  ✔

到这里,你从”写代码”到”上线”只需要一次 git push:CI 自动 lint/测试/构建/打镜像,CD 自动部署,HTTPS 证书每月自动续期。这就是上一篇说的全链路闭环,也是前端工程师最有含金量的加分项。

七、总结:这份部署清单的含金量

把这条链路完整跑通并讲清楚,面试时你就能说出别人说不出的细节:

  1. 安全组 vs 防火墙:两套放行机制,缺一不可;
  2. 多阶段构建:为什么 node 构建产物只需要 nginx 一个运行时,镜像能瘦多少(docker images 对比一下);
  3. HTTPS 卸载:为什么证书要放宿主 Nginx 而不是 Vue 容器里;
  4. 自动续期certbot renew + cron,运维意识落地;
  5. 全自动发布:一次 git push 直达线上,版本可回滚(回滚方法见上一篇)。

一句话总结:”我用 Docker 多阶段构建打包 Vue3 产物,宿主机 Nginx 做 HTTPS 终结和反向代理,certbot 自动续期证书,CI/CD 一键发布到服务器,整个部署链路是自动化的、可回滚的。”

这篇文章和《前端工程化与 CI/CD》《Centos 安装 docker-compose》《Nginx 负载均衡》《vue-cli 项目在 nginx 下子页面刷新404》一起,构成了”前端从开发到上线的完整知识闭环”。下一篇我会把这个博客本身容器化,讲讲静态站如何用 Docker 跑出构建缓存和自动发布。


Similar Posts

Content