Ansible动态主机依赖下带拓扑关联的设备升级执行方案咨询
这个问题绝对是网络设备批量升级时的高频痛点——既要严格遵循拓扑依赖顺序(避免父设备升级导致子设备断连无法后续操作),又不想写一堆重复的硬编码playbook,还得处理升级期间设备离线导致Ansible报错中断的问题。我给你几个实用的、可扩展的解决方案:
方案一:用主机变量定义拓扑依赖,配合串行执行与容错控制
这是最灵活也最容易落地的方案,核心思路是给每台设备定义升级前置依赖的变量,让Ansible自动识别执行顺序,同时处理设备离线的情况:
定义拓扑依赖变量
在你的inventory中,通过host_vars给每台设备添加依赖变量,比如:host_vars/SW2.yml:upgrade_predecessors: [](无前置设备,优先升级)host_vars/SW1.yml:upgrade_predecessors: [SW2](需等SW2升级完成并恢复)host_vars/R1.yml:upgrade_predecessors: [SW1](需等SW1升级完成并恢复)
这种方式完全不需要硬编码组,不管是哪一组设备,只要变量定义正确就能自动适配。
编写通用升级Playbook
利用serial: 1确保单设备串行执行,结合ignore_unreachable忽略升级期间的设备离线报错,再用wait_for_connection等待依赖设备和当前设备恢复在线:--- - name: 按拓扑依赖顺序执行设备升级 hosts: "{{ target_group | default('all') }}" gather_facts: no ignore_unreachable: yes # 忽略升级时设备离线的不可达报错 serial: 1 # 一次只升级一台设备,严格保证顺序 tasks: - name: 等待所有前置依赖设备恢复在线 wait_for_connection: delay: 10 # 延迟10秒再开始检查,避免刚完成升级的设备还在重启 timeout: 180 # 最长等待3分钟,可根据设备重启时间调整 delegate_to: "{{ item }}" loop: "{{ upgrade_predecessors | default([]) }}" when: upgrade_predecessors is defined and upgrade_predecessors | length > 0 - name: 执行设备升级命令 command: "{{ upgrade_command }}" register: upgrade_result ignore_errors: yes # 部分升级命令会返回非零码(比如重启提示),忽略避免中断 - name: 等待当前设备升级后恢复在线 wait_for_connection: delay: 30 # 升级后延迟30秒再检查,给设备留足重启时间 timeout: 240 # 最长等待4分钟 when: upgrade_result.changed多组设备适配
执行时通过extra_vars指定目标组即可,比如:ansible-playbook upgrade.yml -e "target_group=site_a_devices upgrade_command='device-upgrade-script'"完全不需要修改Playbook,直接复用即可。
方案二:基于拓扑层级动态分组(适合复杂拓扑)
如果你的设备拓扑层级清晰(比如核心→汇聚→接入),可以给每台设备定义upgrade_level变量,然后用group_by动态生成升级批次:
定义层级变量
host_vars/SW2.yml:upgrade_level: 1(接入层,最先升级)host_vars/SW1.yml:upgrade_level: 2(汇聚层,次之)host_vars/R1.yml:upgrade_level: 3(核心层,最后升级)
编写分层升级Playbook
--- - name: 按升级层级动态分组 hosts: "{{ target_group }}" gather_facts: no tasks: - group_by: key="upgrade_level_{{ upgrade_level }}" - name: 升级层级1设备(接入层) hosts: upgrade_level_1 gather_facts: no ignore_unreachable: yes tasks: - name: 执行升级命令 command: "{{ upgrade_command }}" ignore_errors: yes - name: 等待设备恢复 wait_for_connection: delay: 30 timeout: 240 - name: 升级层级2设备(汇聚层) hosts: upgrade_level_2 gather_facts: no ignore_unreachable: yes tasks: - name: 等待层级1设备全部恢复 wait_for_connection: delay: 10 timeout: 180 delegate_to: "{{ item }}" loop: "{{ groups['upgrade_level_1'] }}" - name: 执行升级命令 command: "{{ upgrade_command }}" ignore_errors: yes - name: 等待设备恢复 wait_for_connection: delay: 30 timeout: 240 - name: 升级层级3设备(核心层) hosts: upgrade_level_3 gather_facts: no ignore_unreachable: yes tasks: - name: 等待层级2设备全部恢复 wait_for_connection: delay: 10 timeout: 180 delegate_to: "{{ item }}" loop: "{{ groups['upgrade_level_2'] }}" - name: 执行升级命令 command: "{{ upgrade_command }}" ignore_errors: yes - name: 等待设备恢复 wait_for_connection: delay: 30 timeout: 240这种方式适合拓扑层级明确的场景,同样支持通过
target_group指定不同设备组。
方案三:结合CMDB/拓扑数据源动态注入依赖(适合大规模环境)
如果你的设备拓扑已经存储在CMDB(比如NetBox、ServiceNow)中,可以编写自定义Inventory插件,从数据源拉取每台设备的父/子设备关系,自动注入upgrade_predecessors变量。
这种方式完全不需要手动维护变量,拓扑变更时自动同步,非常适合多组、大规模设备的场景,后续只需要在Playbook中复用方案一的逻辑即可。
关键容错注意事项
- 一定要设置
ignore_unreachable: yes:升级时设备会重启离线,Ansible会触发不可达报错,这个参数能让任务继续执行,避免整个Playbook中断。 - 合理调整
wait_for_connection的delay和timeout:根据你的设备重启时间设置,比如交换机可能需要2-3分钟,路由器可能需要更久。 - 用
ignore_errors: yes处理升级命令的非零返回:部分设备升级命令在重启前会返回非零码,忽略这些错误避免中断流程。
备注:内容来源于stack exchange,提问作者Kristof Rado

