Python自动化脚本实战:批量创建用户、文件备份、日志清理、服务监控
在日常工作中,我们经常需要重复执行批量创建用户、定期备份文件、清理过期日志、监控服务状态等机械性操作。手动处理不仅耗时耗力,还容易出错。Python作为一门简洁高效的脚本语言,能轻松实现这些操作的自动化,帮我们节省大量时间。本文将分享4个企业级常用自动化脚本的完整实现,包含详细注释和使用教程,内容兼顾新手入门与实际生产需求,适合直接落地使用。
一、批量创建Linux用户(服务器运维必备)
场景说明
在管理Linux服务器时,经常需要为团队成员、测试环境批量创建用户(比如新员工入职、搭建多账号测试环境)。手动执行useradd命令效率极低,用脚本可一键完成批量创建、密码设置、权限配置。
实现思路
- 定义需要创建的用户名列表(可从文件读取,灵活扩展);
- 生成随机强密码(包含大小写字母、数字、特殊字符);
- 通过
subprocess模块执行Linux命令(useradd创建用户、echo设置密码); - 记录创建结果(用户名+密码)到文件,方便分发;
- 加入异常处理,避免重复创建用户报错。
完整代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Linux批量创建用户脚本
功能:批量创建用户、设置随机强密码、记录账号信息
适用环境:Linux系统(CentOS/Ubuntu),需root权限运行
"""
import subprocess
import string
import random
import os
def generate_strong_password(length=12):
"""生成随机强密码:包含大小写字母、数字、特殊字符"""
# 密码字符集:排除容易混淆的字符(如l、O、0、1)
lowercase = string.ascii_lowercase.replace('l', '').replace('o', '')
uppercase = string.ascii_uppercase.replace('O', '').replace('I', '')
digits = string.digits.replace('0', '').replace('1', '')
special_chars = '!@#$%^&*()_+-=[]{}|;:,.?~'
# 确保密码包含每种字符类型
password = [
random.choice(lowercase),
random.choice(uppercase),
random.choice(digits),
random.choice(special_chars)
]
# 填充剩余长度
all_chars = lowercase + uppercase + digits + special_chars
password += [random.choice(all_chars) for _ in range(length - 4)]
# 打乱密码顺序
random.shuffle(password)
return ''.join(password)
def create_linux_user(username, password):
"""创建Linux用户并设置密码"""
try:
# 1. 检查用户是否已存在(id命令返回0表示存在)
check_cmd = f"id -u {username}"
subprocess.run(
check_cmd, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
print(f"⚠️ 用户 {username} 已存在,跳过创建")
return False
except subprocess.CalledProcessError:
# 2. 用户不存在,执行创建命令
try:
# 创建用户(-m自动创建家目录,-s指定bash shell)
useradd_cmd = f"useradd -m -s /bin/bash {username}"
subprocess.run(useradd_cmd, shell=True, check=True, sudo=True)
# 设置密码(echo密码通过管道传给passwd,--stdin避免交互)
passwd_cmd = f"echo '{username}:{password}' | chpasswd"
subprocess.run(passwd_cmd, shell=True, check=True, sudo=True)
# 可选:添加sudo权限(根据需求注释/启用)
# sudo_cmd = f"echo '{username} ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers"
# subprocess.run(sudo_cmd, shell=True, check=True, sudo=True)
print(f"✅ 用户 {username} 创建成功")
return True
except subprocess.CalledProcessError as e:
print(f"❌ 创建用户 {username} 失败:{str(e)}")
return False
def batch_create_users(user_list, output_file="user_accounts.txt"):
"""批量创建用户并记录账号信息到文件"""
# 清空历史记录文件(若存在)
if os.path.exists(output_file):
os.remove(output_file)
# 遍历用户列表创建
success_count = 0
with open(output_file, "w", encoding="utf-8") as f:
f.write("Linux批量创建用户账号信息\n")
f.write("="*50 + "\n")
f.write(f"用户名\t密码\t创建时间\n")
f.write("="*50 + "\n")
for username in user_list:
password = generate_strong_password()
if create_linux_user(username, password):
# 记录账号信息(包含创建时间,需系统安装date命令)
create_time = subprocess.getoutput("date '+%Y-%m-%d %H:%M:%S'")
f.write(f"{username}\t{password}\t{create_time}\n")
success_count += 1
print(f"\n📊 批量创建完成:成功{success_count}个,失败{len(user_list)-success_count}个")
print(f"账号信息已保存到:{os.path.abspath(output_file)}")
if __name__ == "__main__":
# 1. 定义需要创建的用户列表(可从文件读取,示例:user_list = [line.strip() for line in open('users.txt')])
user_list = ["dev01", "dev02", "test01", "test02", "ops01"]
# 2. 检查是否为root用户(Linux创建用户需root权限)
if os.geteuid() != 0:
print("❌ 错误:创建Linux用户需root权限,请用sudo或root用户运行!")
exit(1)
# 3. 执行批量创建
batch_create_users(user_list)
使用方法
- 环境准备:Linux系统(CentOS/Ubuntu),安装Python3(一般系统自带);
- 修改用户列表:将
user_list改为你需要创建的用户名(支持从文件读取,见代码注释); - 运行脚本:需root权限,执行命令
sudo python3 batch_create_users.py; - 查看结果:脚本会生成
user_accounts.txt文件,包含所有成功创建的用户名和密码。
注意事项
- 必须root权限运行(Linux创建用户的系统限制);
- 密码生成后自动记录到文件,建议创建完成后及时分发给用户并提醒修改密码;
- 若需要给用户添加sudo权限,可启用代码中“添加sudo权限”的相关命令(谨慎使用,避免权限泄露);
- 支持Ubuntu/CentOS主流Linux发行版,其他发行版可能需要调整
useradd命令参数。
二、文件自动备份脚本(支持定时/增量备份)
场景说明
重要文件(如项目代码、数据库备份、配置文件)需要定期备份,防止误删、病毒攻击导致数据丢失。该脚本支持指定备份目录、备份到本地/远程服务器、增量备份(只备份新增/修改的文件),可配合Linux定时任务(crontab)实现自动执行。
实现思路
- 配置备份源目录、目标目录(本地或远程SSH目录);
- 支持全量备份(每次备份所有文件)和增量备份(基于文件修改时间);
- 备份文件压缩为zip包,文件名包含时间戳(便于区分版本);
- 自动清理过期备份(按天数保留,避免占用过多磁盘空间);
- 支持备份完成后发送邮件通知(可选)。
完整代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
文件自动备份脚本
功能:全量/增量备份、压缩存储、过期清理、邮件通知
适用环境:Windows/Linux/MacOS,支持本地备份和SSH远程备份
"""
import os
import zipfile
import shutil
import time
from datetime import datetime, timedelta
import smtplib
from email.mime.text import MIMEText
from email.header import Header
# ==================== 备份配置(根据需求修改)====================
BACKUP_SOURCE = ["/home/user/project", "/etc/nginx"] # 要备份的源目录(可多个)
BACKUP_DEST = "/data/backups" # 备份目标目录(本地)
# BACKUP_DEST = "user@192.168.1.100:/remote/backups" # 远程SSH目录(需配置免密登录)
BACKUP_TYPE = "incremental" # 备份类型:full(全量)/ incremental(增量)
RETENTION_DAYS = 7 # 备份保留天数(超过自动删除)
ZIP_PASSWORD = "backup@2024" # 压缩包密码(为空则不加密)
EMAIL_NOTIFY = False # 是否启用邮件通知
SMTP_CONFIG = {
"server": "smtp.qq.com",
"port": 465,
"username": "your_email@qq.com",
"password": "your_smtp_password",
"recipient": "notify_email@xxx.com"
}
def get_backup_filename():
"""生成备份文件名:包含时间戳和备份类型"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
return f"backup_{timestamp}_{BACKUP_TYPE}.zip"
def is_file_modified(file_path, base_time):
"""判断文件是否在基准时间后被修改(用于增量备份)"""
file_mtime = os.path.getmtime(file_path) # 文件最后修改时间(时间戳)
return file_mtime > base_time
def get_last_full_backup_time():
"""获取最近一次全量备份的时间(用于增量备份基准)"""
last_full_time = 0
if not os.path.exists(BACKUP_DEST):
return last_full_time
# 遍历备份目录,查找最近的全量备份文件
for filename in os.listdir(BACKUP_DEST):
if filename.startswith("backup_") and "_full.zip" in filename:
try:
# 从文件名提取时间戳(如backup_20240520_143000_full.zip)
timestamp_str = filename.split("_")[1]
backup_time = datetime.strptime(timestamp_str, "%Y%m%d_%H%M%S").timestamp()
if backup_time > last_full_time:
last_full_time = backup_time
except Exception as e:
print(f"⚠️ 解析全量备份文件 {filename} 失败:{str(e)}")
return last_full_time if last_full_time != 0 else time.time() - 86400 # 默认基准:24小时前
def backup_files(backup_filename):
"""执行文件备份并压缩"""
backup_path = os.path.join(BACKUP_DEST, backup_filename)
base_time = get_last_full_backup_time() if BACKUP_TYPE == "incremental" else 0
try:
# 若目标是远程SSH目录,先备份到本地临时目录
temp_backup = backup_path
if "@" in BACKUP_DEST and ":" in BACKUP_DEST:
temp_dir = "/tmp/local_backup_temp"
os.makedirs(temp_dir, exist_ok=True)
temp_backup = os.path.join(temp_dir, backup_filename)
# 创建压缩包(支持密码加密)
with zipfile.ZipFile(temp_backup, "w", zipfile.ZIP_DEFLATED) as zipf:
# 若设置密码,使用zipfile的setpassword(仅支持传统ZIP加密)
if ZIP_PASSWORD:
zipf.setpassword(ZIP_PASSWORD.encode("utf-8"))
# 遍历所有源目录,添加文件到压缩包
for source_dir in BACKUP_SOURCE:
if not os.path.exists(source_dir):
print(f"⚠️ 源目录 {source_dir} 不存在,跳过")
continue
for root, dirs, files in os.walk(source_dir):
for file in files:
file_path = os.path.join(root, file)
# 增量备份:只添加修改时间在基准时间之后的文件
if BACKUP_TYPE == "incremental" and not is_file_modified(file_path, base_time):
continue
# 保留文件的相对路径(便于恢复)
arcname = os.path.relpath(file_path, start=os.path.commonpath(BACKUP_SOURCE))
zipf.write(file_path, arcname=arcname)
print(f"📤 备份文件:{file_path}")
# 若目标是远程目录,通过scp复制(需配置SSH免密登录)
if "@" in BACKUP_DEST and ":" in BACKUP_DEST:
scp_cmd = f"scp {temp_backup} {BACKUP_DEST}"
os.system(scp_cmd)
os.remove(temp_backup) # 删除本地临时文件
print(f"🌐 已将备份文件上传到远程服务器:{BACKUP_DEST}")
print(f"✅ 备份完成:{backup_path}")
return True, backup_path
except Exception as e:
print(f"❌ 备份失败:{str(e)}")
# 若备份过程出错,删除不完整的压缩包
if os.path.exists(temp_backup):
os.remove(temp_backup)
return False, ""
def clean_expired_backups():
"""清理过期的备份文件"""
if not os.path.exists(BACKUP_DEST) or "@" in BACKUP_DEST:
return # 远程目录暂不支持自动清理
expired_time = datetime.now() - timedelta(days=RETENTION_DAYS)
deleted_count = 0
for filename in os.listdir(BACKUP_DEST):
file_path = os.path.join(BACKUP_DEST, filename)
if os.path.isfile(file_path) and filename.startswith("backup_"):
try:
# 从文件名提取备份时间
timestamp_str = filename.split("_")[1]
backup_time = datetime.strptime(timestamp_str, "%Y%m%d_%H%M%S")
if backup_time < expired_time:
os.remove(file_path)
print(f"🗑️ 删除过期备份:{filename}")
deleted_count += 1
except Exception as e:
print(f"⚠️ 清理文件 {filename} 失败:{str(e)}")
print(f"🧹 过期备份清理完成:共删除 {deleted_count} 个文件")
def send_email_notify(success, backup_path):
"""发送备份结果邮件通知"""
if not EMAIL_NOTIFY:
return
subject = f"【{'成功' if success else '失败'}】文件备份通知"
content = f"""
<h3>文件备份执行结果</h3>
<p>备份时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
<p>备份类型:{BACKUP_TYPE}</p>
<p>源目录:{', '.join(BACKUP_SOURCE)}</p>
<p>目标位置:{BACKUP_DEST}</p>
<p>备份状态:{'✅ 成功' if success else '❌ 失败'}</p>
{f'<p>备份文件:{backup_path}</p>' if success else ''}
<p>保留天数:{RETENTION_DAYS}天</p>
"""
try:
# 配置邮件
msg = MIMEText(content, "html", "utf-8")
msg["From"] = Header(SMTP_CONFIG["username"], "utf-8")
msg["To"] = Header(SMTP_CONFIG["recipient"], "utf-8")
msg["Subject"] = Header(subject, "utf-8")
# 发送邮件(SSL加密)
server = smtplib.SMTP_SSL(SMTP_CONFIG["server"], SMTP_CONFIG["port"])
server.login(SMTP_CONFIG["username"], SMTP_CONFIG["password"])
server.sendmail(
SMTP_CONFIG["username"],
SMTP_CONFIG["recipient"],
msg.as_string()
)
server.quit()
print(f"📧 邮件通知已发送到:{SMTP_CONFIG['recipient']}")
except Exception as e:
print(f"❌ 发送邮件通知失败:{str(e)}")
def main():
print("="*60)
print(f"📅 开始执行文件备份({datetime.now().strftime('%Y-%m-%d %H:%M:%S')})")
print(f"🔧 备份配置:类型={BACKUP_TYPE},保留={RETENTION_DAYS}天,加密={'是' if ZIP_PASSWORD else '否'}")
print("="*60)
# 1. 生成备份文件名
backup_filename = get_backup_filename()
# 2. 执行备份
success, backup_path = backup_files(backup_filename)
# 3. 清理过期备份
clean_expired_backups()
# 4. 发送邮件通知
send_email_notify(success, backup_path)
print("="*60)
print(f"📋 备份任务执行结束({datetime.now().strftime('%Y-%m-%d %H:%M:%S')})")
print("="*60)
if __name__ == "__main__":
main()
使用方法
- 配置修改:根据实际需求修改脚本开头的
BACKUP_SOURCE(备份源目录)、BACKUP_DEST(备份目标)等参数; - 依赖安装:若启用邮件通知,需确保系统安装
smtplib(Python标准库,一般自带); - 远程备份配置:若备份到远程服务器,需先配置SSH免密登录(
ssh-keygen生成密钥,ssh-copy-id复制到远程服务器); - 定时执行(Linux):通过crontab设置定时任务,例如每天凌晨2点执行:
# 编辑crontab配置 crontab -e # 添加一行(Python路径和脚本路径需替换为实际路径) 0 2 * * * /usr/bin/python3 /home/user/scripts/file_backup.py >> /var/log/backup.log 2>&1
注意事项
- 增量备份依赖最近一次全量备份,建议每周执行一次全量备份,每天执行增量备份;
- 压缩包密码仅支持传统ZIP加密,安全性一般,重要数据建议结合其他加密方式;
- 备份目录需提前创建并确保有写入权限;
- 定期检查备份日志和备份文件完整性,避免备份失败未发现。
三、日志清理脚本(自动删除过期/超大日志)
场景说明
服务器日志(如Nginx、Tomcat、应用程序日志)会持续增长,占用大量磁盘空间,甚至导致磁盘满溢影响服务运行。该脚本可按日志文件的时间(过期天数) 或大小(超过阈值) 自动清理,支持日志切割备份后再清理,避免误删有用日志。
实现思路
- 配置需要清理的日志目录、文件后缀(如
.log、.log.*); - 定义清理规则:过期天数(如7天前的日志)、文件大小阈值(如100MB);
- 支持两种清理方式:直接删除、压缩备份后删除;
- 遍历日志目录,筛选符合清理规则的文件并执行操作;
- 记录清理日志,便于后续审计。
完整代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
日志清理脚本
功能:按时间/大小清理日志、压缩备份、记录清理日志
适用环境:Windows/Linux/MacOS
"""
import os
import time
import zipfile
from datetime import datetime, timedelta
# ==================== 清理配置(根据需求修改)====================
LOG_DIRS = ["/var/log/nginx", "/var/log/tomcat", "/home/user/app/logs"] # 日志目录
LOG_SUFFIX = [".log", ".log.", ".txt", ".out"] # 需要清理的日志文件后缀
EXPIRE_DAYS = 7 # 日志过期天数(超过自动清理)
MAX_SIZE_MB = 100 # 日志最大大小(MB),超过自动清理(0表示不按大小清理)
CLEAN_MODE = "delete" # 清理模式:delete(直接删除)/ compress(压缩备份后删除)
BACKUP_DIR = "/var/log/clean_backup" # 压缩备份目录(CLEAN_MODE=compress时生效)
LOG_FILE = "/var/log/log_cleaner.log" # 清理日志记录文件
def init_backup_dir():
"""初始化备份目录(若不存在则创建)"""
if CLEAN_MODE == "compress" and not os.path.exists(BACKUP_DIR):
os.makedirs(BACKUP_DIR, exist_ok=True)
log_info(f"创建备份目录:{BACKUP_DIR}")
def log_info(message):
"""记录清理日志(包含时间戳)"""
log_msg = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {message}\n"
print(log_msg, end="")
# 写入日志文件
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(log_msg)
def get_file_size_mb(file_path):
"""获取文件大小(单位:MB)"""
file_size = os.path.getsize(file_path)
return round(file_size / 1024 / 1024, 2)
def is_log_file(file_name):
"""判断是否为需要清理的日志文件(根据后缀匹配)"""
return any(file_name.endswith(suffix) for suffix in LOG_SUFFIX)
def compress_and_delete(file_path):
"""压缩文件后删除原文件"""
try:
# 生成备份文件名(原文件名+时间戳)
file_dir, file_name = os.path.split(file_path)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_filename = f"{os.path.splitext(file_name)[0]}_{timestamp}.zip"
backup_path = os.path.join(BACKUP_DIR, backup_filename)
# 压缩文件
with zipfile.ZipFile(backup_path, "w", zipfile.ZIP_DEFLATED) as zipf:
zipf.write(file_path, arcname=file_name)
# 删除原文件
os.remove(file_path)
log_info(f"📦 压缩备份并删除:{file_path} → {backup_path}")
return True
except Exception as e:
log_info(f"❌ 压缩文件 {file_path} 失败:{str(e)}")
return False
def clean_log_files():
"""执行日志清理"""
init_backup_dir()
total_cleaned = 0
total_size_freed = 0 # 释放的磁盘空间(MB)
for log_dir in LOG_DIRS:
if not os.path.exists(log_dir):
log_info(f"⚠️ 日志目录 {log_dir} 不存在,跳过")
continue
log_info(f"🔍 开始扫描日志目录:{log_dir}")
for root, dirs, files in os.walk(log_dir):
# 跳过备份目录(避免清理刚备份的日志)
if BACKUP_DIR in root:
continue
for file in files:
if not is_log_file(file):
continue # 只处理指定后缀的日志文件
file_path = os.path.join(root, file)
file_size = get_file_size_mb(file_path)
file_mtime = os.path.getmtime(file_path) # 文件最后修改时间
expire_time = time.time() - EXPIRE_DAYS * 86400 # 过期时间戳
# 满足以下任一条件则清理:1. 过期 2. 超过最大大小(且MAX_SIZE_MB>0)
need_clean = False
if file_mtime < expire_time:
need_clean = True
reason = f"过期({EXPIRE_DAYS}天)"
elif MAX_SIZE_MB > 0 and file_size > MAX_SIZE_MB:
need_clean = True
reason = f"超过最大大小({file_size}MB > {MAX_SIZE_MB}MB)"
if need_clean:
try:
if CLEAN_MODE == "compress":
if compress_and_delete(file_path):
total_cleaned += 1
total_size_freed += file_size
else:
os.remove(file_path)
log_info(f"🗑️ 直接删除日志:{file_path}(原因:{reason},大小:{file_size}MB)")
total_cleaned += 1
total_size_freed += file_size
except Exception as e:
log_info(f"❌ 删除日志 {file_path} 失败:{str(e)}")
log_info(f"✅ 日志清理完成:共清理 {total_cleaned} 个文件,释放 {total_size_freed:.2f} MB 磁盘空间")
log_info("-"*80 + "\n")
def main():
log_info("="*80)
log_info("🚀 开始执行日志清理任务")
log_info(f"📌 清理配置:过期天数={EXPIRE_DAYS}天,最大大小={MAX_SIZE_MB}MB,模式={CLEAN_MODE}")
log_info("="*80)
clean_log_files()
if __name__ == "__main__":
main()
使用方法
- 配置修改:修改
LOG_DIRS为需要清理的日志目录,根据需求调整EXPIRE_DAYS(过期天数)、MAX_SIZE_MB(最大大小)等参数; - 清理模式选择:
CLEAN_MODE="delete"直接删除日志(高效),CLEAN_MODE="compress"先压缩备份再删除(更安全); - 定时执行:Linux系统通过crontab设置每周日凌晨3点执行:
crontab -e # 添加一行 0 3 * * 0 /usr/bin/python3 /home/user/scripts/log_cleaner.py
注意事项
- 首次使用前建议先注释清理逻辑(
os.remove和compress_and_delete),仅执行扫描日志,确认筛选的文件正确后再启用清理; - 避免将重要日志目录(如系统核心日志)加入
LOG_DIRS,防止误删; - 定期检查清理日志(
LOG_FILE),确认清理任务正常执行; - 若日志文件正在被程序写入(如实时日志),直接删除可能导致程序报错,建议结合日志切割工具(如logrotate)使用。
四、服务监控脚本(自动检查状态+重启+报警)
场景说明
服务器上的核心服务(如Nginx、MySQL、Redis、应用程序)可能因内存溢出、端口占用、配置错误等原因意外停止,导致业务中断。该脚本可实时监控服务状态,发现服务停止后自动重启,同时通过邮件/短信报警通知运维人员,减少业务中断时间。
实现思路
- 配置需要监控的服务列表(服务名、启动命令、监控方式);
- 支持多种监控方式:端口监听(如MySQL默认3306端口)、进程名称(如
nginx进程)、自定义检测命令; - 服务停止时执行自动重启,重启失败则发送报警通知;
- 记录监控日志,包含服务状态、重启记录、报警信息;
- 支持邮件报警,可扩展短信、企业微信/钉钉报警。
完整代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
服务监控脚本
功能:监控服务状态、自动重启、邮件报警、日志记录
适用环境:Linux系统(支持systemd服务管理)
"""
import subprocess
import time
import os
from datetime import datetime
import smtplib
from email.mime.text import MIMEText
from email.header import Header
# ==================== 监控配置(根据需求修改)====================
# 服务列表:name(服务名)、type(监控类型:port/process/command)、target(端口/进程名/检测命令)、start_cmd(启动命令)
MONITOR_SERVICES = [
{
"name": "Nginx",
"type": "port",
"target": 80,
"start_cmd": "systemctl start nginx"
},
{
"name": "MySQL",
"type": "port",
"target": 3306,
"start_cmd": "systemctl start mysqld"
},
{
"name": "Redis",
"type": "process",
"target": "redis-server",
"start_cmd": "systemctl start redis"
},
{
"name": "AppService",
"type": "command",
"target": "curl -s http://localhost:8080/health | grep 'UP'", # 健康检查命令
"start_cmd": "systemctl start app-service"
}
]
CHECK_INTERVAL = 60 # 监控检查间隔(秒)
RESTART_RETRY = 2 # 重启失败重试次数
LOG_FILE = "/var/log/service_monitor.log"
# 邮件报警配置
ALERT_EMAIL = {
"enable": True,
"smtp_server": "smtp.163.com",
"smtp_port": 465,
"username": "your_alert@163.com",
"password": "your_smtp_password",
"recipients": ["admin1@xxx.com", "admin2@xxx.com"] # 多个收件人
}
def log_info(message):
"""记录监控日志"""
log_msg = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {message}\n"
print(log_msg, end="")
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(log_msg)
def execute_command(cmd, timeout=30):
"""执行系统命令并返回结果(状态码+输出)"""
try:
result = subprocess.run(
cmd, shell=True, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=timeout
)
return result.returncode, result.stdout.decode("utf-8", errors="ignore").strip()
except subprocess.TimeoutExpired:
return -1, "命令执行超时"
except Exception as e:
return -2, f"命令执行异常:{str(e)}"
def check_service_status(service):
"""检查单个服务状态"""
service_name = service["name"]
monitor_type = service["type"]
target = service["target"]
try:
if monitor_type == "port":
# 监控端口:使用ss命令检查端口是否监听(比netstat更高效)
cmd = f"ss -tuln | grep ': {target} '"
returncode, _ = execute_command(cmd)
is_running = returncode == 0
elif monitor_type == "process":
# 监控进程:使用pgrep检查进程是否存在
cmd = f"pgrep -x {target}" # -x 精确匹配进程名
returncode, _ = execute_command(cmd)
is_running = returncode == 0
elif monitor_type == "command":
# 自定义命令:返回码为0表示服务正常
returncode, _ = execute_command(target)
is_running = returncode == 0
else:
log_info(f"⚠️ 服务 {service_name} 监控类型 {monitor_type} 不支持")
return False
status = "运行中" if is_running else "已停止"
log_info(f"📊 服务 {service_name} 状态:{status}")
return is_running
except Exception as e:
log_info(f"❌ 检查服务 {service_name} 状态失败:{str(e)}")
return False
def start_service(service):
"""启动服务并返回启动结果"""
service_name = service["name"]
start_cmd = service["start_cmd"]
log_info(f"🚀 开始启动服务 {service_name},命令:{start_cmd}")
# 执行启动命令
returncode, output = execute_command(start_cmd)
if returncode == 0:
# 启动后再次检查状态(确保启动成功)
time.sleep(5) # 等待服务启动完成
if check_service_status(service):
log_info(f"✅ 服务 {service_name} 启动成功")
return True
else:
log_info(f"❌ 服务 {service_name} 启动命令执行成功,但状态仍为停止")
return False
else:
log_info(f"❌ 服务 {service_name} 启动失败:{output}")
return False
def send_alert_email(service, status):
"""发送服务状态报警邮件"""
if not ALERT_EMAIL["enable"]:
return
service_name = service["name"]
subject = f"【紧急】服务 {service_name} {'异常停止' if status == 'stop' else '重启失败'}"
content = f"""
<h3>服务监控报警通知</h3>
<p>报警时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
<p>服务名称:{service_name}</p>
<p>服务状态:{status == 'stop' ? '异常停止' : '重启失败'}</p>
<p>监控类型:{service['type']}</p>
<p>目标信息:{service['target']}</p>
<p>启动命令:{service['start_cmd']}</p>
<p>处理建议:请登录服务器检查服务日志,手动排查故障</p>
"""
try:
msg = MIMEText(content, "html", "utf-8")
msg["From"] = Header(ALERT_EMAIL["username"], "utf-8")
msg["To"] = Header(", ".join(ALERT_EMAIL["recipients"]), "utf-8")
msg["Subject"] = Header(subject, "utf-8")
# 发送邮件
server = smtplib.SMTP_SSL(ALERT_EMAIL["smtp_server"], ALERT_EMAIL["smtp_port"])
server.login(ALERT_EMAIL["username"], ALERT_EMAIL["password"])
server.sendmail(
ALERT_EMAIL["username"],
ALERT_EMAIL["recipients"],
msg.as_string()
)
server.quit()
log_info(f"📧 报警邮件已发送到:{', '.join(ALERT_EMAIL['recipients'])}")
except Exception as e:
log_info(f"❌ 发送报警邮件失败:{str(e)}")
def monitor_single_service(service):
"""监控单个服务:检查状态→停止则重启→重启失败则报警"""
service_name = service["name"]
is_running = check_service_status(service)
if not is_running:
# 服务停止,尝试重启
log_info(f"⚠️ 服务 {service_name} 已停止,开始尝试重启")
restart_success = False
for retry in range(RESTART_RETRY):
log_info(f"🔄 重启尝试 {retry+1}/{RESTART_RETRY}")
if start_service(service):
restart_success = True
break
time.sleep(10) # 重启失败后等待10秒再重试
if not restart_success:
# 多次重启失败,发送报警
log_info(f"🔥 服务 {service_name} 多次重启失败,触发报警")
send_alert_email(service, "restart_fail")
else:
# 重启成功,发送恢复通知
send_alert_email(service, "restart_success")
def main():
log_info("="*80)
log_info("🚀 服务监控脚本启动(持续运行中...)")
log_info(f"📌 监控配置:检查间隔={CHECK_INTERVAL}秒,重启重试={RESTART_RETRY}次")
log_info(f"📌 监控服务数:{len(MONITOR_SERVICES)} 个")
for service in MONITOR_SERVICES:
log_info(f" - {service['name']}({service['type']}:{service['target']})")
log_info("="*80 + "\n")
try:
# 循环执行监控
while True:
for service in MONITOR_SERVICES:
monitor_single_service(service)
# 等待下一次检查
time.sleep(CHECK_INTERVAL)
except KeyboardInterrupt:
log_info("🛑 监控脚本被手动停止")
except Exception as e:
log_info(f"❌ 监控脚本异常退出:{str(e)}")
if __name__ == "__main__":
# 检查是否为root用户(部分服务启动需要root权限)
if os.geteuid() != 0:
log_info("⚠️ 警告:非root用户运行,可能导致部分服务启动失败,请考虑用sudo运行")
main()
使用方法
- 配置修改:在
MONITOR_SERVICES中添加需要监控的服务,指定监控类型(port/process/command)、目标值和启动命令; - 报警配置:启用邮件报警需填写
ALERT_EMAIL中的SMTP信息(如QQ邮箱需开启SMTP服务并获取授权码); - 运行脚本:
# 直接运行(前台) python3 service_monitor.py # 后台运行(推荐,避免终端关闭后脚本停止) nohup python3 service_monitor.py >> /var/log/service_monitor.log 2>&1 & - 开机自启:将脚本添加到Linux开机自启(如
/etc/rc.local),确保服务器重启后自动运行监控脚本。
注意事项
- 启动命令需确保能正常执行(建议先手动测试
start_cmd); - 监控间隔
CHECK_INTERVAL不宜过短(避免频繁检查占用系统资源),也不宜过长(导致服务停止后长时间未发现); - 部分服务启动需要时间(如Java应用),可适当调整
start_service函数中的sleep时间; - 建议结合服务器监控工具(如Prometheus)使用,实现更全面的监控告警。
总结
本文分享的4个自动化脚本覆盖了服务器运维、数据安全、日志管理、服务保障等核心场景,具备以下特点:
- 实用性强:直接贴合工作需求,可快速修改配置落地使用;
- 安全性高:包含异常处理、日志记录、备份机制,避免操作风险;
- 扩展性好:支持自定义配置、功能扩展(如添加短信报警、远程监控);
- 跨平台兼容:大部分脚本支持Windows/Linux/MacOS,Linux系统可通过定时任务实现全自动运行。
掌握这些自动化脚本后,你可以将重复的机械性工作交给代码,专注于更有价值的核心任务。实际使用中,可根据具体需求灵活调整脚本逻辑,比如添加更多监控指标、优化备份策略、集成到运维平台等。

浙公网安备 33010602011771号