2026/9/10 7:47:32

Python自动化运维实战:5个必备工具与脚本案例

Python自动化运维实战:5个必备工具与脚本案例 运维岗干久了最让人崩溃的不是故障本身而是那些周而复始、毫无技术含量的“搬砖操作”。每天上班第一件事先 SSH 登录十几台机器挨个执行free -m、df -h、uptime再把结果复制粘贴到日报里半夜被警报吵醒爬起来登录服务器翻日志结果发现只是某条 cron 任务多打了几行 WARN月底做备份手动 tar 完再传到另一台机器传一半断网又得重来。这些事情不是不能做是太消耗人。我后来做了一个决定凡是重复超过三次的操作一律写 Python 脚本自动化。坚持了两年现在每天真正需要手动处理的运维工作不到半小时剩下的时间用来做架构优化和写工具整个人的状态完全不一样了。这篇文章就聊聊我在这个转变过程中实测下来最值得用的 5 个 Python 自动化运维工具包含选型思路、核心用法、完整实战脚本和踩坑记录。不管你是刚接触运维的新人还是已经被重复任务烦透了的“老兵”照着抄都能少走很多弯路。1. 内容整体设计与思路拆解1.1 为什么是 Python而不是其他语言很多刚入行的朋友会问Shell 不是也能做自动化吗Ansible 不也能批量执行吗为什么非要再加一层 Python我的看法是Shell 适合写一次性、短平快的命令组合但一旦逻辑复杂起来比如要做异常处理、结果统计、并发控制、数据解析Shell 写起来非常痛苦调试也费劲。而 Python 的优势在于语法直观容易维护第三方库覆盖了运维场景的方方面面跨平台本地是 Windows 笔记本也能远程操作 Linux 服务器还能和 Web 系统、监控系统、数据库做联动。拿我自己举例早期用 Shell 写过一个批量部署脚本20 多行后来产品改版需要动态判断不同服务器的角色再执行不同命令脚本膨胀到 200 多行变量满天飞改一个逻辑要小心翼翼。用 Python 重构之后代码量差不多但可读性和可维护性完全不一样出问题也容易排查。运维自动化的核心诉求从来不是“跑通一次”而是“长期稳定可维护”Python 正好适合干这件事。1.2 五款工具如何搭配形成完整闭环5 个工具不是孤立存在的它们分别解决了运维自动化链条上的不同环节。我按自己的实际使用频率和依赖关系做了排序工具解决的核心问题主要应用场景Fabric远程批量执行命令、文件分发多机部署、批量巡检、日志拉取Ansible配置管理与应用编排环境初始化、服务部署、一致性保障psutil本地系统指标采集监控脚本、巡检报告、资源趋势分析watchdog文件系统事件监听新文件自动处理、配置变更感知schedule / APScheduler定时任务调度周期巡检、定时备份、日志清理这 5 个工具形成了一条流水线Fabric 负责“动手”Ansible 负责“确保状态一致”psutil 负责“看数据”watchdog 负责“感知变化”schedule 负责“按时触发”。实际项目中我经常把 psutil 的采集逻辑写进 Fabric 的巡检任务再用 schedule 做定时触发几行代码就串起一个完整的自动化场景。这也是我喜欢这套组合的原因每个工具只干自己最擅长的事组合起来却很顺手。2. 五个必备工具的核心细节解析2.1 Fabric远程批量执行命令的“瑞士军刀”Fabric 是我用过的 Python 远程操作工具里最顺手的一个。它的底层基于 Paramiko 实现 SSH 连接但对上层暴露的 API 非常简洁。2.x 版本的核心是Connection对象一个连接对象就代表了到某台服务器的一条 SSH 通道。from fabric import Connection conn Connection(web01, userroot, connect_kwargs{password: 你的密码}) result conn.run(uptime, hideTrue) print(result.stdout.strip())上面这段代码做的事情等价于你在终端敲ssh rootweb01然后执行uptime。但好处是你可以把它放在循环里批量处理几十台服务器from fabric import Connection hosts [web01, web02, web03] for host in hosts: conn Connection(host, userroot) result conn.run(uptime, hideTrue) print(f{host}: {result.stdout.strip()})连接参数里建议优先使用密钥认证而不是密码安全性和稳定性都好很多。如果必须用密码可以把connect_kwargs单独放到配置文件中不要硬编码在脚本里。Fabric 还支持put和get用来上传和下载文件这个功能在收集日志、分发配置文件时非常实用。我在实际项目中用 Fabric 写过一键上传新版 Jar 包并重启服务的脚本整个流程从原来手工操作十几分钟压缩到 1 分钟以内。2.2 Ansible让服务器配置“指哪打哪”的声明式工具如果说 Fabric 偏“命令式”那么 Ansible 就是典型的“声明式”。它不关心你怎么一步一步去执行只关心最终状态是什么样。比如你希望所有 Web 服务器都安装了 Nginx 并且服务是启动的直接写一个 Playbook 描述这个状态Ansible 会自己去判断需要做什么不会像命令式脚本那样每次都无脑执行。以下是我常用的一个基础环境初始化任务片段- hosts: web_servers become: yes tasks: - name: Install nginx apt: name: nginx state: present - name: Start nginx service service: name: nginx state: started enabled: yesAnsible 最大的优点是“幂等”。所谓幂等就是同一个操作重复执行多次结果是一致的。不会因为脚本重复运行导致服务重复启动、包重复安装而出问题。这一点对于定时任务和无人值守场景尤其重要。它的另一个优势是无需在被管理机器上安装 Agent。只要目标机器支持 SSH控制机上装 Ansible配置好主机清单就能开始工作。对于中小团队来说这意味着极低的落地成本。我在一个 30 台服务器的项目里实践过从零配置好友机清单到 Playbook 跑完整个集群不到半天时间。2.3 psutil把服务器的“体检报告”变成结构化数据psutil 是 Python 生态里做系统信息采集最老牌的库没有之一。它把 CPU、内存、磁盘、网络、进程等信息全部封装成了 Python 对象调用起来非常简单。import psutil print(psutil.cpu_percent(interval1)) print(psutil.virtual_memory().percent) print(psutil.disk_usage(/).percent) print(psutil.net_io_counters().bytes_sent)这段代码分别输出了 CPU 使用率、内存使用率、根分区磁盘占用率和网卡累计发送字节数。相比直接用top和free去抓输出再正则解析psutil 返回的是结构化数据直接进数据库、绘图、赋值给变量都方便得多。我在监控脚本里通常还会用到psutil.Process这一层用来判断某个进程是否还在运行、占用了多少内存。比如可以这样检查 Java 进程import psutil for proc in psutil.process_iter([pid, name, memory_info]): if proc.info[name] java: print(proc.info)如果你的自动化脚本只需要在单机范围内采集指标那 psutil 几乎是唯一需要引入的外部依赖。它是整个监控链路里数据源的基础。2.4 watchdog让脚本自动感知文件变化很多自动化场景里脚本是被动的文件没变就不需要处理文件一旦变化就要立刻响应。watchdog 就是干这个的。它会监听指定目录的文件创建、修改、删除、移动等事件并触发你注册的回调函数。from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler class NewFileHandler(FileSystemEventHandler): def on_created(self, event): print(f新文件出现: {event.src_path}) observer Observer() observer.schedule(NewFileHandler(), path/data/upload, recursiveTrue) observer.start()这个工具最适合的应用场景是“数据落地后自动处理”。我有一次做业务系统对接上游合作方通过 SFTP 把数据文件丢到指定目录我们需要在文件到达后立刻解析入库。用 watchdog 写了个监听脚本文件一落地就自动触发解析全程不需要人工盯目录。有一点需要注意watchdog 只是触发机制。如果你的处理逻辑本身比较耗时建议把任务丢给后台线程或消息队列避免新事件到来时上一次还没处理完。我一般会在回调函数里起一个ThreadPoolExecutor去执行实际任务实现异步处理。2.5 schedule 与 APScheduler给自动化脚本装上“闹钟”运维自动化里“什么时候执行”和“执行什么”一样重要。Python 里最轻量的定时方案是schedule库它的调用方式非常直观几乎不需要学习成本。import schedule import time def job(): print(执行巡检任务) schedule.every().day.at(22:30).do(job) schedule.every(10).minutes.do(job) while True: schedule.run_pending() time.sleep(1)schedule适合简单、单机的定时场景。但如果任务很多、需要持久化、支持复杂触发规则或者要并行执行任务建议换成 APScheduler。APScheduler 的 API 稍微复杂一点但功能强很多支持 cron 表达式、任务合并、错过任务处理等。from apscheduler.schedulers.blocking import BlockingScheduler scheduler BlockingScheduler() scheduler.add_job(job, cron, hour22, minute30) scheduler.start()我个人的习惯是定时任务少于 5 个、不需要持久化的直接用schedule涉及多个业务模块、需要任务记录和动态增删的上用 APScheduler。不要一上来就用重武器够用就好。3. 实操过程与核心环节实现3.1 一键批量巡检从半小时到 30 秒以前我每天上班第一件事就是遍历服务器列表手动执行检查命令。后来我写了一个巡检脚本把 Fabric 和 psutil 结合起来实现“连上去 - 查指标 - 汇总输出”的全自动流程。脚本的核心逻辑不复杂关键在两点一是并行执行取代串行二是输出格式统一。下面是去掉项目敏感信息后的简化版from concurrent.futures import ThreadPoolExecutor from fabric import Connection HOSTS [web01, web02, db01, db02] USER ops def check_host(host): try: conn Connection(host, userUSER, connect_timeout10) cpu conn.run(top -bn1 | grep Cpu(s) | awk {print $2}, hideTrue) mem conn.run(free -m | awk NR2{print $3\/\$2}, hideTrue) disk conn.run(df -h / | awk NR2{print $5}, hideTrue) return f{host}: CPU {cpu.stdout.strip()}%, MEM {mem.stdout.strip()}M, DISK {disk.stdout.strip()} except Exception as e: return f{host}: 连接失败 - {e} with ThreadPoolExecutor(max_workers8) as pool: for result in pool.map(check_host, HOSTS): print(result)这里用ThreadPoolExecutor实现多台服务器并发检查。在可控数量范围内并发可以显著提升速度。30 台服务器串行 SSH 可能要执行近一分钟并发后基本 10 秒内全部出结果。输出我通常还会落一份 CSV 文件方便月底汇总和对比趋势。有一点要特别提醒并发数不要无脑调太高。我曾经一次跑 50 台机器max_workers设成 50结果触发了部分机器的 SSH 连接限制出现大量连接失败。一般建议并发数控制在 5 到 10 之间任务量大可以分批处理。3.2 日志自动清理用 watchdog schedule 组合实现“无人值守”日志清理是运维里最无聊但必须做的活。早期我听凭 cron 定时执行一个find /var/log -mtime 30 -delete但总觉得不够可控。后来我写了一个 Python 服务把“清理策略”做成配置文件用 schedule 每天定时扫描同时对磁盘空间做二次判断。核心逻辑分三步第一步遍历指定目录下所有日志文件按修改时间排序。第二步保留最近 N 天文件其余的删除。第三步删除前先用 psutil 检查磁盘空间是否真的紧张避免误删。简化代码如下import os import time import psutil import schedule LOG_DIR /var/log/myapp RETENTION_DAYS 30 def clean_logs(): disk_usage psutil.disk_usage(/).percent if disk_usage 80: print(f磁盘占用 {disk_usage}%无需清理) return cutoff time.time() - RETENTION_DAYS * 86400 for root, dirs, files in os.walk(LOG_DIR): for name in files: path os.path.join(root, name) mtime os.path.getmtime(path) if mtime cutoff: os.remove(path) print(f已清理: {path}) schedule.every().day.at(03:30).do(clean_logs) while True: schedule.run_pending() time.sleep(60)实际项目里我还会在删除前记录一条日志把清理过的文件路径和大小写到独立的 audit 日志里。这样万一误删了文件至少可以回溯到底删了什么。这算是“在自己能控制的范围内留后路”。3.3 配置变更自动感知watchdog 监听 自动备份生产环境最怕的是有人改了配置没告诉你导致排查故障时两眼一抹黑。我后来用 watchdog 做了一个配置文件监控工具对指定目录做监听一旦检测到配置文件变更就自动做三件事记录变更时间、复制旧文件到备份目录、触发一次服务优雅重载。import shutil import datetime from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler class ConfigHandler(FileSystemEventHandler): def on_modified(self, event): if event.is_directory: return ts datetime.datetime.now().strftime(%Y%m%d_%H%M%S) backup_path f/data/backup/{event.src_path.replace(/, _)}.{ts} shutil.copy2(event.src_path, backup_path) print(f配置已备份: {backup_path}) observer Observer() observer.schedule(ConfigHandler(), path/etc/myapp, recursiveTrue) observer.start()这里有个小技巧很多服务在修改配置文件时会先写临时文件再 rename如果你只监听on_modified可能漏掉。所以监听事件时最好同时处理on_created和on_modified或者直接监听父目录的recursiveTrue避免遗漏。我在实际使用中就遇到过文件被 Vim 以 rename 方式保存导致on_modified不触发的坑后来改成监听目录级别的事件才解决。3.4 让脚本长期稳定运行的部署细节脚本写得再好如果它自己挂了没人知道自动化也就失去了意义。部署 Python 自动化脚本时我通常会做这几件事使用 systemd 服务托管替代裸跑 nohup。开启日志输出统一写到固定路径。外层加一个崩溃自愈机制比如 systemd 的Restartalways。关键任务加一个简单的运行锁避免上次没跑完下次又触发。systemd 配置示例如下[Unit] DescriptionAuto Cleanup Service Afternetwork.target [Service] ExecStart/usr/bin/python3 /opt/scripts/cleanup.py Restartalways RestartSec10 [Install] WantedBymulti-user.target很多从开发转运维的朋友会忽略 systemd 的Restartalways脚本一崩就彻底安静了。加了这个参数只要系统活着脚本就能自动拉起。另外要注意脚本里的输出不能无限增长要定期轮转日志否则小问题也会拖成大事故。4. 常见问题与排查技巧实录4.1 SSH 连接失败和认证问题怎么排查Fabric 连接远程服务器失败90% 的原因集中在三个方面。第一是目标机器的 SSH 端口不是默认 22需要在Connection里显式指定port参数。第二是密钥权限不对OpenSSH 对私钥文件的权限很敏感设为600通常能解决大部分权限报错。第三是连接超时需要设置connect_timeout否则默认情况下脚本可能卡很久才报错。我踩过最隐蔽的一个坑是同一台机器用命令行 ssh 能连上用 Fabric 却报认证失败。后来发现是 Fabric 默认读取的私钥路径和本地 ssh 客户端不一致。解决方法是显式传入私钥路径from fabric import Connection conn Connection(web01, userroot, connect_kwargs{ key_filename: /home/ops/.ssh/id_rsa })4.2 Python 版本不同导致脚本运行结果不一致自动化脚本最怕的就是“在我电脑上运行正常”。我遇到过脚本在本地 Python 3.8 正常放到服务器 Python 3.6 上报语法错误的情况。一个常见的原因是 f-string 和某些新语法在旧版本不支持。解决思路很简单统一运行环境。我建议在目标机器上用虚拟环境或者直接用 Docker 封装。如果不想引入容器退而求其次在脚本文件头部声明#!/usr/bin/env python3并在文档中写明依赖版本。另一个常见问题是psutil这类库在不同系统上的返回值精度有差异跨平台对比数据时要留意。4.3 定时任务重复执行或者中途卡死用schedule的时候如果任务本身执行时间很长超过了间隔时间就会出现任务堆积。比如任务每 5 分钟一次但执行要 20 分钟那系统就会产生大量堆积线程。解决方法是加任务锁任务执行前尝试获取一个文件锁或多线程锁拿不到就直接跳过本次执行。import fcntl def run_with_lock(): with open(/tmp/cleanup.lock, w) as f: try: fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) do_cleanup() except BlockingIOError: print(上一次任务还在执行跳过本次)这种锁在单机环境下很有效。如果是多台机器共享同一个任务建议用 Redis 分布式锁或者数据库唯一索引来保证幂等。4.4 脚本日志出现乱码和字符编码问题运维脚本最容易被忽略的就是字符编码。远程服务器可能是 C.UTF-8也可能是 en_US.UTF-8甚至更老的系统默认 ISO-8859-1。你用 Fabric 执行命令并捕获输出时如果不做编码处理很容易在打印时乱码。我的经验是Fabric 的run方法捕获输出后第一时间规范编码output result.stdout.strip() if isinstance(output, bytes): output output.decode(utf-8, errorsreplace)另外日志文件写入时我统一用encodingutf-8打开文件保证所有环境的格式一致。这一点对于做日志分析和告警匹配非常关键。4.5 如果脚本依赖系统命令注意 PATH 环境差异用 Fabric 远程执行命令时坑最深的往往是 PATH。非交互式 SSH 会话不会加载.bashrc里的 PATH 设置导致某些系统命令找不到。比如kubectl、docker、java这些装在自定义路径下的命令直接执行可能报command not found。我的处理方式是在脚本里显式指定命令的绝对路径或者在执行前设置环境变量。conn.run(export PATH$PATH:/usr/local/bin docker ps)尽量少依赖“当前环境默认能找到命令”这个假设。自动化和人手动操作的最大区别就是一模一样的环境可重复的执行结果。做到这点的前提是打破所有隐式依赖。写代码这件事有时候真的能改变工作状态。我刚开始做自动化时也觉得写脚本要花时间不如手动操作几分钟就完事。但后来发现一旦任务变成每周、每天都在重复自动化的红利就会越来越大。这 5 个工具的组合本质上不是帮我敲键盘而是把“人盯着机器干活”的模式变成了“机器自己干活、人只处理异常”的模式。最后分享一个我自己的小习惯新写好的自动化脚本先让它“裸跑”两周期间不删除手工操作流程。确认脚本输出稳定、边界情况都处理到位了再彻底放手。自动化不是一锤子买卖它需要迭代和维护。但也正是这种逐步完善的过程才让我对这些工具有了更深的理解。希望这篇文章能帮你少踩几个坑把时间花在更有价值的事情上。