在现代前端开发中,自动化部署已经成为提高开发效率的关键环节。对于个人网站项目,如何实现代码提交后自动构建并部署到服务器,是每个前端开发者都需要面对的问题。本文将详细介绍两种主流的自动化部署方案:Git Hooks 本地部署和 Gitee Actions 云端部署,并分析它们的优缺点和适用场景。
方案一:Git Hooks + 本地脚本(纯本地部署)
原理
Git Hooks 是 Git 提供的钩子机制,允许在特定的 Git 事件(如 commit、push)发生时执行自定义脚本。通过在 pre-push 或 post-commit 钩子中执行打包和部署命令,实现本地自动部署。
核心步骤
- 生成 SSH 密钥:确保本地可以免密登录服务器
- 创建部署脚本:编写
deploy.sh脚本(或deploy.ps1对于 Windows PowerShell),包含构建、上传、权限设置等步骤 - 配置 Git 钩子:在
.git/hooks/pre-push中添加触发部署的逻辑 - 赋予执行权限:确保脚本可以正常执行
详细配置示例
1. 生成 SSH 密钥
ssh-keygen -t ed25519 -C "local-deploy-key"
ssh-copy-id user@your-server-ip
2. 创建部署脚本 (deploy.sh)
#!/bin/bash
set -e # 遇到错误立即退出
# 日志文件
LOG_FILE=".deploy.log"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始部署" >> $LOG_FILE
# 错误处理
trap 'echo "[$(date '+%Y-%m-%d %H:%M:%S')] 部署失败" >> $LOG_FILE' ERR
echo "===== 开始构建 ====="
npm ci # 使用 npm ci 确保依赖版本一致
npm run build
# 服务器配置
SERVER_USER="user"
SERVER_IP="your-server-ip"
BASE_PATH="/var/www"
APP_NAME="third_party_services"
DEPLOY_PATH="$BASE_PATH/$APP_NAME"
# 创建版本目录
VERSION=$(date +%Y%m%d-%H%M%S)
DEPLOY_DIR="$DEPLOY_PATH-$VERSION"
DEPLOY_DIR="$BASE_PATH/${APP_NAME}_$VERSION"
echo "===== 上传到服务器 ====="
# 单服务器部署
ssh $SERVER_USER@$SERVER_IP "mkdir -p $DEPLOY_DIR"
rsync -avz --delete --compress --exclude="*.map" --exclude="*.log" dist/ $SERVER_USER@$SERVER_IP:$DEPLOY_DIR/
# 更新符号链接
echo "===== 更新部署链接 ====="
ssh $SERVER_USER@$SERVER_IP "rm -f $DEPLOY_PATH"
ssh $SERVER_USER@$SERVER_IP "ln -sf $DEPLOY_DIR $DEPLOY_PATH"
# 设置文件权限
echo "===== 设置文件权限 ====="
ssh $SERVER_USER@$SERVER_IP "chown -R www-data:www-data $DEPLOY_DIR && chmod -R 755 $DEPLOY_DIR"
# 健康检查
echo "===== 检查服务状态 ====="
HEALTH_CHECK=$(ssh $SERVER_USER@$SERVER_IP "curl -s -o /dev/null -w '%{http_code}' http://localhost")
if [ "$HEALTH_CHECK" = "200" ]; then
echo "✅ 服务正常"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 服务正常" >> $LOG_FILE
else
echo "❌ 服务异常,HTTP 状态码: $HEALTH_CHECK"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 服务异常,HTTP 状态码: $HEALTH_CHECK" >> $LOG_FILE
fi
echo "===== 部署完成 ====="
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 部署完成" >> $LOG_FILE
3. 高级错误处理与日志记录
为了更全面的错误处理和日志记录,可以增强部署脚本:
#!/bin/bash
set -e
# 日志文件
LOG_FILE=".deploy.log"
# 清空旧日志(可选)
> $LOG_FILE
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> $LOG_FILE
echo "$1"
}
# 错误处理函数
error_handler() {
log "部署失败: $1"
exit 1
}
# 设置错误陷阱
trap 'error_handler "未知错误"' ERR
log "开始部署"
try {
log "开始构建"
npm ci
npm run build
if [ $? -ne 0 ]; then
error_handler "构建失败"
fi
# 服务器配置
SERVER_USER="user"
SERVER_IP="your-server-ip"
DEPLOY_PATH="/var/www/third-party-services"
# 创建版本目录
VERSION=$(date +%Y%m%d-%H:%M:%S)
DEPLOY_DIR="$DEPLOY_PATH-$VERSION"
log "上传到服务器"
ssh $SERVER_USER@$SERVER_IP "mkdir -p $DEPLOY_DIR" || error_handler "创建目录失败"
rsync -avz --delete --compress --exclude="*.map" --exclude="*.log" dist/ $SERVER_USER@$SERVER_IP:$DEPLOY_DIR/ || error_handler "文件同步失败"
log "更新部署链接"
ssh $SERVER_USER@$SERVER_IP "ln -sf $DEPLOY_DIR $DEPLOY_PATH" || error_handler "更新链接失败"
log "设置文件权限"
ssh $SERVER_USER@$SERVER_IP "chown -R www-data:www-data $DEPLOY_DIR && chmod -R 755 $DEPLOY_DIR" || error_handler "设置权限失败"
log "检查服务状态"
HEALTH_CHECK=$(ssh $SERVER_USER@$SERVER_IP "curl -s -o /dev/null -w '%{http_code}' http://localhost")
if [ "$HEALTH_CHECK" = "200" ]; then
log "✅ 服务正常"
else
log "❌ 服务异常,HTTP 状态码: $HEALTH_CHECK"
fi
log "部署完成"
} catch {
error_handler "执行过程中出错"
}
4. 配置 Git 钩子 (pre-push)
#!/bin/sh
# .git/hooks/pre-push
# 获取当前分支
current_branch=$(git symbolic-ref --short HEAD)
# 只在 main 分支执行部署
if [ "$current_branch" != "main" ]; then
echo "🚀 当前分支不是 main,跳过部署"
exit 0
fi
echo "🔨 Building project..."
npm run build
if [ $? -ne 0 ]; then
echo "❌ Build failed, deployment aborted."
exit 1
fi
echo "📦 Uploading to server..."
./deploy.sh
echo "✅ Deployment completed."
5. Windows PowerShell 替代方案
对于 Windows 用户,可以创建 deploy.ps1 文件:
Write-Host "===== 开始构建 ====="
npm run build
if ($LASTEXITCODE -ne 0) {
Write-Host "❌ Build failed, deployment aborted." -ForegroundColor Red
exit 1
}
Write-Host "===== 上传到服务器 ====="
# 使用 scp 替代 rsync(Windows 内置)
scp -r dist/* user@your-server-ip:/var/www/third-party-services/
Write-Host "===== 部署完成 ====="
然后在 .git/hooks/pre-push 中调用 PowerShell 脚本:
#!/bin/sh
# .git/hooks/pre-push
# 获取当前分支
current_branch=$(git symbolic-ref --short HEAD)
# 只在 main 分支执行部署
if [ "$current_branch" != "main" ]; then
echo "🚀 当前分支不是 main,跳过部署"
exit 0
fi
echo "🔨 Building project..."
npm run build
if [ $? -ne 0 ]; then
echo "❌ Build failed, deployment aborted."
exit 1
fi
echo "📦 Uploading to server..."
powershell -ExecutionPolicy Bypass -File deploy.ps1
echo "✅ Deployment completed."
优点
- 安全性高:所有操作在本地执行,密钥不存储在第三方平台
- 配置简单:无需学习 CI/CD 配置语法,只需编写简单的 shell 脚本
- 不依赖外部服务:纯本地操作,不受网络或平台限制
- 速度快:本地网络环境通常比云端构建更快
缺点
- 环境依赖:打包依赖本地环境,需要确保所有开发者的环境配置一致
- 多人协作成本高:每个开发者都需要配置本地钩子和 SSH 密钥
- 受本地网络影响:上传速度取决于本地网络状况
- 缺乏集中管理:部署配置分散在各个开发者的本地环境中
方案二:Gitee Actions(云端打包推送)
原理
Gitee Actions 是 Gitee 提供的 CI/CD 服务,允许在云端服务器上执行构建和部署任务。通过配置工作流文件,当代码推送到仓库时自动触发构建和部署流程。
核心步骤
- 生成部署密钥:创建专门用于 Gitee Actions 登录服务器的 SSH 密钥
- 配置 Gitee 仓库密钥:将私钥和服务器信息添加到仓库密钥中
- 创建工作流文件:在
.gitee/workflows/deploy.yml中配置构建和部署步骤 - 提交并推送:将工作流文件推送到仓库,触发自动部署
详细配置示例
1. 生成部署密钥
ssh-keygen -t ed25519 -C "gitee-actions-deploy-key" -f ~/.ssh/deploy_key
2. 配置 Gitee 仓库密钥
SERVER_SSH_KEY:私钥内容SERVER_HOST:服务器 IP 或域名SERVER_USER:SSH 登录用户名SERVER_TARGET:服务器上的部署路径
3. 创建工作流文件 (deploy.yml)
name: Deploy to Cloud Server
on:
push:
branches: [main] # 当推送到 main 分支时触发,可按需修改
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install pnpm
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Build project
run: pnpm run build
- name: Deploy to server via rsync
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
REMOTE_HOST: ${{ secrets.SERVER_HOST }}
REMOTE_USER: ${{ secrets.SERVER_USER }}
TARGET: ${{ secrets.SERVER_TARGET }}
SOURCE: "dist/" # 打包输出目录
ARGS: "-avz --delete --compress --exclude='*.map'"
GitHub Actions 配置示例
如果你使用 GitHub 作为代码托管平台,可以使用 GitHub Actions 实现类似的自动化部署。GitHub Actions 的配置与 Gitee Actions 非常相似,只是配置文件存放位置和某些语法略有不同。
1. 生成部署密钥
ssh-keygen -t ed25519 -C "github-actions-deploy-key" -f ~/.ssh/github_deploy_key
2. 配置 GitHub 仓库密钥
- 在 GitHub 仓库中,进入 “Settings” → “Secrets and variables” → “Actions”
- 添加以下密钥:
SERVER_SSH_KEY:私钥内容SERVER_HOST:服务器 IP 或域名SERVER_USER:SSH 登录用户名SERVER_TARGET:服务器上的部署路径
3. 创建工作流文件 (.github/workflows/deploy.yml)
name: Deploy to Cloud Server
on:
push:
branches: [main] # 当推送到 main 分支时触发,可按需修改
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install pnpm
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Build project
run: pnpm run build
- name: Deploy to server via rsync
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
REMOTE_HOST: ${{ secrets.SERVER_HOST }}
REMOTE_USER: ${{ secrets.SERVER_USER }}
TARGET: ${{ secrets.SERVER_TARGET }}
SOURCE: "dist/" # 打包输出目录
ARGS: "-avz --delete --compress --exclude='*.map'"
优点
- 环境统一:在云端服务器上执行构建,确保环境一致性
- 团队协作友好:只需配置一次,所有团队成员都可以享受自动部署
- 不占本地性能:构建和部署在云端执行,不影响本地开发
- 安全可靠:通过 Gitee 仓库密钥管理,避免密码泄露
- 易于扩展:支持多环境部署、测试集成等高级功能
缺点
- 依赖第三方平台:需要依赖 Gitee 的 CI/CD 服务
- 有使用限制:Gitee 免费版每月有 2000 分钟的构建额度
- 配置相对复杂:需要学习 YAML 配置语法和 CI/CD 概念
- 构建速度受平台影响:云端构建速度可能不如本地快速
两种方案对比
| 特性 | Git Hooks | Gitee Actions | GitHub Actions |
|---|---|---|---|
| 构建位置 | 本地电脑 | 云端服务器 | 云端服务器 |
| 配置复杂度 | 低 | 中 | 中 |
| 多人协作成本 | 高(每人需配置) | 低(仅需配置一次) | 低(仅需配置一次) |
| 安全性 | 高(密钥本地管理) | 中(密钥存储在平台) | 中(密钥存储在平台) |
| 依赖外部服务 | 无 | 是(依赖 Gitee 平台) | 是(依赖 GitHub 平台) |
| 构建速度 | 快(本地执行) | 中(云端执行) | 中(云端执行) |
| 适用场景 | 个人项目、小型团队、对安全性要求高 | 团队项目、需要统一构建环境 | 团队项目、需要统一构建环境 |
如何选择适合的方案
选择 Git Hooks 的场景
- 个人项目:无需考虑多人协作,配置简单
- 电脑固定:如果你的开发环境固定,无需在多台设备间切换,Git Hooks 是理想选择
- 对安全性要求高:不想将密钥存储在第三方平台
- 网络环境好:本地网络上传速度快
- 团队规模小:团队成员较少,配置成本可控
- 快速迭代:需要频繁部署,本地构建速度快
- 简单项目:对于简单的个人网站项目,没必要把简单问题复杂化
选择 Gitee Actions/GitHub Actions 的场景
- 团队协作:多人开发,需要统一的部署流程
- 开源项目:需要保证所有贡献者的部署结果一致
- 多设备开发:在不同设备上开发,无需重复配置
- 环境一致性:避免因本地环境差异导致的构建问题
- 高级功能需求:需要多环境部署、测试集成等功能
- 时间成本敏感:不想占用本地时间进行构建和部署
差异性总结
| 场景 | 推荐方案 | 优势 |
|---|---|---|
| 个人项目,电脑固定 | Git Hooks | 配置简单,无需依赖第三方服务,安全性高 |
| 开源项目,多台设备 | Gitee Actions/GitHub Actions | 保证部署结果一致,无需在多设备上重复配置 |
| 小型团队 | 两者均可 | 根据团队具体需求选择 |
| 大型团队 | Gitee Actions/GitHub Actions | 集中管理,环境统一,易于扩展 |
与 Jenkins 的对比
| 特性 | Git Hooks | Gitee Actions | GitHub Actions | Jenkins |
|---|---|---|---|---|
| 部署位置 | 本地 | 云端 | 云端 | 服务器(需自己搭建) |
| 配置复杂度 | 低(仅需编写简单脚本) | 中(YAML 配置) | 中(YAML 配置) | 高(需要学习 Jenkinsfile 和插件配置) |
| 维护成本 | 低(无额外维护) | 低(平台维护) | 低(平台维护) | 高(需要维护服务器、插件和配置) |
| 集成能力 | 有限(主要依赖脚本) | 中等(支持主流工具集成) | 中等(支持主流工具集成) | 强大(丰富的插件生态,支持各种工具集成) |
| 适用规模 | 个人/小团队 | 小/中/大团队 | 小/中/大团队 | 中/大团队(企业级应用) |
| 依赖外部服务 | 无 | 是(依赖 Gitee 平台) | 是(依赖 GitHub 平台) | 是(需自己搭建服务器和维护) |
| 学习曲线 | 低(基本 shell 脚本知识) | 中(YAML 语法和 CI/CD 概念) | 中(YAML 语法和 CI/CD 概念) | 高(Jenkins 生态和插件系统) |
| 定制性 | 中等(通过脚本定制) | 中等(通过工作流配置) | 中等(通过工作流配置) | 极高(通过插件和自定义脚本) |
| 扩展性 | 有限 | 中等 | 中等 | 极高(支持复杂的 CI/CD 流程) |
| 安全性 | 高(密钥本地管理) | 中(密钥存储在平台) | 中(密钥存储在平台) | 高(可完全控制安全配置) |
有关 Jenkins 搭建个人博客的文章,有兴趣的可以了解一下:
服务器配置细节
Nginx 配置示例
1. 基本配置
server {
listen 80;
server_name example.com; # 替换为你的域名或 IP
root /var/www/third-party-services; # 部署目录
index index.html;
location / {
try_files $uri $uri/ /index.html; # 支持 SPA 路由
}
# 静态文件缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
# 日志配置
access_log /var/log/nginx/third-party-services.access.log;
error_log /var/log/nginx/third-party-services.error.log;
}
2. 支持 HTTPS 的配置
如果需要启用 HTTPS,可以使用以下配置:
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
root /var/www/third-party-services;
index index.html;
# SSL 证书配置
ssl_certificate /etc/ssl/certs/your-certificate.crt;
ssl_certificate_key /etc/ssl/private/your-certificate.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
try_files $uri $uri/ /index.html;
}
# 静态文件缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
# 日志配置
access_log /var/log/nginx/third-party-services.access.log;
error_log /var/log/nginx/third-party-services.error.log;
}
启用配置并重启 Nginx
1. 创建配置文件
# 创建配置文件
nano /etc/nginx/sites-available/third-party-services
# 复制上述配置内容到文件中,保存并退出
2. 启用配置
# 创建符号链接
ln -s /etc/nginx/sites-available/third-party-services /etc/nginx/sites-enabled/
# 测试配置是否正确
nginx -t
# 重启 Nginx 服务
systemctl restart nginx
# 查看 Nginx 状态
systemctl status nginx
3. 防火墙配置
如果服务器启用了防火墙,需要允许 HTTP 和 HTTPS 流量:
# 允许 HTTP 流量
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# 允许 HTTPS 流量
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 保存防火墙规则
iptables-save > /etc/iptables/rules.v4
或者使用 ufw 防火墙:
# 允许 HTTP 流量
ufw allow 80/tcp
# 允许 HTTPS 流量
ufw allow 443/tcp
# 启用防火墙
ufw enable
# 查看防火墙状态
ufw status
错误排查与解决
通用排查步骤
- 检查网络连接:确保本地与服务器之间网络连接正常
- 检查服务器状态:确保服务器运行正常,SSH 服务已启动
- 检查部署目录权限:确保部署目录存在且有写权限
- 检查 SSH 密钥配置:确保 SSH 密钥正确配置且有适当的权限
Git Hooks 常见错误
| 错误信息 | 可能原因 | 解决方法 |
|---|---|---|
Permission denied |
SSH 公钥未正确添加 | 重新添加公钥到服务器的 authorized_keys |
rsync: command not found |
rsync 未安装 | 安装 rsync(Windows 用户使用 Git Bash 或 WSL) |
Build failed |
依赖安装失败或构建错误 | 检查构建日志,确保依赖安装正确 |
bash: ./deploy.sh: Permission denied |
脚本没有执行权限 | 运行 chmod +x deploy.sh 赋予执行权限 |
fatal: cannot exec '.git/hooks/pre-push': Permission denied |
钩子脚本没有执行权限 | 运行 chmod +x .git/hooks/pre-push 赋予执行权限 |
Gitee Actions 常见错误
| 错误信息 | 可能原因 | 解决方法 |
|---|---|---|
Permission denied (publickey) |
SSH 公钥未正确添加 | 重新添加公钥到服务器的 authorized_keys |
SSH_PRIVATE_KEY is invalid |
私钥内容不完整 | 确保完整复制私钥内容,包括 -----BEGIN 和 -----END 行 |
No such file or directory |
部署目录不存在 | 在服务器上创建部署目录 |
Build failed |
依赖安装失败或构建错误 | 查看构建日志,检查依赖和构建脚本 |
Error: Invalid workflow file |
工作流文件语法错误 | 检查 YAML 语法,确保格式正确 |
Error: Secrets not found |
仓库密钥未设置 | 检查 Gitee 仓库密钥配置,确保所有必要的密钥已添加 |
查看 Gitee Actions 日志
- 进入 Gitee 仓库:打开你的 Gitee 仓库
- 进入 Actions 页面:点击顶部导航栏的「Actions」或「Gitee Actions」
- 查看运行记录:在左侧列表中选择对应的工作流,然后点击具体的运行记录
- 查看详细日志:点击各个步骤查看详细的执行日志,重点关注失败的步骤
- 分析错误信息:根据日志中的错误信息定位问题
测试 SSH 连接
在本地终端测试 SSH 连接是否正常:
# 测试普通 SSH 连接
ssh user@your-server-ip
# 测试使用特定密钥的 SSH 连接
ssh -i ~/.ssh/deploy_key user@your-server-ip
# 测试 SSH 连接并执行简单命令
ssh user@your-server-ip "echo 'Hello World'"
服务器日志检查
检查服务器的日志文件,了解部署过程中可能出现的问题:
# 查看 Nginx 错误日志
tail -f /var/log/nginx/error.log
# 查看 Nginx 访问日志
tail -f /var/log/nginx/access.log
# 查看系统日志
tail -f /var/log/syslog
# 查看 SSH 日志
tail -f /var/log/auth.log
常见问题排查流程
构建失败:
- 检查依赖是否正确安装
- 检查构建脚本是否有语法错误
- 检查环境变量是否正确设置
部署失败:
- 检查 SSH 连接是否正常
- 检查服务器上的部署目录权限
- 检查 rsync 命令是否正确执行
服务无法访问:
- 检查 Nginx 配置是否正确
- 检查防火墙是否允许 HTTP/HTTPS 流量
- 检查服务器是否正常运行
回滚策略
Git Hooks 回滚
1. 服务器版本备份
在部署脚本中添加备份步骤,每次部署前备份当前版本:
#!/bin/bash
set -e
# 日志文件
LOG_FILE=".deploy.log"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始部署" >> $LOG_FILE
trap 'echo "[$(date '+%Y-%m-%d %H:%M:%S')] 部署失败" >> $LOG_FILE' ERR
echo "===== 开始构建 ====="
npm ci
npm run build
# 服务器配置
SERVER_USER="user"
SERVER_IP="your-server-ip"
DEPLOY_PATH="/var/www/third-party-services"
# 备份当前版本
echo "===== 备份服务器上的旧版本 ====="
ssh $SERVER_USER@$SERVER_IP "if [ -d $DEPLOY_PATH ]; then mv $DEPLOY_PATH $DEPLOY_PATH-$(date +%Y%m%d-%H%M%S)-backup; fi && mkdir -p $DEPLOY_PATH"
# 创建版本目录
VERSION=$(date +%Y%m%d-%H%M%S)
DEPLOY_DIR="$DEPLOY_PATH-$VERSION"
echo "===== 上传到服务器 ====="
ssh $SERVER_USER@$SERVER_IP "mkdir -p $DEPLOY_DIR"
rsync -avz --delete --compress --exclude="*.map" --exclude="*.log" dist/ $SERVER_USER@$SERVER_IP:$DEPLOY_DIR/
# 更新符号链接
echo "===== 更新部署链接 ====="
ssh $SERVER_USER@$SERVER_IP "ln -sf $DEPLOY_DIR $DEPLOY_PATH"
# 设置文件权限
echo "===== 设置文件权限 ====="
ssh $SERVER_USER@$SERVER_IP "chown -R www-data:www-data $DEPLOY_DIR && chmod -R 755 $DEPLOY_DIR"
echo "===== 部署完成 ====="
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 部署完成" >> $LOG_FILE
2. 手动回滚
如果部署失败,可使用以下命令回滚到上一个版本:
# 在服务器上执行
# 查看所有备份版本
ls -la /var/www/ | grep third-party-services-.*-backup
# 选择一个备份版本进行回滚
ln -sf /var/www/third-party-services-20260411-123456-backup /var/www/third-party-services
# 重启 Nginx(如果需要)
systemctl restart nginx
Gitee Actions 回滚
1. 自动备份
在 workflow 文件中添加备份步骤,每次部署前备份当前版本:
- name: Backup current version
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
REMOTE_HOST: ${{ secrets.SERVER_HOST }}
REMOTE_USER: ${{ secrets.SERVER_USER }}
TARGET: ${{ secrets.SERVER_TARGET }}_backup
SOURCE: "${{ secrets.SERVER_TARGET }}/"
ARGS: "-avz"
2. 手动回滚
如果部署失败,可使用以下命令回滚到备份版本:
# 在服务器上执行
cp -r /var/www/third-party-services_backup/* /var/www/third-party-services/
# 重启 Nginx(如果需要)
systemctl restart nginx
3. 版本管理策略
为了更好地管理版本和回滚,可以在服务器上采用以下策略:
- 保留多个版本:每次部署时创建新的版本目录,保留最近3-5个版本
- 使用符号链接:通过符号链接指向当前活动版本
- 定期清理:定期清理旧的备份版本,避免占用过多空间
# 清理30天前的备份版本
find /var/www/ -name "third-party-services-*-backup" -mtime +30 -exec rm -rf {} \;
环境变量管理
Git Hooks 环境变量
在 deploy.sh 中设置环境变量:
# 加载环境变量
if [ -f .env ]; then
export $(cat .env | grep -v '#' | xargs)
fi
# 构建时使用环境变量
export VITE_API_URL="https://api.example.com"
export VITE_APP_ENV="production"
npm run build
Gitee Actions 环境变量
在 Gitee 仓库密钥中添加环境变量,然后在 workflow 文件中使用:
- name: Build project
run: |
export API_URL=${{ secrets.API_URL }}
export API_KEY=${{ secrets.API_KEY }}
export APP_ENV=${{ secrets.APP_ENV }}
pnpm run build
多环境配置
为不同环境创建不同的环境变量文件:
# .env.development
VITE_API_URL="https://dev-api.example.com"
VITE_APP_ENV="development"
# .env.production
VITE_API_URL="https://api.example.com"
VITE_APP_ENV="production"
# .env.staging
VITE_API_URL="https://staging-api.example.com"
VITE_APP_ENV="staging"
在部署脚本中根据环境选择不同的配置文件:
# 选择环境
ENVIRONMENT="production"
# 加载对应环境的配置文件
if [ -f .env.$ENVIRONMENT ]; then
export $(cat .env.$ENVIRONMENT | grep -v '#' | xargs)
fi
部署验证步骤
1. 访问验证
基本访问验证
- 直接访问:打开浏览器,访问你的网站域名或 IP
- 状态码检查:使用
curl命令检查 HTTP 状态码curl -I https://example.com # 期望返回 200 OK - 多设备访问:在不同设备(桌面、手机、平板)上访问网站,确保响应式布局正常
- 多浏览器测试:在不同浏览器(Chrome、Firefox、Safari、Edge)中测试网站
网络环境测试
- 不同网络环境:在不同网络环境(WiFi、4G、5G)下测试访问速度
- 地理位置测试:使用在线工具(如 GTmetrix、Pingdom)测试不同地理位置的访问速度
2. 功能验证
核心功能测试
- 导航测试:测试网站的所有导航链接,确保能正确跳转到对应页面
- 表单提交:测试所有表单的提交功能,确保数据能正确处理
- API 调用:测试网站的 API 调用功能,确保能正确获取和处理数据
- 用户认证:如果网站有登录功能,测试用户登录、注册和密码重置等功能
边界情况测试
- 空输入测试:测试表单提交空数据的情况
- 错误输入测试:测试输入无效数据的情况,确保有适当的错误提示
- 异常流程测试:测试各种异常情况下的网站行为
3. 性能验证
加载性能测试
- 浏览器开发者工具:使用 Chrome DevTools 的 Performance 面板分析页面加载性能
- Lighthouse 测试:使用 Chrome DevTools 的 Lighthouse 工具进行性能审计
# 使用命令行工具运行 Lighthouse npx lighthouse https://example.com --output=html --output-path=./lighthouse-report.html - PageSpeed Insights:使用 Google PageSpeed Insights 测试网站性能
资源优化验证
- 资源大小:检查页面资源的大小,确保没有过大的文件
- 资源加载顺序:检查资源的加载顺序,确保关键资源优先加载
- 缓存策略:验证静态资源的缓存策略是否正确配置
响应时间测试
- 首屏加载时间:测量首屏内容的加载时间
- 交互响应时间:测量用户交互到页面响应的时间
- API 响应时间:测量 API 请求的响应时间
4. 日志验证
服务器日志检查
- Nginx 错误日志:检查 Nginx 的错误日志,确保没有错误信息
tail -f /var/log/nginx/error.log - Nginx 访问日志:检查 Nginx 的访问日志,确认请求正常
tail -f /var/log/nginx/access.log - 系统日志:检查系统日志,确保服务器运行正常
tail -f /var/log/syslog
应用日志检查
- 前端日志:在浏览器开发者工具的 Console 面板中检查前端日志,确保没有 JavaScript 错误
- API 日志:如果网站有后端 API,检查 API 服务器的日志
5. 安全验证
基本安全检查
- HTTPS 配置:确认网站使用 HTTPS,且证书有效
- CORS 配置:验证 CORS 配置是否正确
- 安全头:检查网站是否设置了适当的安全头
curl -I https://example.com | grep -i "x-frame-options\|x-xss-protection\|content-security-policy"
漏洞扫描
- 使用在线工具:使用在线安全扫描工具(如 Sucuri SiteCheck)扫描网站漏洞
- 手动检查:检查网站是否存在常见的安全漏洞
6. 自动化验证
集成测试
- 使用测试框架:使用 Jest、Cypress 等测试框架编写自动化测试
- CI/CD 集成:在 CI/CD 流程中集成自动化测试
- 测试覆盖率:在 CI/CD 中集成测试覆盖率报告,确保代码质量
Git Hooks 测试集成
# 在 pre-push 中添加测试
echo "🧪 Running tests..."
npm test
if [ $? -ne 0 ]; then
echo "❌ Tests failed, deployment aborted."
exit 1
fi
# 运行构建测试
echo "🔨 Running build test..."
npm run build
if [ $? -ne 0 ]; then
echo "❌ Build failed, deployment aborted."
exit 1
fi
Gitee Actions 测试集成
- name: Run tests
run: pnpm test
- name: Run build
run: pnpm run build
- name: Run end-to-end tests
run: pnpm run e2e
- name: Run tests with coverage
run: pnpm test --coverage
- name: Upload coverage report
uses: actions/upload-artifact@v3
with:
name: coverage-report
path: coverage/
监控与通知
- 设置监控:使用监控工具(如 New Relic、Datadog)监控网站的运行状态
- 设置告警:配置告警机制,当网站出现问题时及时通知
- 部署通知:在部署完成后发送通知,包括成功和失败的情况
Git Hooks 部署通知
# 部署成功后发送通知
if [ $? -eq 0 ]; then
curl -X POST -H "Content-Type: application/json" \
-d '{"text": "🚀 项目部署成功!"}' \
https://hooks.slack.com/services/YOUR_WEBHOOK_URL
# 发送邮件通知
echo "部署成功,详情请查看:https://example.com" | mail -s "项目部署成功" user@example.com
fi
# 部署失败后发送通知
if [ $? -ne 0 ]; then
curl -X POST -H "Content-Type: application/json" \
-d '{"text": "❌ 项目部署失败!"}' \
https://hooks.slack.com/services/YOUR_WEBHOOK_URL
fi
Gitee Actions 部署通知
- name: Send success notification
if: success()
run: |
curl -X POST -H "Content-Type: application/json" \
-d '{"text": "🚀 项目部署成功!"}' \
https://hooks.slack.com/services/YOUR_WEBHOOK_URL
- name: Send failure notification
if: failure()
run: |
curl -X POST -H "Content-Type: application/json" \
-d '{"text": "❌ 项目部署失败!"}' \
https://hooks.slack.com/services/YOUR_WEBHOOK_URL
其他通知方式
- 钉钉通知:使用钉钉机器人发送部署通知
- 企业微信通知:使用企业微信机器人发送部署通知
验证报告
每次部署后,建议生成验证报告,记录验证结果和发现的问题:
# 部署验证报告
## 部署信息
- 部署时间:2026-04-11 12:00:00
- 部署版本:v1.0.1
- 部署环境:生产环境
## 验证结果
### 1. 访问验证
- [x] 直接访问正常
- [x] HTTP 状态码 200
- [x] 多设备访问正常
- [x] 多浏览器访问正常
### 2. 功能验证
- [x] 导航功能正常
- [x] 表单提交正常
- [x] API 调用正常
- [x] 用户认证功能正常
### 3. 性能验证
- [x] 首屏加载时间:1.2s
- [x] Lighthouse 性能评分:90/100
- [x] 资源加载正常
### 4. 日志验证
- [x] Nginx 错误日志无异常
- [x] Nginx 访问日志正常
- [x] 前端控制台无错误
### 5. 安全验证
- [x] HTTPS 配置正确
- [x] 安全头设置正确
- [x] 无明显安全漏洞
## 问题与解决方案
### 发现的问题
1. 首页图片加载较慢
2. 部分 API 响应时间较长
### 解决方案
1. 优化图片大小,使用 WebP 格式
2. 优化 API 查询,增加缓存
## 结论
部署成功,网站运行正常,无严重问题。
项目结构说明
构建输出目录
不同构建工具的默认输出目录:
- Vite:默认
dist/目录 - Create React App:默认
build/目录 - Next.js:默认
.next/目录(静态导出为out/目录) - Vue CLI:默认
dist/目录 - Angular CLI:默认
dist/目录 - SvelteKit:默认
build/目录(静态导出)
Vite 配置示例
基本配置
// vite.config.ts
import { defineConfig } from "vite";
import path from "path";
export default defineConfig({
build: {
outDir: "dist",
minify: "terser",
sourcemap: false, // 生产环境不生成 sourcemap
rollupOptions: {
output: {
manualChunks: {
vendor: ["react", "react-dom"],
utils: ["lodash"],
},
},
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
});
自定义输出目录
如果需要自定义构建输出目录,可以修改 outDir 配置:
// vite.config.ts
import { defineConfig } from "vite";
import path from "path";
export default defineConfig({
build: {
outDir: path.resolve(__dirname, "dist/public"),
// 其他配置...
},
});
Create React App 配置
Create React App 的构建配置通过 package.json 文件中的 build 脚本配置:
{
"scripts": {
"build": "react-scripts build"
}
}
默认情况下,Create React App 会将构建产物输出到 build/ 目录。
Next.js 配置
静态导出配置
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
distDir: "out",
// 其他配置...
};
module.exports = nextConfig;
服务器端渲染配置
对于服务器端渲染的 Next.js 应用,需要使用 next start 命令启动服务器,部署方式与静态网站不同。
配置示例
根据项目结构调整部署脚本:
# Vite 项目
rsync -avz --delete dist/ user@your-server-ip:/var/www/third-party-services/
# Create React App 项目
rsync -avz --delete build/ user@your-server-ip:/var/www/third-party-services/
# Next.js 静态导出项目
rsync -avz --delete out/ user@your-server-ip:/var/www/third-party-services/
# Vue CLI 项目
rsync -avz --delete dist/ user@your-server-ip:/var/www/third-party-services/
# Angular CLI 项目
rsync -avz --delete dist/ user@your-server-ip:/var/www/third-party-services/
# SvelteKit 静态导出项目
rsync -avz --delete build/ user@your-server-ip:/var/www/third-party-services/
项目结构最佳实践
- 统一构建输出目录:尽量使用默认的构建输出目录,便于部署脚本的统一管理
- 分离源码和构建产物:源码放在
src/目录,构建产物放在dist/或build/目录 - 使用版本控制忽略构建产物:在
.gitignore文件中添加构建输出目录,避免将构建产物提交到版本控制系统# .gitignore node_modules/ dist/ build/ out/ .next/ - 配置文件分离:将部署配置与应用配置分离,便于管理和维护
扩展功能
1. 多服务器部署方案
Git Hooks 多服务器部署
#!/bin/bash
set -e
# 日志文件
LOG_FILE=".deploy.log"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始部署" >> $LOG_FILE
trap 'echo "[$(date '+%Y-%m-%d %H:%M:%S')] 部署失败" >> $LOG_FILE' ERR
echo "===== 开始构建 ====="
npm ci
npm run build
# 服务器配置
SERVER_USER="user"
DEPLOY_PATH="/var/www/third-party-services"
# 多服务器列表
SERVERS=(
"server1.example.com"
"server2.example.com"
"server3.example.com"
)
# 创建版本目录
VERSION=$(date +%Y%m%d-%H%M%S)
# 部署到多个服务器
for server in "${SERVERS[@]}"; do
echo "===== 部署到 $server ====="
# 创建部署目录
DEPLOY_DIR="$DEPLOY_PATH-$VERSION"
ssh $SERVER_USER@$server "mkdir -p $DEPLOY_DIR"
# 上传文件
rsync -avz --delete --compress --exclude="*.map" --exclude="*.log" dist/ $SERVER_USER@$server:$DEPLOY_DIR/
# 更新符号链接
ssh $SERVER_USER@$server "ln -sf $DEPLOY_DIR $DEPLOY_PATH"
# 设置文件权限
ssh $SERVER_USER@$server "chown -R www-data:www-data $DEPLOY_DIR && chmod -R 755 $DEPLOY_DIR"
# 健康检查
HEALTH_CHECK=$(ssh $SERVER_USER@$server "curl -s -o /dev/null -w '%{http_code}' http://localhost")
if [ "$HEALTH_CHECK" = "200" ]; then
echo "✅ $server 服务正常"
else
echo "❌ $server 服务异常,HTTP 状态码: $HEALTH_CHECK"
fi
done
echo "===== 部署完成 ====="
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 部署完成" >> $LOG_FILE
Gitee Actions 多服务器部署
name: Deploy to Multiple Servers
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install pnpm
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Build project
run: pnpm run build
# 部署到服务器 1
- name: Deploy to server 1
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
REMOTE_HOST: ${{ secrets.SERVER_HOST_1 }}
REMOTE_USER: ${{ secrets.SERVER_USER }}
TARGET: ${{ secrets.SERVER_TARGET }}
SOURCE: "dist/"
ARGS: "-avz --delete"
# 部署到服务器 2
- name: Deploy to server 2
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
REMOTE_HOST: ${{ secrets.SERVER_HOST_2 }}
REMOTE_USER: ${{ secrets.SERVER_USER }}
TARGET: ${{ secrets.SERVER_TARGET }}
SOURCE: "dist/"
ARGS: "-avz --delete"
# 部署到服务器 3
- name: Deploy to server 3
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
REMOTE_HOST: ${{ secrets.SERVER_HOST_3 }}
REMOTE_USER: ${{ secrets.SERVER_USER }}
TARGET: ${{ secrets.SERVER_TARGET }}
SOURCE: "dist/"
ARGS: "-avz --delete"
最佳实践
Git Hooks 最佳实践
- 使用配置文件:将服务器信息存储在单独的配置文件中,便于管理
- 添加错误处理:在脚本中添加错误处理和日志记录
- 分支判断:只在特定分支(如 main)执行部署
- 权限设置:确保服务器上的文件权限正确设置
- 健康检查:部署后检查服务是否正常运行
Gitee Actions 最佳实践
- 使用缓存:缓存依赖,提高构建速度
- 多环境部署:配置不同分支对应不同环境
- 添加测试:在部署前运行测试,确保代码质量
- 备份策略:部署前备份当前版本,便于回滚
- 通知机制:添加部署成功/失败的通知
- 环境变量管理:使用 Gitee 仓库密钥管理敏感信息
部署优化建议
1. 构建优化
依赖管理优化
- 使用
npm ci或pnpm install --frozen-lockfile:确保依赖版本一致,避免因依赖差异导致的构建问题 - 使用 pnpm 作为包管理器:pnpm 比 npm 更快,占用空间更小
- 清理未使用的依赖:定期运行
npm prune或pnpm prune清理未使用的依赖
构建配置优化
- 启用构建缓存:在 Gitee Actions 中使用缓存,减少重复构建时间
- name: Cache dependencies uses: actions/cache@v3 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node- - 优化构建输出:
- 使用代码分割(Code Splitting)减少初始加载时间
- 启用树摇(Tree Shaking)移除未使用的代码
- 配置适当的压缩选项
构建工具配置
Vite 配置优化:
// vite.config.ts import { defineConfig } from "vite"; export default defineConfig({ build: { minify: "terser", terserOptions: { compress: { drop_console: true, drop_debugger: true, }, }, rollupOptions: { output: { manualChunks: { vendor: ["react", "react-dom"], utils: ["lodash"], }, }, }, }, });
2. 部署优化
传输优化
- 使用
rsync的优化选项:rsync -avz --delete --compress --exclude="*.map" --exclude="*.log" --exclude="node_modules" dist/ user@your-server-ip:/var/www/third-party-services/ - 增量部署:只传输更改的文件,减少传输时间
- 使用并行传输:对于大型项目,可以考虑使用并行传输工具
部署策略优化
- 蓝绿部署:在不中断服务的情况下进行部署
- 滚动部署:逐步更新服务器,降低风险
- 金丝雀部署:先向部分用户推出新功能,验证无误后再全面部署
3. 缓存配置
浏览器缓存
- Nginx 静态文件缓存配置:
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 30d; add_header Cache-Control "public, max-age=2592000, immutable"; add_header Last-Modified "$date_gmt"; add_header ETag "$etag"; }
CDN 缓存
- 使用 CDN:将静态资源部署到 CDN,加速全球访问
- CDN 缓存策略:
- 为不同类型的资源设置不同的缓存时间
- 使用版本化的资源 URL,确保缓存能够正确失效
构建缓存
启用 Vite 构建缓存:
// vite.config.ts import { defineConfig } from "vite"; export default defineConfig({ cacheDir: ".vite-cache", // 其他配置... });
4. 安全性优化
SSH 密钥管理
- 使用 Ed25519 算法生成 SSH 密钥:安全性更高,推荐使用
ssh-keygen -t ed25519 -C "deploy-key" - 定期轮换 SSH 密钥:建议每 3-6 个月更换一次 SSH 密钥,提高安全性
- 为不同用途生成不同密钥:为不同的部署环境(开发、测试、生产)生成不同的密钥对
- 密钥文件权限设置:确保私钥文件权限为 600,公钥文件权限为 644
chmod 600 ~/.ssh/id_ed25519 chmod 644 ~/.ssh/id_ed25519.pub
服务器权限设置
遵循最小权限原则:限制部署用户的权限,只授予必要的访问权限
创建专门的部署用户:创建一个专门用于部署的用户,避免使用 root 用户
# 创建部署用户 useradd -m deploy # 为部署用户设置密码(可选) passwd deploy # 授予部署目录的写权限 chown -R deploy:deploy /var/www/third-party-services禁用密码登录:在服务器的
sshd_config中设置PasswordAuthentication no,提高服务器安全性# 编辑 SSH 配置文件 nano /etc/ssh/sshd_config # 修改以下配置 PasswordAuthentication no PermitRootLogin no # 重启 SSH 服务 systemctl restart sshdIP 限制:在服务器的防火墙中限制 SSH 登录的 IP 地址,只允许特定 IP 访问
# 使用 iptables 限制 SSH 访问 iptables -A INPUT -p tcp --dport 22 -s your-ip-address -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j DROP # 保存规则 iptables-save > /etc/iptables/rules.v4
环境变量安全
- 不在代码中硬编码敏感信息:使用环境变量或配置文件存储敏感信息
- 加密存储环境变量:对于 Gitee Actions,使用仓库密钥存储敏感信息
- 定期更新敏感信息:定期更新 API 密钥、密码等敏感信息
服务器安全加固
- 定期更新系统:定期运行
apt update && apt upgrade(Ubuntu/Debian)或yum update(CentOS/RHEL)更新系统 - 安装安全软件:安装防火墙、入侵检测系统等安全软件
- 禁用不必要的服务:关闭不需要的服务,减少攻击面
- 定期备份:定期备份服务器数据,确保数据安全
5. 网络优化
- 使用 CDN:加速静态资源访问,提高网站加载速度
- 启用 HTTP/2:提高传输效率,减少延迟
- 配置 Gzip 压缩:减小传输文件大小,提高传输速度
gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_comp_level 6; gzip_buffers 16 8k; gzip_min_length 256; gzip_proxied any; gzip_vary on; - 启用 Brotli 压缩:比 Gzip 压缩率更高
brotli on; brotli_comp_level 6; brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
6. 服务器优化
- 创建专门的部署用户:仅授予部署目录的读写权限,提高安全性
- 定期检查文件权限:确保敏感文件的权限设置正确
- 配置防火墙:限制 SSH 登录的 IP 地址,提高服务器安全性
- 使用 SSD 存储:提高文件读写速度
- 优化服务器内存和 CPU:根据网站流量合理配置服务器资源
实际应用案例
案例一:个人网站项目
对于个人网站项目,Git Hooks 是一个理想的选择。开发者可以在本地完成构建和部署,无需依赖第三方服务,同时保证了部署的安全性。通过简单的脚本配置,每次推送代码时自动更新网站内容。
案例二:团队协作项目
对于团队协作的项目,Gitee Actions 更为适合。团队成员只需专注于代码开发,推送代码后由云端自动处理构建和部署,确保了环境的一致性和部署的可靠性。同时,通过配置多环境部署,可以实现开发、测试、生产环境的自动切换。
总结
Git Hooks 和 Gitee Actions 是两种各有优势的自动化部署方案,选择哪种方案取决于项目的具体需求和团队的实际情况。
- 个人项目或小型团队:推荐使用 Git Hooks,配置简单,安全性高
- 团队协作或需要统一环境:推荐使用 Gitee Actions,便于集中管理和扩展
适用范围说明
本文介绍的自动化部署方案不仅适用于个人网站项目,也适用于各种前端项目,包括但不限于:
- React 项目
- Vue 项目
- Angular 项目
- 静态网站生成器(如 Next.js、Gatsby、Hexo 等)
- 任何需要构建和部署的前端项目
无论选择哪种方案,自动化部署都能显著提高开发效率,减少手动操作的错误,让开发者更加专注于代码本身。通过本文的介绍,希望能帮助你找到适合自己项目的部署方案,实现更高效的前端开发流程。