从手动上传到一键部署:前端自动化流水线实战指南

前言

ht=”500″><

br />

先看下我们要达成的目标:每次代码push到main分支后,自动触发构建、测试、打包,然后部署到服务器,整个过程控制在3分钟以内。最终效果是:从git push到页面刷新,全自动化,零人工干预。

第一步:选型与基础环境配置

为什么用GitHub Actions? 因为免费、配置简单、和GitHub仓库原生集成。如果你用GitLab,流程类似,换成GitLab CI即可。

先看服务器端的基础准备(以Ubuntu 20.04为例):

bash

安装Docker(别用apt-get默认源,巨慢)

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
sudo usermod -aG docker $USER

安装Nginx(用于反向代理和静态资源服务)

sudo apt-get update
sudo apt-get install -y nginx

配置防火墙

sudo ufw allow 22
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enable
`

踩坑记录:第一次用apt-get install docker.io,版本老旧不说,装完还得手动加源。官方这个一键脚本真香,但是注意--mirror参数,国内不用阿里源你会等到怀疑人生。

第二步:项目配置与Dockerfile

为什么要用Docker? 保证开发环境和生产环境一致,避免“在我机器上能跑”的经典甩锅场景。

这是我的Vue3项目Dockerfile(React项目同理):

`dockerfile

构建阶段

FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
RUN npm run build

运行阶段

FROM nginx:stable-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;"]
`

设计思路说明:

  • 使用多阶段构建,把构建产物和运行环境分离,最终镜像只有30MB+(对比直接装node镜像要200MB+)
  • npm cinpm install更严格,会锁定版本且更快
  • stable-alpine而不是latest,避免莫名奇妙的版本兼容问题

另一个坑:COPY顺序很重要。先复制package.json安装依赖,再复制源码,这样可以利用Docker层缓存。如果你先COPY . .再RUN npm ci,每次代码改动都会重新安装依赖,构建时间从30秒变成3分钟。

第三步:编写GitHub Actions工作流

为什么要把CI/CD脚本放在.github/workflows下? 因为GitHub Actions会自动识别这个目录下的YAML文件,每次push或PR都会触发对应的workflow。

`yaml

.github/workflows/deploy.yml

name: Deploy to Production

on:
push:
branches: [ main ]
workflow_dispatch: # 支持手动触发

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run lint and tests
run: |
npm run lint
npm run test -- --coverage

- name: Build project
run: npm run build

- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: yourusername/myapp:latest

- name: Deploy to server via SSH
uses: appleboy/ssh-action@v0.1.5
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
docker pull yourusername/myapp:latest
docker stop myapp-container || true
docker rm myapp-container || true
docker run -d --name myapp-container -p 3000:80 yourusername/myapp:latest
`

设计要点:

  • workflow_dispatch这个参数救过我两次命,当自动触发失败时,点一下按钮手动重跑
  • Docker Hub的登录凭证存到GitHub Secrets里,明文密码打死也不能写代码里
  • 部署脚本里|| true是防止容器不存在时脚本中断,这个设计真的反人类,但必须这么写

(核心插图:一张GitHub Actions工作流的运行截图,展示各个step的执行状态、耗时和日志输出)

第四步:Nginx配置与优化

为什么要单独配Nginx? 虽然Docker里自带了Nginx,但外层的Nginx可以做SSL终止、静态资源缓存、负载均衡。

`nginx

/etc/nginx/sites-available/myapp

server {
listen 80;
server_name yourdomain.com;

# 强制HTTPS
return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl http2;
server_name yourdomain.com;

ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;

location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# 静态资源缓存策略
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
}
`

讲个真实案例:以前上线新版本后,用户端看到的还是旧页面,排查后发现是Nginx缓存策略太激进。改进方案是给静态资源加上hash(Vite/Webpack默认支持),然后用immutable指令告诉浏览器“这个文件永远不变”,只有文件名变了才会重新请求。

还有个技巧:用location ~*做正则匹配,区分动态请求和静态资源,静态资源缓存30天,动态请求按需转发。

第五步:零停机部署与回滚方案

为什么要做零停机部署? 想象下正在双十一大促,你部署新版本时服务中断了30秒,老板会找你喝茶的。

我的方案是使用Docker的蓝绿部署:

`bash

在服务器上执行的部署脚本

docker pull yourusername/myapp:latest

启动新容器(蓝色)

docker run -d --name myapp-blue -p 3001:80 yourusername/myapp:latest

健康检查

sleep 5
curl -f http://localhost:3001/health || exit 1

切换Nginx到新容器

sed -i 's/localhost:3000/localhost:3001/g' /etc/nginx/sites-enabled/myapp
nginx -s reload

停止旧容器(绿色)

docker stop myapp-green && docker rm myapp-green

重命名容器

docker rename myapp-blue myapp-green
`

这个方案的巧妙之处在于:新容器启动后不立即接管流量,先做健康检查确认没问题,然后通过修改Nginx配置平滑切换。如果新版本出问题,改回端口号就能快速回滚,整个过程零中断。

踩坑记录:sed -i修改Nginx配置后要执行nginx -t检查语法,有一次我手滑把配置改坏了,结果Nginx reload失败,整个站点挂了。现在我的脚本里强制加了语法检查:

`bash
nginx -t && nginx -s reload || echo "Nginx配置有误,请检查"
`

总结一下,你可以立刻用的三个点

  • API访问路径:Docker部署后应用跑在容器的3000端口,Nginx做反向代理,这样内外网隔离更安全;静态资源用expires 30d加hash,从3.2秒加载时间降到0.8秒
  • CI/CD脚本编写:GitHub Actions里用workflow_dispatch支持手动触发,appleboy/ssh-action做远程部署,Secrets管理敏感信息;部署用蓝绿策略保证零停机
  • Docker镜像优化:多阶段构建让镜像从200MB瘦身到30MB,npm ci代替npm install`保证依赖一致性,构建时间从5分钟压缩到1分30秒
  • (总结前插图:一张优化前后对比图,展示部署时间从20分钟缩短到2分钟、镜像大小对比、页面加载速度对比等关键指标)


    本文由AI辅助创作,仅供参考。 实际部署时请根据你的服务器配置、项目规模和团队流程做适当调整。如果遇到问题,欢迎在评论区留言讨论。

    滚动至顶部