一、为什么要用Shell脚本管服务器?
想象一下,你手头有十台、甚至上百台服务器需要维护。每台服务器上,你可能都需要检查磁盘空间、更新某个软件、或者修改一个配置文件。如果一台一台登录上去操作,不仅耗时费力,还容易因为手误而出错。这种感觉,就像是用一把小勺子给一个巨大的游泳池换水。
这时候,Shell脚本就派上大用场了。它就像是你预先写好的“操作清单”,可以自动、批量地在多台服务器上执行相同的任务。你只需要写好一次脚本,然后让它去“跑腿”,就能同时管理成百上千台机器,效率提升不是一点半点。今天,我们就来聊聊怎么用Shell脚本这把“瑞士军刀”,来高效地管理多台服务器的配置。
二、准备工作:打通服务器间的“信任通道”
要让脚本能在一台机器上指挥其他机器干活,首先得解决身份认证问题。总不能每次执行命令都手动输入密码吧?所以,我们首先要配置SSH免密登录。
这背后的原理是使用SSH密钥对。你在管理机(我们称它为“控制机”)上生成一对钥匙:一把私钥(自己保管好,绝不外传),一把公钥(可以放心地放到你想管理的服务器上)。当控制机试图连接服务器时,服务器会用你留下的公钥来“出题”,只有拥有对应私钥的控制机才能“答对”,从而无需密码直接登录。
技术栈声明:本文所有示例均基于 Linux/Unix 环境下的 Bash Shell。
示例1:配置SSH免密登录
#!/bin/bash
# 文件名:setup_ssh_keys.sh
# 功能:批量配置从本机到目标服务器的SSH免密登录
# 1. 在本机(控制机)生成SSH密钥对(如果尚未生成)
# -t 指定密钥类型为rsa,-N 设置空密码,-f 指定密钥文件路径
ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa_batch_admin <<< y 2>&1 >/dev/null
# 2. 定义需要管理的服务器IP地址或主机名列表
SERVER_LIST=("192.168.1.101" "192.168.1.102" "server03.example.com")
# 3. 定义远程服务器的登录用户名
REMOTE_USER="admin"
# 4. 循环遍历服务器列表,将公钥上传到每一台服务器
for SERVER in "${SERVER_LIST[@]}"
do
echo "正在处理服务器: $SERVER ..."
# 使用ssh-copy-id工具将公钥复制到远程服务器的authorized_keys文件中
# -i 指定公钥文件,-f 强制模式(不检查是否已存在)
ssh-copy-id -i ~/.ssh/id_rsa_batch_admin.pub -f $REMOTE_USER@$SERVER &> /dev/null
# 检查上一条命令是否执行成功
if [ $? -eq 0 ]; then
echo " [成功] SSH公钥已部署至 $SERVER"
else
echo " [失败] 无法连接或部署到 $SERVER,请检查网络和认证信息。"
fi
done
echo "SSH免密登录配置完成!"
运行这个脚本后,你的控制机就可以无密码登录 SERVER_LIST 里指定的那些服务器了。这是所有后续批量操作的基础。
三、核心武器:编写批量管理脚本
有了免密登录的通道,我们就可以编写核心的批量管理脚本了。这里的关键是使用 ssh 命令在远程执行指令,并结合循环。
示例2:批量获取服务器基础信息 让我们先写一个脚本来收集所有服务器的基本信息,比如主机名、操作系统版本、CPU核心数和内存大小。这就像是一次“普查”。
#!/bin/bash
# 文件名:batch_collect_info.sh
# 功能:批量收集多台服务器的系统信息并输出报告
SERVER_LIST=("192.168.1.101" "192.168.1.102")
REMOTE_USER="admin"
REPORT_FILE="server_report_$(date +%Y%m%d).txt" # 报告文件以日期命名
echo "========== 服务器批量巡检报告 ==========" > $REPORT_FILE
echo "生成时间:$(date)" >> $REPORT_FILE
echo "========================================" >> $REPORT_FILE
for SERVER in "${SERVER_LIST[@]}"
do
echo "正在巡检服务器: $SERVER" | tee -a $REPORT_FILE # tee命令同时输出到屏幕和文件
# 通过SSH在远程服务器执行一系列命令,并用分号隔开
# 命令输出通过管道传回本地,赋值给变量
INFO=$(ssh $REMOTE_USER@$SERVER "
echo ' 主机名:' \$(hostname);
echo ' 系统版本:' \$(cat /etc/os-release | grep PRETTY_NAME | cut -d'\"' -f2);
echo ' CPU核心数:' \$(grep -c '^processor' /proc/cpuinfo);
echo ' 内存总量(MB):' \$(free -m | awk '/^Mem:/ {print \$2}');
echo ' 磁盘根分区使用率:' \$(df -h / | awk 'NR==2 {print \$5}');
")
# 将获取到的信息追加到报告文件
echo "$INFO" >> $REPORT_FILE
echo "----------------------------------------" >> $REPORT_FILE
done
echo "巡检完成!详细报告请查看:$REPORT_FILE"
这个脚本展示了远程执行命令的基本范式:ssh user@host “command1; command2; ...”。你可以把任何想在单台服务器上执行的命令,放进这个引号里。
示例3:批量更新应用配置文件
更常见的场景是统一修改配置。假设我们需要在所有服务器的Nginx配置中,将worker_processes 改为 auto,并重启服务。
#!/bin/bash
# 文件名:batch_update_nginx.sh
# 功能:批量更新多台服务器上的Nginx配置并重启服务
SERVER_LIST=("web01.example.com" "web02.example.com")
REMOTE_USER="deploy"
CONFIG_FILE="/etc/nginx/nginx.conf"
BACKUP_DIR="/etc/nginx/backup/"
for SERVER in "${SERVER_LIST[@]}"
do
echo "开始处理服务器: $SERVER"
# 第一步:备份原始配置文件
ssh $REMOTE_USER@$SERVER "sudo mkdir -p $BACKUP_DIR && sudo cp $CONFIG_FILE ${BACKUP_DIR}nginx.conf.backup.$(date +%s)"
# 第二步:使用sed命令在线修改配置。这里将worker_processes后面的数字改为auto。
# -i 表示直接修改文件,s/原内容/新内容/ 是替换语法。
ssh $REMOTE_USER@$SERVER "sudo sed -i 's/^worker_processes.*/worker_processes auto;/' $CONFIG_FILE"
# 第三步:检查配置文件语法是否正确
CHECK_RESULT=$(ssh $REMOTE_USER@$SERVER "sudo nginx -t 2>&1")
if echo "$CHECK_RESULT" | grep -q "syntax is ok"; then
echo " [通过] 配置文件语法检查"
# 第四步:优雅重启Nginx服务
ssh $REMOTE_USER@$SERVER "sudo systemctl reload nginx"
if [ $? -eq 0 ]; then
echo " [成功] Nginx服务已重新加载"
else
echo " [警告] Nginx重新加载失败,尝试重启..."
ssh $REMOTE_USER@$SERVER "sudo systemctl restart nginx"
fi
else
echo " [严重错误] 配置文件语法检查失败:"
echo " $CHECK_RESULT"
echo " 正在从备份恢复..."
ssh $REMOTE_USER@$SERVER "sudo cp ${BACKUP_DIR}nginx.conf.backup.* $CONFIG_FILE"
fi
echo "处理完成:$SERVER"
echo "---"
done
这个脚本体现了更严谨的运维思路:先备份、再修改、然后测试、最后才应用。一旦测试失败,还能自动回滚到备份版本,安全性大大提升。
四、进阶技巧:让脚本更健壮、更灵活
基础的循环和SSH可以解决很多问题,但想写出更专业的脚本,还需要一些技巧。
1. 使用 Here Document 传递复杂逻辑 当需要远程执行多行命令或复杂的逻辑判断时,使用 Here Document 语法会更清晰。
示例4:批量部署一个简单的监控脚本
#!/bin/bash
# 文件名:deploy_monitor_script.sh
# 功能:批量向服务器部署一个用于检查磁盘使用率的监控脚本
SERVER_LIST=("192.168.1.101" "192.168.1.102")
REMOTE_USER="admin"
REMOTE_SCRIPT_PATH="/usr/local/bin/check_disk.sh"
# 本地要部署的脚本内容,使用 Here Document 定义
LOCAL_SCRIPT_CONTENT=$(cat <<'EOF'
#!/bin/bash
# 监控脚本:检查根分区使用率,超过阈值则告警
THRESHOLD=80
USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $USAGE -gt $THRESHOLD ]; then
echo "[警报 $(date)] 根分区使用率: ${USAGE}%,超过阈值 ${THRESHOLD}%!" >> /var/log/disk_alert.log
# 这里可以添加发送邮件或通知的指令,例如:
# echo "警报内容" | mail -s "磁盘空间告警" admin@example.com
else
echo "[正常 $(date)] 根分区使用率: ${USAGE}%"
fi
EOF
)
for SERVER in "${SERVER_LIST[@]}"
do
echo "正在部署监控脚本到: $SERVER"
# 将脚本内容通过ssh和cat命令写入远程文件
# 注意:EOF两边的单引号防止本地变量被解析
ssh $REMOTE_USER@$SERVER "cat > $REMOTE_SCRIPT_PATH <<'REMOTE_EOF'
$LOCAL_SCRIPT_CONTENT
REMOTE_EOF"
# 给部署的脚本添加执行权限
ssh $REMOTE_USER@$SERVER "chmod +x $REMOTE_SCRIPT_PATH"
echo " 部署完成。"
done
2. 并行执行以提升速度
如果服务器很多,串行执行(一台做完再做下一台)会非常慢。我们可以利用 & 将任务放入后台,实现并行。
示例5:并行批量执行命令
#!/bin/bash
# 文件名:batch_parallel_command.sh
# 功能:并行在多台服务器上执行耗时命令(如查找大文件)
SERVER_LIST=("host1" "host2" "host3" "host4")
REMOTE_USER="ops"
COMMAND="find /var/log -type f -size +100M 2>/dev/null | head -5" # 查找/var/log下大于100M的文件,取前5个
echo "开始在 ${#SERVER_LIST[@]} 台服务器上并行查找大日志文件..."
# 初始化一个数组,用于存放后台进程的PID
PIDS=()
for SERVER in "${SERVER_LIST[@]}"
do
(
echo "=== $SERVER 结果 ==="
ssh $REMOTE_USER@$SERVER "$COMMAND" || echo " 在 $SERVER 上执行命令失败。"
echo ""
) & # 将整个子shell放入后台执行
PIDS+=($!) # $! 获取最后一个后台进程的PID,并记录到数组
done
# 等待所有后台进程结束
echo "等待所有任务完成..."
wait ${PIDS[@]}
echo "所有并行任务执行完毕。"
这样,所有服务器的查找任务会同时开始,总耗时取决于最慢的那台服务器,而不是所有服务器耗时的总和。
五、应用场景与优缺点分析
应用场景:
- 日常巡检: 批量检查系统负载、磁盘空间、服务状态、日志错误等。
- 配置管理: 统一修改系统参数(如
sysctl.conf)、应用配置(如Nginx, MySQL)。 - 软件部署与更新: 在多台服务器上安装、更新或卸载相同的软件包。
- 数据收集与备份: 定期从多台机器拉取日志、数据库dump文件到中央存储。
- 应急响应: 安全漏洞出现时,快速在所有服务器上执行修补命令或封禁IP。
技术优点:
- 简单直接: 基于现有的SSH和Shell环境,无需安装额外的Agent或管理工具。
- 灵活强大: Shell脚本本身功能强大,可以组合各种Linux命令,实现复杂逻辑。
- 门槛较低: 对于熟悉Linux命令的开发者或运维人员来说,学习成本低。
- 轻量级: 对服务器资源消耗极小,只有执行命令时才有网络和CPU开销。
技术缺点与注意事项:
- 缺乏集中化管理: 脚本和服务器列表需要自己维护,当服务器规模极大、拓扑复杂时,会变得难以管理。此时应考虑专业的配置管理工具(如Ansible,它底层也大量使用了SSH)。
- 错误处理需要精心设计: 网络中断、命令失败等情况必须考虑在内,否则可能导致批量故障。示例3中的“备份-测试-回滚”流程就很重要。
- 安全性: SSH私钥是最高权限的钥匙,必须妥善保管(如设置强密码、使用专用密钥对、定期更换)。避免在脚本中硬编码密码。
- 网络与性能: 批量操作对网络稳定性有要求。并行执行时,要注意控制并发数,避免对控制机或网络造成过大压力。
- 可读性与维护性: 复杂的Shell脚本可能难以阅读和维护。良好的注释、模块化设计(将功能拆分成小函数)和日志记录至关重要。
六、总结
使用Shell脚本进行批量服务器管理,是一种非常实用且高效的“平民化”运维手段。它完美体现了“自动化”和“批量处理”的思想,将运维人员从重复、繁琐的机械操作中解放出来。
其核心在于 “SSH免密登录” 和 “循环执行远程命令” 这两项技术的结合。通过本文的示例,你可以快速上手,实现信息收集、配置更新、文件部署等常见任务。进阶的并行执行和健壮性设计,则能让你的脚本在更复杂、更要求效率的生产环境中游刃有余。
虽然对于超大规模或需要状态管理的场景,更推荐使用Ansible、SaltStack等专业工具,但Shell脚本仍然是每个系统管理员或后端开发者工具箱里最基础、最可靠、最快速的利器之一。掌握它,意味着你掌握了与成批服务器高效对话的基本语言。
评论