Nginx-rsync简单自动化脚本

一、引言:为什么你需要一个"简单"的自动化脚本?

在 Nginx 运维的日常中,我们每天都在重复这样的操作:构建前端资源、测试配置、推送到服务器、重载 Nginx。当这些步骤还停留在手动敲命令的阶段时,不仅效率低下,更埋下了人为失误的隐患------路径拼错、权限遗漏、忘记重载,任何一个疏忽都可能导致线上故障。

很多教程直接抛出复杂的 CI/CD 流水线或 Ansible Playbook,但对于中小项目、个人站点或快速验证场景,这些方案显得过于沉重。你真正需要的,是一个足够简单、开箱即用、又能覆盖核心安全边界的 Shell 脚本

本文将带你从零构建一个 Nginx-rsync 自动化部署脚本,从最基础的单行命令逐步演进为具备参数校验、日志记录、错误处理和安全防护的生产级工具。所有代码均可直接复制使用,无需额外依赖。


二、V1.0:最小可用版本(30秒上手)

如果你只是想快速摆脱手动输入,这个版本已经够用:

bash 复制代码
#!/bin/bash
# deploy.sh - 最简版 Nginx rsync 部署脚本

SRC="/data/build/dist/"
DEST="deploy@192.168.1.100:/var/www/html/"

rsync -avz --delete "$SRC" "$DEST"
ssh deploy@192.168.1.100 "sudo nginx -t && sudo systemctl reload nginx"
⚠️ V1.0 的致命缺陷
  • 无错误处理:rsync 失败后仍会执行 nginx reload,可能用旧配置重启服务
  • 无路径校验 :源目录不存在时静默同步空内容,目标被 --delete 清空
  • 硬编码:换环境需改脚本源码,易出错
  • 无日志:出问题后无法追溯

📌 定位 :仅适合本地开发环境的临时调试,严禁直接用于生产


三、V2.0:生产级自动化脚本(推荐直接使用)

以下是经过实战打磨的完整脚本,兼顾简洁性与可靠性:

bash 复制代码
#!/bin/bash
#================================================================
# Nginx-rsync 自动化部署脚本 v2.0
# 用法: ./deploy.sh [dev|prod]
#================================================================
set -euo pipefail

# ==================== 配置区 ====================
declare -A CONFIG=(
    [dev_src]="/data/build/dist/"
    [dev_dest]="deploy@192.168.1.100:/var/www/html/"
    [dev_nginx_user]="www-data"
    
    [prod_src]="/data/build/dist/"
    [prod_dest]="deploy@10.0.1.50:/var/www/html/"
    [prod_nginx_user]="nginx"
)

SSH_KEY="$HOME/.ssh/nginx_deploy_key"
LOG_FILE="/var/log/nginx_deploy.log"
EXCLUDE_FILE=".rsync-exclude"
# ================================================

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
die() { log "❌ FATAL: $*"; exit 1; }

# ---------- 参数校验 ----------
ENV="${1:-}"
[[ -z "$ENV" ]] && die "用法: $0 <dev|prod>"
[[ ! "${CONFIG[${ENV}_src]+_}" ]] && die "未知环境: $ENV (可选: dev, prod)"

SRC="${CONFIG[${ENV}_src]}"
DEST="${CONFIG[${ENV}_dest]}"
NGINX_USER="${CONFIG[${ENV}_nginx_user]}"

# ---------- 前置检查 ----------
[[ ! -d "$SRC" ]] && die "源目录不存在: $SRC"
[[ ! -f "$SSH_KEY" ]] && die "SSH密钥不存在: $SSH_KEY"
[[ ! -f "$EXCLUDE_FILE" ]] && log "⚠️ 排除文件不存在: $EXCLUDE_FILE (将同步全部文件)"

# ---------- 核心同步 ----------
log "🔄 开始部署 [$ENV]: $SRC → $DEST"

RSYNC_OPTS=(-avz --progress --chown="$NGINX_USER:$NGINX_USER")
[[ -f "$EXCLUDE_FILE" ]] && RSYNC_OPTS+=(--exclude-from="$EXCLUDE_FILE")

# 注意:仅在确认目标目录专用于本次同步时才加 --delete
# RSYNC_OPTS+=(--delete)

if ! rsync "${RSYNC_OPTS[@]}" \
    -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new -o BatchMode=yes" \
    "$SRC" "$DEST"; then
    die "rsync 同步失败,终止部署"
fi

log "✅ 文件同步完成"

# ---------- Nginx 重载 ----------
REMOTE_HOST="${DEST%%:*}"
log "🔧 验证并重载 Nginx..."

if ! ssh -i "$SSH_KEY" -o BatchMode=yes "$REMOTE_HOST" \
    "sudo nginx -t 2>&1 && sudo systemctl reload nginx"; then
    die "Nginx 配置验证失败或重载异常,请检查远程日志"
fi

log "🎉 [$ENV] 部署成功!"
核心设计解析
特性 实现方式 解决的问题
多环境支持 declare -A 关联数组 + 命令行参数 避免维护多个脚本副本
严格错误处理 set -euo pipefail + 显式 die() 任何环节失败立即终止,防止脏状态
SSH 安全 BatchMode=yes + 专用密钥 杜绝交互式密码提示,自动化不卡死
权限自动修正 --chown 参数 消除部署后 403 Forbidden
可观测性 带时间戳的结构化日志 故障追溯有据可查
安全防护 默认注释 --delete 防止误删非托管文件

四、关键细节深度拆解

1. 为什么必须用 BatchMode=yes

在自动化脚本中,SSH 连接绝对不能 出现任何交互式提示。BatchMode=yes 确保:

  • 密码/密钥短语缺失时直接报错退出,而非挂起等待输入
  • 主机指纹变更时拒绝连接,而非弹出确认对话框

这是区分"能跑的脚本"和"能在 Cron/CI 中稳定运行的脚本"的关键分水岭。

2. --delete 的安全使用策略

脚本中默认注释了 --delete,这是刻意的设计。启用前请完成以下检查清单:

  • 目标目录是否包含本次同步管理的文件?
  • 是否有用户上传内容、SSL 证书、日志等非同步文件?
  • 是否已先用 --dry-run 验证过删除列表?
  • 是否在 .rsync-exclude 中保护了关键路径?

若答案有任何一项为"否",请勿启用 --delete。增量同步(默认行为)对大多数 Nginx 场景已足够安全。

3. 排除文件的最佳实践

创建 .rsync-exclude 并纳入版本管理:

复制代码
# .rsync-exclude
.git/
node_modules/
*.log
*.tmp
.env*
.DS_Store
uploads/
*.pem
*.key

通过 --exclude-from 引用,比在命令行堆砌多个 --exclude 更易维护和审查。

4. 日志轮转

长期运行的部署脚本必须配合日志轮转,避免磁盘写满:

bash 复制代码
# /etc/logrotate.d/nginx-deploy
/var/log/nginx_deploy.log {
    daily
    rotate 30
    compress
    missingok
    notifempty
}

五、进阶扩展方向

当基础脚本稳定运行后,可按需叠加以下能力:

  • 原子部署 :同步到临时目录 → mv 替换 → 回滚机制
  • 多节点扇出:循环遍历服务器列表,并行或串行推送
  • 健康检查:reload 后 curl 验证关键接口返回 200
  • 通知集成:成功/失败时发送钉钉/企微/邮件告警
  • 版本标记 :在目标目录写入 .deploy_version 文件,便于排查

📌 原则:先让 V2.0 稳定运行至少一个月,再考虑扩展。过早优化是自动化脚本腐化的常见原因。


六、常见踩坑速查表

现象 根因 解决方案
脚本卡在密码提示 未配置免密或未加 BatchMode 配置 SSH 密钥 + 添加 -o BatchMode=yes
部署后 Nginx 403 文件所有者不正确 添加 --chown=nginx_user:nginx_user
目标目录结构多一层 源路径末尾缺少 / 确保 $SRC/ 结尾
--delete 清空了上传文件 目标目录混用了非托管内容 移除 --delete 或使用 exclude 保护
Cron 中执行失败但手动正常 环境变量/PATH 差异 脚本中使用绝对路径,Cron 中 source 环境文件
inotify watch 耗尽(实时场景) 默认上限过低 sysctl fs.inotify.max_user_watches=524288

七、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!