第三章 脚本之力:Shell与Python双剑合璧
3.1 为什么是双剑合璧?
说实话,我见过不少运维同行,要么只认Shell,要么只认Python。其实没必要这么极端。Shell和Python,就像螺丝刀和电钻——各有各的用武之地。
我个人习惯是:短平快的系统操作交给Shell,复杂逻辑和数据处理交给Python。你想想看,写个日志轮转,用Shell三五行就搞定;但要解析JSON格式的监控数据,Python的字典操作明显更顺手。
我在项目中遇到过一位同事,非要用Python写个简单的文件移动脚本,结果光处理路径转义就折腾了半天。嗯,这就是典型的「杀鸡用牛刀」。
3.2 第一个自动化脚本:日志轮转
日志轮转,说白了就是防止日志文件把磁盘撑爆。我刚开始做运维时,有台服务器因为没配轮转,/var/log 目录直接飙到 50GB,系统直接卡死。从那以后,轮转脚本就成了我的标配。
下面这个脚本,我用了好几年,稳定得很:
#!/bin/bash
# 日志轮转脚本 - log_rotate.sh
LOG_DIR="/var/log/myapp"
RETENTION_DAYS=7
ARCHIVE_DIR="/backup/logs/$(date +%Y%m)"
# 创建归档目录
mkdir -p $ARCHIVE_DIR
# 轮转逻辑:压缩并移动
for logfile in $(find $LOG_DIR -name "*.log" -mtime +1); do
gzip $logfile
mv $logfile.gz $ARCHIVE_DIR/
echo "[$(date)] 已轮转: $logfile" >> /var/log/rotate.log
done
# 清理过期归档
find $ARCHIVE_DIR -name "*.gz" -mtime +$RETENTION_DAYS -delete
我的小技巧: 轮转时间建议选在凌晨3点,这时候业务流量最低。用crontab配置:
0 3 * * * /usr/local/bin/log_rotate.sh
3.3 批量部署脚本
批量部署,是运维的日常操作。我曾经手动给100台服务器装过Agent,那叫一个酸爽。后来我写了这个脚本,把重复劳动变成了自动化流水线。
这里我用Python写,因为需要处理服务器列表、错误重试、结果汇总:
#!/usr/bin/env python3
# 批量部署脚本 - batch_deploy.py
import paramiko
import json
import sys
from concurrent.futures import ThreadPoolExecutor
def deploy_to_host(host, user, pkey_path, package_path):
"""单机部署函数"""
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, username=user, key_filename=pkey_path)
# 上传安装包
sftp = ssh.open_sftp()
sftp.put(package_path, "/tmp/deploy_pkg.tar.gz")
sftp.close()
# 执行安装
stdin, stdout, stderr = ssh.exec_command(
"cd /tmp && tar -xzf deploy_pkg.tar.gz && ./install.sh"
)
exit_code = stdout.channel.recv_exit_status()
ssh.close()
return {"host": host, "status": "success" if exit_code == 0 else "failed"}
except Exception as e:
return {"host": host, "status": "error", "msg": str(e)}
# 主逻辑
if __name__ == "__main__":
with open("hosts.json") as f:
hosts = json.load(f)
with ThreadPoolExecutor(max_workers=10) as executor:
results = list(executor.map(
lambda h: deploy_to_host(
h["ip"], h["user"], h["key_path"], sys.argv[1]
), hosts
))
# 输出结果
success = [r for r in results if r["status"] == "success"]
failed = [r for r in results if r["status"] != "success"]
print(f"成功: {len(success)}, 失败: {len(failed)}")
for f in failed:
print(f" {f['host']}: {f.get('msg', '部署失败')}")
避坑指南: 我曾经因为没加超时控制,导致脚本卡在一台宕机的服务器上,整个部署流程停了20分钟。建议给SSH连接加上timeout参数:
ssh.connect(host, timeout=30)
3.4 健康检查脚本
健康检查,是运维的「听诊器」。我习惯用Shell写快速检查,用Python写详细报告。下面这个脚本,是我每天巡检的标配:
#!/bin/bash
# 健康检查脚本 - health_check.sh
echo "=== 系统健康检查报告 ==="
echo "时间: $(date)"
echo ""
# CPU检查
CPU_LOAD=$(uptime | awk -F'load average:' '{print $2}' | cut -d, -f1)
echo "CPU负载: $CPU_LOAD"
if (( $(echo "$CPU_LOAD > 5.0" | bc -l) )); then
echo " ⚠️ CPU负载过高"
fi
# 内存检查
MEM_TOTAL=$(free -m | awk '/Mem:/ {print $2}')
MEM_USED=$(free -m | awk '/Mem:/ {print $3}')
MEM_PERCENT=$(( MEM_USED * 100 / MEM_TOTAL ))
echo "内存使用: ${MEM_USED}MB / ${MEM_TOTAL}MB (${MEM_PERCENT}%)"
if [ $MEM_PERCENT -gt 90 ]; then
echo " ⚠️ 内存使用超过90%"
fi
# 磁盘检查
echo "磁盘使用:"
df -h | grep -E '^/dev/' | awk '{print $5, $6}'
Python版本更擅长做数据汇总和告警:
#!/usr/bin/env python3
# 健康检查报告 - health_report.py
import psutil
import json
from datetime import datetime
def check_system():
report = {
"timestamp": datetime.now().isoformat(),
"cpu": {
"percent": psutil.cpu_percent(interval=1),
"load_avg": psutil.getloadavg()
},
"memory": {
"total": psutil.virtual_memory().total,
"available": psutil.virtual_memory().available,
"percent": psutil.virtual_memory().percent
},
"disk": []
}
for part in psutil.disk_partitions():
if part.fstype:
usage = psutil.disk_usage(part.mountpoint)
report["disk"].append({
"mount": part.mountpoint,
"total": usage.total,
"used": usage.used,
"percent": usage.percent
})
return report
if __name__ == "__main__":
result = check_system()
print(json.dumps(result, indent=2))
# 告警逻辑
if result["cpu"]["percent"] > 90:
print("⚠️ CPU使用率超过90%")
if result["memory"]["percent"] > 90:
print("⚠️ 内存使用率超过90%")
3.5 核心逻辑流程图
下面这张图,是我梳理的自动化脚本核心逻辑。说白了,就是三个步骤:检查→执行→报告:
3.6 脚本整合与最佳实践
这三个脚本,我建议你放在 /usr/local/bin/ 目录下,加上执行权限。然后通过一个主调度脚本来统一管理:
#!/bin/bash
# 主调度脚本 - auto_ops.sh
# 用法: ./auto_ops.sh [rotate|deploy|check]
case "$1" in
rotate)
/usr/local/bin/log_rotate.sh
;;
deploy)
/usr/local/bin/batch_deploy.py /path/to/package.tar.gz
;;
check)
/usr/local/bin/health_check.sh
/usr/local/bin/health_report.py
;;
*)
echo "用法: $0 {rotate|deploy|check}"
exit 1
;;
esac
核心要点:
- Shell适合系统级操作,Python适合复杂逻辑
- 所有脚本必须加错误处理,别裸奔
- 日志输出要规范,方便事后排查
- 用crontab做定时调度,别手动跑
嗯,这三个脚本,基本覆盖了日常运维的80%场景。你拿去改改,就能直接用在生产环境。记住一点:脚本写出来只是开始,持续优化才是关键。