Python+Netmiko自动化备份交换机配置:从单台到批量实战指南
发布时间:2026/10/5 4:00:53 作者:尧图编辑部 阅读量:1,286

干了几年网络运维你一定经历过这种瞬间核心交换机出了故障急需拿之前的配置做对照翻遍聊天记录和U盘才找到一份三个月前的残缺版本又或者领导突然要求整理全机房几十台设备的配置存档只能晚上一台台登录设备复制粘贴到一个又一个文档里。这种活不累但磨人还特别容易出错。我自己后来的办法很直接写一个 Python 脚本自动登录交换机、抓取 running-config、按时间命名存进本地目录几秒钟一台十几分钟就把全机房搞定。这篇内容就把这个脚本从零到跑通的完整思路、代码和踩坑记录分享出来尤其适合刚学 Python、又天天跟交换机打交道的网络工程师。1. 为什么要写这个备份脚本先搞清楚它解决什么1.1 手动备份配置的三个痛点曾经在半夜被叫起来处理过一起故障核心交换机要紧急重启但登录上去之后我盯着配置界面想确认一下 uplink 口是不是正确结果翻遍整个工位都找不到上周那台设备的最终配置。身边只有三个月前的一份半成品备份最后只能靠着记忆和对端设备的信息一点一点核对。那次之后就下定决心配置备份这种事情一定得自动化。手动备份最扎心的其实是三个问题。第一设备多、命令重复人肉操作容易漏。机房哪怕只有二十台交换机每一台都要登录、输命令、复制输出、存文件。做到第十台的时候你很难保证复制的内容没有丢行、没有粘到上一台设备的片段。深夜加班时尤其明显手指一抖按错 CtrlC整个文件就废了。第二备份文件命名混乱时间一长根本分不清。今天存一个 backup1.cfg明天存一个 20240301_final_v2.cfg放在同一个文件夹里。过两个月自己都分不清哪份是最终版本哪份是哪台设备的。等到真出故障这种混乱才是最大的风险。第三没有定期机制备份的时效性全靠自觉。很多网络的重大变更都发生在深夜或周末变更前没人想着先备份一份。变更出问题想 rollback结果发现上一份备份还在几周之前。脚本化之后这些问题基本都能被兜住连接参数固定、命名规则固定、执行时间固定剩下的只是定期检查一下备份文件是否正常落地。1.2 脚本到底能替代哪些重复工作这个脚本本质上做的事情就四步登录设备、执行配置导出命令、把回显写入本地文件、做异常记录。听起来简单但把重复动作交给代码后省下来的是大量可预期的时间。脚本更适合做成“一键式”比如我每天只需要在终端里执行一下python backup.py脚本就会读取预先维护好的设备列表逐个尝试 SSH 登录把每一台设备的 running-config 存成带有时间戳的文件。整个过程不需要打开 SecureCRT、不需要手工输入账号密码、不需要考虑“这个窗口是不是还停在某个分页状态”。非常适合谁参考一种是刚学 Python 的网工拿这个脚本练手很合适它几乎覆盖了 Python 入门的大部分核心概念字典、列表、函数、循环、异常处理、文件读写、第三方库调用。另一种是日常运维工作量大、设备种类杂的一线运维同学脚本能真正提升效率而不是只当作一个玩具。1.3 为什么选择 Python 而不是其他方案做配置备份的方案不是只有 Python。商业网管平台比如各类 NMS、网管软件确实功能全面但很多场景下要么太贵要么太重为了备份个配置还要专门部署一套平台得不偿失。也有团队用 Ansible自动化能力很强但 Ansible 本身是一套配置管理生态学习曲线比 Python 入门脚本要陡不少而且在一个全是手写命令习惯的团队里Ansible 的推广成本并不低。Python 的优势在于生态里有一个叫 Netmiko 的库专门为网络设备交互而生代码量非常小。它帮我们屏蔽了 SSH 连接的底层细节还兼容 Cisco、华为、H3C、锐捷、Arista 等主流厂商设备。你可以用几乎相同的一套代码只改一两个参数就能连接不同品牌的设备。这是它最吸引人的地方。而且Python 脚本的扩展性极好。今天做配置备份明天想写脚本做批量配置下发后天想对接企业微信做变更通知都可以在同一个脚本基础上改。对网络工程师来说Python 不是要取代网络知识而是把重复劳动压缩到最小让精力回到真正需要判断的事情上。2. 动手前的准备Python 环境、Netmiko 库与连接原理2.1 Python 环境安装Windows 和 Linux 两边都说一下很多网工日常办公还是 Windows所以先讲 Windows。去 Python 官网下载安装包注意安装时有一个很关键的选项——Add Python to PATH一定要勾上。这个选项如果没有勾后面在 cmd 里敲python或pip就会提示“python 不是内部或外部命令”或者“pip 不是内部或外部命令”这是热词榜单里最常出现的问题之一绝大多数新手都栽在这里。安装完成后按 WinR 打开运行输入 cmd 回车在命令行里依次执行python --version pip --version如果都正常输出了版本号说明环境没有问题。如果提示找不到先检查安装时是否勾选了 PATH或者手动把 Python 安装目录下的 Scripts 文件夹加到系统环境变量里。Scripts 目录通常在C:\Users\你的用户名\AppData\Local\Programs\Python\Python311\Scripts具体路径取决于 Python 版本。Linux 和 macOS 这边就更简单。Debian/Ubuntu 系统可以直接用apt install python3 python3-pipCentOS/RHEL 用yum install python3 python3-pip。安装完成后注意有些发行版里命令是python3和pip3不是python写脚本时用python3 backup.py执行即可。如果本机已经有多个 Python 版本建议建一个虚拟环境避免包冲突不过对入门来说不是必须先能跑通更重要。2.2 Netmiko 库安装与它到底帮你干了什么接下来安装今天的主角 Netmiko。在命令行里执行pip install netmikoNetmiko 是基于 Paramiko 的上层封装。Paramiko 是 Python 里的 SSH 协议库可以建立底层 SSH 连接、执行命令、传输文件但用法比较原始解析交换机回显、处理分页、等待提示符这些都要自己写。Netmiko 把这些繁琐细节打包了你只需要告诉它设备类型、地址、账号密码它就能帮你连上设备并提供一个send_command()方法直接返回命令输出。这套设计对网络工程师非常友好。它的安装也很简单pip 会自动把依赖的 Paramiko、色情文本解析相关的库等一并装好。装完可以在 Python 解释器里验证import netmiko print(netmiko.__version__)能打印出版本号就说明安装成功。后面在写代码时会发现Netmiko 自动处理了很多我们容易忽略的操作登录后处理设备的欢迎 banner、关闭分页、等待命令执行完、识别设备提示符。这些工作如果全用原生 SSH 库实现可能要写上百行这也是我喜欢它的原因。这里需要特别留意device_type这个参数Netmiko 靠它来确定怎么和设备交互。不同厂商对应的 device_type 不完全一样厂商/平台device_typeCisco IOScisco_iosCisco Nexuscisco_nxos华为 VRPhuaweiH3C Comwarehp_comware锐捷ruijie_osArista EOSarista_eos如果 device_type 写错典型的报错是Unknown device_type或者是连接后命令交互明显不正常。我见过有人用huawei类型去连 Cisco 设备结果命令提示符识别都成问题怎么都连不通。背这张表不是重点真的使用的时候可以随时查文档但脑子里要有这个概念Netmiko 的“设备兼容”不是自动识别设备品牌而是要你告诉它对方是谁。2.3 设备登录与远端连接的基本原理脚本工作之前你得确保设备本身允许 SSH 登录。这就像你写好了快递单但收件地址的门禁系统没开快递员照样进不了门。Cisco 设备上至少需要配置以下内容enable configure terminal ip ssh version 2 username netadmin privilege 15 secret Net2024 line vty 0 4 transport input ssh login local end核心就三件事开 SSH 服务、创建本地账号、让 VTY 线路允许 SSH 登录。如果不加上transport input ssh设备默认可能还允许 Telnet这不安全而如果不创建本地账号SSH 登录就没地方验证身份。华为和 H3C 设备稍有不同大体逻辑也一样。华为设备常见的几个命令system-view stelnet server enable ssh user netadmin ssh user netadmin authentication-type password ssh user netadmin service-type stelnet local-user netadmin password irreversible-cipher Net2024 local-user netadmin privilege level 15 local-user netadmin service-type ssh如果你的设备上配置了 VTY 访问控制列表也就是只允许某些管理网段过来那还得把运行脚本的这台机器 IP 加进去否则脚本端看到的永远是 Connection refused 或超时。理解了设备端配置再看 Python 这边就顺了。Netmiko 的ConnectHandler建立连接后内部完成 SSH 握手和登录进入设备的用户模式然后可以调用conn.enable()进入特权模式再调用conn.send_command(show running-config)发送命令并等待输出最后conn.disconnect()断开连接。整个过程你看不到底层的密钥交换和提示符匹配但它的确是按照 SSH 的标准流程在走。3. 脚本编写从单台备份到批量备份的完整过程3.1 第一版先让单台设备跑通写脚本最忌讳一上来就搞大而全。第一次写建议先只针对一台 Cisco 设备备份配置哪怕这台设备是 GNS3/EVE-NG 里的一台模拟器都行。跑通之后再扩展批量会省很多排查问题的时间。下面这个版本是最小可用版本先把它保存在一个文件里比如backup.pyfrom netmiko import ConnectHandler from datetime import datetime device { device_type: cisco_ios, host: 192.168.1.1, username: netadmin, password: Net2024, secret: enable123, port: 22, } conn ConnectHandler(**device) conn.enable() output conn.send_command(show running-config) conn.disconnect() filename fbackup_{datetime.now().strftime(%Y%m%d_%H%M%S)}.cfg with open(filename, w, encodingutf-8) as f: f.write(output) print(f备份完成文件{filename})这里有几个地方要解释一下。device是一个字典除了host、username、password之外secret是设备的 enable 密码。如果交换机登录后需要进入特权模式才能执行 show running-config这个参数就不能省。conn.send_command(show running-config)执行后会把完整的命令行回显以字符串形式返回一般包含几百上千行配置。最后用open写入本地文件命名带上时间戳这样每次备份不会覆盖前一次。**device是 Python 的字典解包语法等效于ConnectHandler(device_typecisco_ios, host192.168.1.1, ...)。这个写法很常见也方便后续把参数复用给多个设备。跑这个脚本前提是能 ping 通设备SSH 端口通用户名密码正确。如果顺利你会看到当前目录下多了一个.cfg文件打开看看内容应该就是设备配置。这一步成功整个项目就跑通了一半。3.2 第二版从文本文件读取设备列表批量备份单台跑通以后就可以把“所有设备”塞进来了。这里我建议用一个纯文本文件devices.txt维护设备清单每行一台设备格式建议是“IP hostname”两者用空格或逗号分隔。注释行用#开头脚本会自动忽略# 核心设备 192.168.1.1 core-sw-01 192.168.1.2 core-sw-02 # 接入层 192.168.1.10 access-sw-01 192.168.1.11 access-sw-02改写的脚本逻辑也很自然from netmiko import ConnectHandler from datetime import datetime import os BACKUP_DIR backups DEVICE_LOGIN { device_type: cisco_ios, username: netadmin, password: Net2024, secret: enable123, port: 22, } def backup_device(ip, hostname): device {**DEVICE_LOGIN, host: ip} conn ConnectHandler(**device) conn.enable() output conn.send_command(show running-config) conn.disconnect() os.makedirs(BACKUP_DIR, exist_okTrue) name hostname if hostname else ip timestamp datetime.now().strftime(%Y%m%d_%H%M%S) filename os.path.join(BACKUP_DIR, f{name}_{timestamp}.cfg) with open(filename, w, encodingutf-8) as f: f.write(output) print(f[OK] {ip} ({name}) 备份完成 - {filename}) def load_devices(): devices [] with open(devices.txt, r, encodingutf-8) as f: for line in f: line line.strip() if not line or line.startswith(#): continue parts line.split() ip parts[0] hostname parts[1] if len(parts) 1 else devices.append((ip, hostname)) return devices if __name__ __main__: for ip, hostname in load_devices(): backup_device(ip, hostname)这版脚本最大的变化就是引入了函数和循环。backup_device(ip, hostname)把“备份一台设备”的逻辑封装起来以后想并发、想加日志、想统计结果都在这个函数里改即可。DEVICE_LOGIN放所有设备通用的连接参数单独挖了host字段每次循环用{**DEVICE_LOGIN, host: ip}拼出一台设备完整的连接字典。备份目录提前创建一个backups文件夹文件命名从单一时间戳升级成“主机名_时间戳”以后按文件名就能快速定位是哪台设备、哪天备份的。这一步如果设备数量多体验已经比手工操作强很多了。3.3 第三版异常处理、运行日志与成功失败统计批量循环最现实的问题来了机房二十台设备不可能每台都保证账号密码一样、SSH 服务都正常。如果其中一台连不上脚本就可能直接报错中断后面的设备全都不执行了。所以必须把“连接设备、导出配置”这个动作放进异常处理里。另外我还加上了超时参数和全局延时因子对网络状况波动比较大的设备很管用。conn_timeout指建立 TCP 连接的等待时间global_delay_factor可以整体调大 Netmiko 等待设备回显的时间设备响应慢的时候默认参数下命令容易被认为超时把它的值调成 2 或 3 会更稳。from netmiko import ConnectHandler from datetime import datetime import os BACKUP_DIR backups DEVICE_LOGIN { device_type: cisco_ios, username: netadmin, password: Net2024, secret: enable123, port: 22, conn_timeout: 10, global_delay_factor: 2, } def backup_device(ip, hostname): device {**DEVICE_LOGIN, host: ip} try: conn ConnectHandler(**device) conn.enable() output conn.send_command(show running-config) conn.disconnect() name hostname if hostname else ip timestamp datetime.now().strftime(%Y%m%d_%H%M%S) os.makedirs(BACKUP_DIR, exist_okTrue) filename os.path.join(BACKUP_DIR, f{name}_{timestamp}.cfg) with open(filename, w, encodingutf-8) as f: f.write(output) print(f[OK] {ip} ({name}) 备份完成 - {filename}) return True except Exception as e: print(f[FAIL] {ip} ({hostname}) 备份失败: {e}) return False def load_devices(): devices [] with open(devices.txt, r, encodingutf-8) as f: for line in f: line line.strip() if not line or line.startswith(#): continue parts line.split() ip parts[0] hostname parts[1] if len(parts) 1 else devices.append((ip, hostname)) return devices if __name__ __main__: devices load_devices() success 0 for ip, hostname in devices: if backup_device(ip, hostname): success 1 print(f 共 {len(devices)} 台设备成功 {success} 台失败 {len(devices) - success} 台 )try/except是这个版本的关键。无论设备密码错误、网络不通还是设备提示符不匹配都会被 except 捕获打一条 FAIL 信息然后继续处理下一台设备。最后统计成功和失败的数量这样跑完一眼就知道哪些设备需要人工介入。还有一个容易被忽略的点conn.disconnect()必须放在try的正常流程里不要放在except后面又要执行否则连接异常时可能再抛一个错误。更稳妥的做法是用finally但对这个入门项目来说尽量确保成功路径能正常断开即可。3.4 备份文件命名、存储目录与历史版本保留很多入门脚本在“能备份”之后就停了但实际运维中备份文件的存储策略同样重要。如果每次备份都生成新文件一年后backups目录里可能躺着几千个文件反而无法快速找到需要的版本。我的经验是至少做三件事第一存储目录按日期分层。文件夹结构可以设计成backups/2024/12/31/core-sw-01_20241231_020000.cfg。这样查历史时不需要在几千个文件里翻按日期层次一层层进去即可。实现也不难把os.makedirs(BACKUP_DIR, exist_okTrue)改成基于当前日期拼接多级路径。第二是否自动获取 hostname。如果在设备列表里没填主机名只写了 IP虽然也能备份但后期看了文件名并不直观。可以从设备本身取 hostnameCisco 上执行show running-config | include hostname华为上执行display current-configuration | include sysname。不过为了代码跨厂商通用我更推荐在设备清单里维护一个 hostname 字段既明确又省一次交互。第三保留最近 N 份。历史备份也不是越多越好磁盘空间和文件数量都需要控制。可以写一个清理函数把每台设备目录下超过 30 个文件的最老文件删除def clean_old_backups(directory, keep30): files [] for name in os.listdir(directory): path os.path.join(directory, name) if os.path.isfile(path): files.append((path, os.path.getmtime(path))) files.sort(keylambda x: x[1], reverseTrue) for path, _ in files[keep:]: os.remove(path) print(f清理旧文件: {path})这个函数在每台设备备份完后调用一遍参数 keep 指定保留几份。这样磁盘占用永远是可控的也不会因为备份文件太多导致目录浏览卡顿。4. 实际运行与问题排查第一次跑脚本会遇到的事4.1 首次运行前检查清单入门阶段写脚本最容易出现的挫败感不是代码逻辑错了而是环境或设备端问题导致脚本连不上。为了避免反复调试我列了一个“跑之前先排查”的清单按顺序确认基本能排除 80% 的问题脚本所在机器能 ping 通目标设备目标交换机已经开启 SSH且 VTY 允许 SSH 登录用户名/密码正确enable 密码正确devices.txt 文件里的 IP 和 hostname 没有多余空格编码是 UTF-8Python 环境里已经执行过pip install netmiko如果设备只有 Telnetdevice_type 要改成cisco_ios_telnet等对应类型但生产环境不推荐这些项目里最隐蔽的是最后一条。很多网络设备虽然开了 SSH但是某些老设备只支持 SSHv1而 Netmiko 默认使用 SSHv2 协商有可能连不上。遇到这种情况要么升级设备 SSH 配置要么在连接参数里调整。不过大多数情况下只要设备端ip ssh version 2配了现代设备都能正常协商。4.2 常见报错与排查方法整理成速查表我把自己和同事跑脚本过程中踩过的坑整理成了下面这张表基本覆盖了新手会遇到的大部分情况错误现象可能原因解决办法pip 不是内部或外部命令Python 未加入 PATH安装时勾选 Add Python to PATH或手动配置环境变量ModuleNotFoundError: No module named netmikoNetmiko 未安装执行 pip install netmikoConnection refused / Connection timed out设备 SSH 服务未开启、防火墙阻断、VTY 访问控制检查设备 SSH 配置确认管理网段允许本机访问Authentication failed用户名密码错误或 enable 密码不对核对账号密码连接参数里补 secretUnknown device_typedevice_type 参数拼写错误对照厂商 device_type 表修正ValueError: Unknown terminal...Netmiko 无法识别设备提示符大概率 device_type 不对或对端不是标准设备show running-config 输出为空未进入特权模式或设备命令不同确认 conn.enable() 已调用华为设备改用 display current-configuration备份文件中文乱码字符编码不一致文件用 utf-8 写入Windows 终端切到 UTF-8 显示这里说一个特别容易让人懵的场景脚本报Authentication failed但你明明用 SecureCRT 能连上。原因是 Netmiko 在连接后可能需要额外处理 enable 密码。如果你的账号本身已经是 privilege 15可以不加secret参数但如果设备登录后是普通用户级别必须传secret否则 Netmiko 在enable()时不知道密码是什么自然报认证失败。还有一个坑是设备响应慢导致的“假超时”。命令明明发出去了设备也执行了但回显太慢Netmiko 的默认等待时间不够就会抛超时异常。这种情况先不要怀疑网络把连接参数里的global_delay_factor调到 2 或 3很多时候就好了。如果设备负载实在高再配合conn_timeout调大 TCP 连接等待时间。4.3 备份完怎么看结果才安心脚本跑完之后别只看“成功了几台”就觉得万事大吉。我一般会做三个检查动作。第一个是检查文件大小。配置备份文件的体积通常在几 KB 到几十 KB 之间如果某台设备备份出来的文件只有几百字节多半有问题要么是命令没执行完要么是设备配置本身是空的。用os.path.getsize()可以加一个简单校验小于某个阈值就告警避免后面用到的时候才发现备份文件是残的。第二个是抽查内容。随机打开一两个备份文件看一下开头是否有!或者hostname core-sw-01这样的标志确认设备类型和内容吻合。尤其是刚开始批量备份时一定不要一整批全信要随机抽验。第三个是配置差异比较。如果这是第二次备份可以用 diff 工具和上一次备份做对比确认网络中没有意外变更。Windows 下可以用fc.exeLinux 下直接用diff命令。如果你愿意更进一步后面可以做配置变更告警但这部分在第五部分再细说。5. 从“能跑”到“好用”低成本优化方向5.1 多台设备并发备份效率再往上提一档设备数量多的时候一台一台顺序备份虽然稳但确实慢。二十台设备每台耗时几秒到十几秒合起来就要好几分钟。用 Python 自带的concurrent.futures线程池可以把连接操作并发出去效率提升明显。import concurrent.futures if __name__ __main__: devices load_devices() with concurrent.futures.ThreadPoolExecutor(max_workers5) as executor: futures [executor.submit(backup_device, ip, hostname) for ip, hostname in devices] results [f.result() for f in futures] print(f成功 {sum(results)} 台失败 {len(results) - sum(results)} 台)这里有个经验值并发数不要太高5 个线程同时连接已经不少了。很多低端交换机的 SSH 进程数和 CPU 都很有限一次性来二三十个连接设备 CPU 占用率可能直接飙到百分之八九十反而导致登录超时。尤其是巡检或半夜定时任务时别给设备太大压力3 到 5 个并发是折中的选择。另外并发模式下print输出会交错看着有点乱但不影响结果想更规范可以引入 logging 模块这里先不展开了。5.2 定时备份让脚本变成每天的例行公事手动执行脚本虽然比手工登录舒服但离“自动化”还差一步。定时任务是让备份真正发挥作用的关键设定成每天凌晨两个小时之内跑一次第二天早上到公司检查一下日志或者尝试恢复一台设备的配置验证备份有效性。Windows 下可以使用任务计划程序也可以直接用命令行注册schtasks /create /tn BackupSwitchConfig /tr python D:\scripts\backup.py /sc daily /st 02:00Linux 下就更简单编辑 crontab加入一行0 2 * * * cd /opt/switch-backup /usr/bin/python3 backup.py /opt/switch-backup/backup.log 21如果是 Linux注意 Python 路径建议写绝对路径python3可能在cron的最小环境里找不到。 backup.log 21是把脚本输出和错误信息都写进日志方便第二天排查。日志文件记得定期清空不然几个月下来会很大。定时任务跑起来之后最关键的是“验证备份真的有效”。我见过有人配置了每天备份跑了半年结果某一天真的需要回滚配置时打开备份文件发现文件是 0 字节。这种事故的根因往往是脚本执行时权限不对或路径问题。所以定时任务上线后的头一周每天都要看一眼日志确认成功率和文件大小都正常。5.3 配置变更提醒与安全收尾当定期备份稳定以后就可以考虑“对比变更”了。原理很简单备份完成后把本次文件与上一次文件做哈希对比如果哈希值不同说明配置发生变化可以发通知给运维群。用 Python 内建的 hashlib 就能实现import hashlib import os def file_md5(path): with open(path, rb) as f: return hashlib.md5(f.read()).hexdigest() def get_latest_file(hostname): files [f for f in os.listdir(BACKUP_DIR) if f.startswith(hostname)] if not files: return None files.sort(reverseTrue) return os.path.join(BACKUP_DIR, files[0])在每次备份完成后拿到最新文件和上次文件比较 file_md5。如果不一致就以本次文件为准记录一条变更日志。通知方式可以选企业微信机器人、钉钉机器人或者邮件Webhook 方式最简单只需要一个 HTTP POST 请求。这里不贴完整实现了算是一个自己发挥的方向。最后说一个安全习惯脚本里的密码不要明文写死尤其是放进 Git 仓库的时候。最简单的做法是把密码放在环境变量里import os PASSWORD os.environ.get(NET_PASSWORD)或者运行时用getpass手工输入。更专业一点可以用密钥管理工具。作为入门项目至少记住一点任何账号密码信息都不要提交到公开仓库否则泄露风险太大了。一点个人收尾写这个脚本本身没什么难度最难的是让自己从“手动敲命令”的惯性里跳出来。第一版脚本可能比手动登录还慢跑的时候遇到一台设备失败还会怀疑是不是代码写错了但其实往往是设备端 SSH 配置没开。只要跑通一两周你会发现省下来的时间远超写脚本花的时间。建议你从最小组件开始先备份一台再加设备列表再加定时任务。三版改完你其实已经越过了“网络工程师会用 Python 做点小工具”这个门槛。后续如果还想升级可以研究 Netmiko 的send_config_set()做配置批量下发也可以把备份文件同步到 Git 仓库做版本管理甚至对接其他系统做自动生成巡检报告。这个方向的空间很大起步就是这个小脚本。