一、引言
作为一名运维工程师,你是否也经常遇到这些场景:
- 每天重复检查几十台服务器的状态
- 手动分析几百 MB 的日志文件
- 半夜被告警电话叫醒,手动处理故障
- 花几个小时部署新版本
我曾经也是这样,直到我开始用 Python 写自动化脚本。今天分享 5 个我工作中最常用的自动化脚本,帮你从重复劳动中解放出来。
二、5 个实用自动化脚本
1. 日志自动分析脚本
场景: 每天需要分析 Nginx 日志,找出访问异常的 IP 和 URL
传统做法: 用 grep、awk 手动分析,花 30 分钟
自动化方案:
python
#!/usr/bin/env python3
"""
Nginx 日志分析脚本
功能:提取访问 TOP10 的 IP、URL,识别异常请求
"""
import re
from collections import Counter
from datetime import datetime
def analyze_nginx_log(log_file, top_n=10):
"""分析 Nginx 日志"""
# 正则表达式匹配日志行
log_pattern = r'(\d+\.\d+\.\d+\.\d+).*?"(GET|POST|PUT|DELETE)\s+([^\s]+).*?"\s+(\d{3})'
ip_counter = Counter()
url_counter = Counter()
error_requests = []
with open(log_file, 'r', encoding='utf-8') as f:
for line in f:
match = re.search(log_pattern, line)
if match:
ip, method, url, status = match.groups()
ip_counter[ip] += 1
url_counter[url] += 1
# 记录错误请求(4xx 和 5xx)
if status.startswith(('4', '5')):
error_requests.append({
'ip': ip,
'method': method,
'url': url,
'status': status,
'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
})
# 输出结果
print("=" * 60)
print(f"📊 Nginx 日志分析报告")
print("=" * 60)
print(f"\n🔝 访问 TOP{top_n} IP:")
for ip, count in ip_counter.most_common(top_n):
print(f" {ip}: {count} 次")
print(f"\n🔝 访问 TOP{top_n} URL:")
for url, count in url_counter.most_common(top_n):
print(f" {url}: {count} 次")
print(f"\n⚠️ 错误请求数:{len(error_requests)}")
if error_requests:
print("\n最近 5 条错误请求:")
for req in error_requests[:5]:
print(f" [{req['status']}] {req['method']} {req['url']} (IP: {req['ip']})")
return {
'top_ips': ip_counter.most_common(top_n),
'top_urls': url_counter.most_common(top_n),
'error_count': len(error_requests),
'errors': error_requests
}
if __name__ == '__main__':
analyze_nginx_log('/var/log/nginx/access.log')
效果: 30 秒出报告,效率提升 60 倍
进阶用法:
- 定时执行(cron 每天凌晨 2 点)
- 邮件发送报告
- 异常 IP 自动封禁
2. 服务器健康检查脚本
场景: 管理 50+ 台服务器,每天需要检查 CPU、内存、磁盘
传统做法: 登录每台服务器执行 top、df 命令
自动化方案:
python
#!/usr/bin/env python3
"""
服务器健康检查脚本
功能:批量检查服务器资源使用情况,自动告警
"""
import paramiko
import json
from datetime import datetime
# 服务器列表
SERVERS = [
{'host': '192.168.1.10', 'username': 'root', 'password': 'xxx'},
{'host': '192.168.1.11', 'username': 'root', 'password': 'xxx'},
# ... 更多服务器
]
# 告警阈值
THRESHOLDS = {
'cpu': 80, # CPU 使用率 > 80% 告警
'memory': 85, # 内存使用率 > 85% 告警
'disk': 90 # 磁盘使用率 > 90% 告警
}
def check_server(server):
"""检查单台服务器"""
try:
# SSH 连接
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(
hostname=server['host'],
username=server['username'],
password=server['password'],
timeout=5
)
# 执行检查命令
commands = {
'cpu': "top -bn1 | grep 'Cpu(s)' | awk '{print $2 + $4}'",
'memory': "free | grep Mem | awk '{printf \"%.2f\", $3/$2 * 100}'",
'disk': "df -h / | tail -1 | awk '{print $5}' | tr -d '%'"
}
result = {'host': server['host'], 'status': 'OK', 'alerts': []}
for metric, cmd in commands.items():
stdin, stdout, stderr = client.exec_command(cmd)
value = float(stdout.read().decode().strip())
result[metric] = value
# 检查是否超过阈值
if value > THRESHOLDS[metric]:
result['alerts'].append(f"{metric.upper()} 使用率过高:{value}%")
result['status'] = 'WARNING'
client.close()
return result
except Exception as e:
return {
'host': server['host'],
'status': 'ERROR',
'error': str(e)
}
def health_check_all():
"""检查所有服务器"""
print("=" * 60)
print(f"🏥 服务器健康检查报告 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
results = []
for server in SERVERS:
result = check_server(server)
results.append(result)
# 打印结果
status_icon = {'OK': '✅', 'WARNING': '⚠️', 'ERROR': '❌'}
print(f"\n{status_icon.get(result['status'], '❓')} {result['host']}")
if result['status'] == 'OK':
print(f" CPU: {result.get('cpu', 'N/A')}% | 内存:{result.get('memory', 'N/A')}% | 磁盘:{result.get('disk', 'N/A')}%")
elif result['status'] == 'WARNING':
for alert in result['alerts']:
print(f" ⚠️ {alert}")
else:
print(f" 错误:{result.get('error', '未知错误')}")
# 统计
ok_count = sum(1 for r in results if r['status'] == 'OK')
warning_count = sum(1 for r in results if r['status'] == 'WARNING')
error_count = sum(1 for r in results if r['status'] == 'ERROR')
print(f"\n📊 汇总:✅ {ok_count} 正常 | ⚠️ {warning_count} 告警 | ❌ {error_count} 错误")
return results
if __name__ == '__main__':
health_check_all()
效果: 5 分钟检查完 50 台服务器,自动生成报告
依赖安装:
bash
pip install paramiko
3. 批量部署脚本
场景: 需要同时更新 20 台服务器上的应用
传统做法: 一台一台登录、拉代码、重启服务
自动化方案:
python
#!/usr/bin/env python3
"""
批量部署脚本
功能:同时在多台服务器上部署应用,支持回滚
"""
import paramiko
import time
from datetime import datetime
SERVERS = [
{'host': '192.168.1.10', 'username': 'root', 'password': 'xxx'},
{'host': '192.168.1.11', 'username': 'root', 'password': 'xxx'},
]
DEPLOY_CONFIG = {
'app_dir': '/opt/myapp',
'git_repo': 'git@github.com:myorg/myapp.git',
'branch': 'main',
'service_name': 'myapp'
}
def deploy_server(server, config):
"""在单台服务器上部署"""
try:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(**server, timeout=10)
commands = [
f"cd {config['app_dir']}",
"git fetch",
f"git checkout {config['branch']}",
"git pull",
"pip install -r requirements.txt",
f"systemctl restart {config['service_name']}",
f"systemctl status {config['service_name']} --no-pager"
]
print(f"\n🚀 开始部署 {server['host']}...")
for cmd in commands:
stdin, stdout, stderr = client.exec_command(cmd)
exit_status = stdout.channel.recv_exit_status()
if exit_status != 0:
error_msg = stderr.read().decode()
print(f" ❌ 命令失败:{cmd}")
print(f" 错误:{error_msg}")
client.close()
return False
time.sleep(1) # 避免命令执行过快
print(f" ✅ {server['host']} 部署成功")
client.close()
return True
except Exception as e:
print(f" ❌ {server['host']} 部署失败:{str(e)}")
return False
def rolling_deploy():
"""滚动部署(一次一台,避免服务中断)"""
print("=" * 60)
print(f"🚀 滚动部署开始 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
success_count = 0
for i, server in enumerate(SERVERS, 1):
print(f"\n[{i}/{len(SERVERS)}] 部署第 {i} 台服务器")
if deploy_server(server, DEPLOY_CONFIG):
success_count += 1
print(f" ⏳ 等待 30 秒观察服务状态...")
time.sleep(30) # 观察服务是否正常
else:
print(f" ⚠️ 部署失败,暂停后续部署")
break
print(f"\n📊 部署完成:成功 {success_count}/{len(SERVERS)} 台")
return success_count == len(SERVERS)
if __name__ == '__main__':
success = rolling_deploy()
exit(0 if success else 1)
效果: 部署时间从 2 小时缩短到 15 分钟
4. 数据库备份脚本
场景: 每天需要备份 MySQL 数据库,并上传到云存储
自动化方案:
python
#!/usr/bin/env python3
"""
数据库自动备份脚本
功能:备份 MySQL 数据库,压缩并上传到 OSS/S3
"""
import subprocess
import os
from datetime import datetime
import boto3 # AWS S3
# 配置
DB_CONFIG = {
'host': 'localhost',
'user': 'root',
'password': 'your_password',
'databases': ['db1', 'db2', 'db3']
}
BACKUP_DIR = '/backup/mysql'
S3_BUCKET = 'my-backup-bucket'
RETENTION_DAYS = 30 # 保留 30 天
def backup_database(db_name):
"""备份单个数据库"""
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = f"{db_name}_{timestamp}.sql.gz"
filepath = os.path.join(BACKUP_DIR, filename)
# 确保备份目录存在
os.makedirs(BACKUP_DIR, exist_ok=True)
# 执行备份命令
cmd = f"mysqldump -h{DB_CONFIG['host']} -u{DB_CONFIG['user']} -p{DB_CONFIG['password']} {db_name} | gzip > {filepath}"
try:
subprocess.run(cmd, shell=True, check=True)
print(f" {db_name} 备份完成:{filename}")
return filename
except subprocess.CalledProcessError as e:
print(f"db_name} 备份失败:{e}")
return None
def upload_to_s3(filepath, bucket):
"""上传到 S3"""
s3 = boto3.client('s3',
aws_access_key_id='YOUR_KEY',
aws_secret_access_key='YOUR_SECRET'
)
filename = os.path.basename(filepath)
s3.upload_file(filepath, bucket, f"mysql/{filename}")
print(f"☁️ 已上传到 S3: {bucket}/mysql/{filename}")
def cleanup_old_backups():
"""清理过期备份"""
cutoff_date = datetime.now().timestamp() - (RETENTION_DAYS * 86400)
for filename in os.listdir(BACKUP_DIR):
filepath = os.path.join(BACKUP_DIR, filename)
if os.path.getmtime(filepath) < cutoff_date:
os.remove(filepath)
print(f"🗑️ 已删除过期备份:{filename}")
def main():
print("=" * 60)
print(f"💾 数据库备份开始 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
for db_name in DB_CONFIG['databases']:
filename = backup_database(db_name)
if filename:
filepath = os.path.join(BACKUP_DIR, filename)
upload_to_s3(filepath, S3_BUCKET)
cleanup_old_backups()
print("\n 所有备份完成")
if __name__ == '__main__':
main()
依赖安装:
bash
pip install boto3
配置 cron 定时任务:
bash
# 每天凌晨 2 点备份
0 2 * * * /usr/bin/python3 /opt/scripts/backup_mysql.py >> /var/log/backup.log 2>&1
5. SSL 证书到期提醒脚本
场景: 管理多个域名,需要监控 SSL 证书到期时间
自动化方案:
python
#!/usr/bin/env python3
"""
SSL 证书监控脚本
功能:检查证书到期时间,提前 30 天邮件提醒
"""
import ssl
import socket
import smtplib
from datetime import datetime
from email.mime.text import MIMEText
from email.header import Header
# 域名列表
DOMAINS = [
'example.com',
'api.example.com',
'www.example.com'
]
# 邮件配置
EMAIL_CONFIG = {
'smtp_server': 'smtp.qq.com',
'smtp_port': 465,
'from_addr': 'alert@example.com',
'password': 'your_password',
'to_addrs': ['admin@example.com']
}
def check_ssl_cert(domain, port=443):
"""检查 SSL 证书"""
context = ssl.create_default_context()
with socket.create_connection((domain, port)) as sock:
with context.wrap_socket(sock, server_hostname=domain) as ssock:
cert = ssock.getpeercert()
not_after = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y GMT')
days_left = (not_after - datetime.utcnow()).days
return {
'domain': domain,
'expire_date': not_after,
'days_left': days_left,
'status': 'OK' if days_left > 30 else 'WARNING'
}
def send_alert_email(cert_info):
"""发送告警邮件"""
subject = f"⚠️ SSL 证书即将到期 - {cert_info['domain']}"
body = f"""
尊敬的运维团队:
域名 {cert_info['domain']} 的 SSL 证书即将到期!
📅 到期时间:{cert_info['expire_date'].strftime('%Y-%m-%d')}
⏰ 剩余天数:{cert_info['days_left']} 天
请尽快续期,避免服务中断!
---
自动化监控系统
{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""
msg = MIMEText(body, 'plain', 'utf-8')
msg['From'] = Header("SSL 监控", 'utf-8')
msg['To'] = Header("运维团队", 'utf-8')
msg['Subject'] = Header(subject, 'utf-8')
with smtplib.SMTP_SSL(EMAIL_CONFIG['smtp_server'], EMAIL_CONFIG['smtp_port']) as server:
server.login(EMAIL_CONFIG['from_addr'], EMAIL_CONFIG['password'])
server.sendmail(
EMAIL_CONFIG['from_addr'],
EMAIL_CONFIG['to_addrs'],
msg.as_string()
)
print(f"📧 已发送告警邮件到 {EMAIL_CONFIG['to_addrs']}")
def main():
print("=" * 60)
print(f"🔒 SSL 证书检查 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
for domain in DOMAINS:
try:
cert_info = check_ssl_cert(domain)
status_icon = {'OK': '!', 'WARNING': '!!'}
print(f"\n{status_icon.get(cert_info['status'], '❓')} {domain}")
print(f" 到期时间:{cert_info['expire_date'].strftime('%Y-%m-%d')}")
print(f" 剩余天数:{cert_info['days_left']} 天")
if cert_info['status'] == 'WARNING':
send_alert_email(cert_info)
except Exception as e:
print(f"\n X {domain} 检查失败:{e}")
print("\n ! 检查完成")
if __name__ == '__main__':
main()
效果: 避免证书过期导致的服务中断
三、进阶:定时任务管理
脚本写好了,如何定时执行?
方案 1:Cron(简单任务)
bash
# 编辑 crontab
crontab -e
# 示例:每天早上 8 点检查服务器健康
0 8 * * * /usr/bin/python3 /opt/scripts/health_check.py >> /var/log/health.log 2>&1
# 每天凌晨 2 点备份数据库
0 2 * * * /usr/bin/python3 /opt/scripts/backup_mysql.py
方案 2:APScheduler(Python 内置)
python
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime
from my_scripts import health_check, backup_database
scheduler = BlockingScheduler()
# 每天早上 8 点执行健康检查
scheduler.add_job(health_check, 'cron', hour=8, minute=0)
# 每天凌晨 2 点备份数据库
scheduler.add_job(backup_database, 'cron', hour=2, minute=0)
# 每周一上午 9 点发送周报
scheduler.add_job(send_weekly_report, 'cron', day_of_week='mon', hour=9)
print("⏰ 调度器启动...")
scheduler.start()
安装:
bash
pip install apscheduler
四、最佳实践建议
1. 脚本管理
- 统一存放在
/opt/scripts/目录 - 使用 Git 版本控制
- 添加详细的注释和文档
2. 日志记录
python
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='/var/log/my_script.log'
)
logging.info("脚本执行成功")
logging.error("发生错误")
3. 错误处理
python
try:
# 执行操作
pass
except Exception as e:
logging.error(f"操作失败:{e}")
# 发送告警
send_alert(f"脚本执行失败:{e}")
exit(1)
4. 安全性
- 敏感信息使用环境变量
- 脚本权限设置为 700
- 定期更新依赖包
python
import os
# 从环境变量读取密码
db_password = os.getenv('DB_PASSWORD')
五、总结
自动化是运维的核心竞争力之一。这 5 个脚本是我工作中最常用的,希望能帮你:
- 节省时间 - 从重复劳动中解放
- 减少错误 - 避免人为失误
- 提升价值 - 把时间花在更有意义的事情上
最后送大家一句话:
好的运维不是救火英雄,而是让火灾永远不会发生的人。
自动化,就是预防火灾的最好方式。
相关资源:
- Python 官方文档:docs.python.org/3/
- Paramiko 文档:www.paramiko.org/
欢迎关注我,获取更多运维自动化实战技巧!
本文首发于掘金,转载请联系作者