第97课 - 实战:批量运维自动化脚本
学习目标
通过本课学习,你将能够:
- 理解自动化运维的价值:认识到手动重复操作的局限性,并理解脚本自动化如何提升效率与可靠性。
- 掌握批量操作的核心方法:学习使用
for循环结合 SSH、scp、rsync等工具,在多台服务器上批量执行命令或传输文件。 - 编写结构健壮的自动化脚本:学会使用配置文件管理目标主机列表,添加日志记录和错误处理机制,使脚本更易于维护和排错。
- 实现错误处理与日志记录:在脚本中捕获错误,并将执行过程的关键信息写入日志文件,便于事后审计和问题追踪。
- 安全地部署和执行脚本:了解如何通过密钥认证、sudo配置等方式,安全、无交互地运行批量任务。
核心概念
在实际运维工作中,我们经常需要对一批服务器执行相同的操作,例如:更新软件包、分发配置文件、重启某个服务、收集系统信息等。如果服务器数量较多,手动逐一登录操作不仅耗时,而且极易出错。
自动化脚本就是为了解决这个问题而生的。它就像是一个“万能遥控器”,你可以提前设定好要执行的命令和目标主机列表,然后一键让所有机器同步执行。
本课我们将主要使用 Bash 脚本作为自动化工具,因为它功能强大、几乎所有 Linux 系统都内置支持。我们的核心思路是:
- 配置管理:将目标服务器地址、用户名等信息存放在一个单独的配置文件中(如
hosts.txt)。 - 循环控制:使用
for循环遍历配置文件中的每一台主机。 - 远程执行:通过
ssh命令在远程主机上执行命令。 - 错误处理与日志:检查每一步的执行结果(
$?),将成功或失败的信息记录到日志文件,方便排查。
代码示例:一个综合性的批量运维脚本
以下是一个名为 update_servers.sh 的脚本,它演示了如何批量更新一组服务器的系统软件包,并记录执行结果。
#!/bin/bash
# 脚本名称: update_servers.sh
# 功能:批量更新指定服务器的软件包,并记录日志。
# === 配置区 ===
# 目标主机列表文件,每行格式: 主机地址 用户名
HOSTS_FILE="./hosts.txt"
# SSH 私钥文件路径(建议使用免密钥登录)
SSH_KEY="~/.ssh/id_rsa"
# 日志文件
LOG_FILE="./update_$(date +%Y%m%d_%H%M%S).log"
# 要执行的远程命令(以 root 权限更新软件包)
REMOTE_CMD="apt update && apt upgrade -y"
# === 函数定义 ===
# 日志函数:将消息同时输出到屏幕和日志文件
log() {
local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo "$message" | tee -a "$LOG_FILE"
}
# 远程执行函数:在一台主机上执行命令
execute_on_host() {
local host=$1
local user=$2
log ">>> 开始处理主机: ${user}@${host}"
# 执行远程命令,将标准输出和错误输出都重定向到日志(追加模式)
ssh -i ${SSH_KEY} -o ConnectTimeout=10 -o BatchMode=yes ${user}@${host} "${REMOTE_CMD}" >> "${LOG_FILE}" 2>&1
local exit_code=$?
if [ $exit_code -eq 0 ]; then
log "<<< 主机 ${host} 更新成功。"
else
log "<<< [错误] 主机 ${host} 更新失败,退出码: ${exit_code}"
fi
# 返回命令执行结果,供主循环判断
return $exit_code
}
# === 主流程 ===
log "===== 批量更新任务开始 ====="
log "主机列表文件: ${HOSTS_FILE}"
# 检查主机列表文件是否存在
if [ ! -f "$HOSTS_FILE" ]; then
log "[严重错误] 主机列表文件 ${HOSTS_FILE} 不存在!"
exit 1
fi
# 统计变量
total_hosts=0
success_hosts=0
failed_hosts=0
# 遍历主机列表文件
while IFS=' ' read -r host user; do
# 跳过空行和注释行(以#开头)
[[ -z "$host" || "$host" =~ ^# ]] && continue
total_hosts=$((total_hosts + 1))
# 调用函数在主机上执行命令
if execute_on_host "$host" "$user"; then
success_hosts=$((success_hosts + 1))
else
failed_hosts=$((failed_hosts + 1))
fi
done < "$HOSTS_FILE"
# === 结果汇报 ===
log "===== 批量更新任务结束 ====="
log "总计主机数: ${total_hosts}"
log "成功主机数: ${success_hosts}"
log "失败主机数: ${failed_hosts}"
log "详细日志请查看: ${LOG_FILE}"
# 如果有任何一台主机失败,则脚本以非零状态退出
if [ $failed_hosts -gt 0 ]; then
exit 1
else
exit 0
fi
配套的 hosts.txt 文件示例:
# 这是一个注释行
192.168.1.101 admin
192.168.1.102 ops
# 192.168.1.103 root # 这一行被注释掉了,不会执行
10.0.0.5 deploy
实践练习
练习 1:基础 - 添加新的服务器
- 在
hosts.txt文件中添加两台你管理的测试服务器(或虚拟机)的地址和用户名。 - 修改
update_servers.sh中的REMOTE_CMD变量,将命令改为"echo 'Hello from $(hostname)' && uptime",即打印主机名和运行时间。 - 运行脚本,观察日志文件,确认在两台新服务器上成功执行了命令。
练习 2:进阶 - 增加磁盘空间检查
基于 update_servers.sh 脚本,编写一个新的脚本 check_disk.sh。
- 要求:
- 脚本功能:检查
hosts.txt中所有主机的根分区(/)磁盘使用率。 - 远程命令使用
df -h / | tail -1 | awk '{print $5}'来获取使用率百分比。 - 如果某台主机的磁盘使用率超过 80%,则在日志中特别标出警告。
- 最终汇总报告中,列出所有磁盘使用率超过 80% 的主机。
- 脚本功能:检查
- 预期输出(在日志和屏幕上):
>>> 开始检查主机: [email protected] 磁盘使用率: 45% <<< 主机 192.168.1.101 正常。 >>> 开始检查主机: [email protected] 磁盘使用率: 85% [警告] 主机 192.168.1.102 磁盘使用率超过 80%! <<< 主机 192.168.1.102 检查完成。 ===== 磁盘检查任务结束 ===== 警告主机列表: - 192.168.1.102 (85%)
练习 3:挑战 - 实现带条件的服务重启
编写脚本 restart_service_if_down.sh。
- 要求:
- 为脚本增加一个输入参数,用于指定要检查的服务名称(如
nginx)。 - 在
hosts.txt中的每台主机上,检查该服务的运行状态。 - 如果服务未运行(状态为
inactive (dead)),则尝试重启它。 - 记录每台主机的服务初始状态、是否执行了重启、以及重启后的最终状态。
- 如果服务未安装,则记录为“未找到”,不尝试重启。
- 为脚本增加一个输入参数,用于指定要检查的服务名称(如
- 使用方式:
./restart_service_if_down.sh nginx
常见错误
-
SSH 连接问题:
- 错误:
Permission denied (publickey,password)或Connection refused。 - 解决:确保
hosts.txt中的用户名正确;确保目标主机的 SSH 服务已开启并允许该用户登录;最佳实践是提前配置好基于 SSH 密钥的免密登录。
- 错误:
-
脚本权限问题:
- 错误:
-bash: ./update_servers.sh: Permission denied。 - 解决:使用
chmod +x update_servers.sh为脚本添加可执行权限。
- 错误:
-
变量引用错误:
- 错误:远程命令中的变量(如
$())被本地 shell 解析,而不是在远程执行。 - 解决:对整个远程命令使用单引号包裹,例如
ssh host 'echo $(hostname)'。单引号内的内容会被原样传递给远程 shell。
- 错误:远程命令中的变量(如
-
路径问题:
- 错误:脚本中引用了相对路径(如
./hosts.txt),但在其他目录运行时找不到文件。 - 解决:在脚本中,先使用
cd "$(dirname "$0")"切换到脚本所在目录,或者使用绝对路径。
- 错误:脚本中引用了相对路径(如
-
错误处理不当:
- 错误:脚本遇到错误继续执行,最终报告“全部成功”,但实际上部分任务失败。
- 解决:像示例中那样,使用
$?捕获每条命令的退出码,并根据退出码进行条件判断和记录。使用set -e可以让脚本在遇到第一个错误时立即退出(需谨慎使用,可能不适用于所有场景)。
小结
本节课我们通过一个实战案例,学习了批量运维自动化脚本的核心要素:
- 配置化:将可变部分(如主机列表)从脚本逻辑中分离出来,使脚本更灵活。
- 模块化:用函数封装重复逻辑(如日志、执行),让代码更清晰、易维护。
- 健壮性:通过错误检查(
$?)和日志记录,让脚本的执行过程和结果可观测、可追溯。 - 安全性:强调使用密钥认证、
BatchMode=yes等参数进行安全的非交互式连接。
掌握编写自动化脚本的能力,是从“手动运维”迈向“高效运维”的关键一步。它不仅能将你从重复劳动中解放出来,更能确保操作的一致性和可靠性,是高级运维工程师的必备技能。
练习编辑器
rust
Loading...