第282篇:数据中心网络自动化:Ansible 批量配置

关键词

Ansible、网络自动化、Playbook、Inventory、Module、批量配置、配置推送、配置备份、Network Automation


一、Ansible 网络自动化基础

1.1 为什么用 Ansible

传统 CLI 操作 vs Ansible 自动化:

┌─ 传统方式(单设备 CLI) └─ Ansible 方式(Playbook) --- - name: Config leaf switches hosts: leafs tasks: - name: Configure interface huawei_switch_interface: interface: 10GE1/0/1 mode: trunk vlan: 100

  一次编写,50 台同时执行,可重复、可审计

1.2 Ansible 架构

Ansible 网络自动化架构:

控制节点(Control Node) ┌──────────────────────────────────┐ └──────────────────────────────────┘ ┌──────────────────────────────────┐ └──────────────────────────────────┘ ┌──────────────────────────────────┐ └──────────────────────────────────┘ ┌──────────────────────────────────┐ Inventory(主机清单) [leafs] leaf01 ansible_host=10.1.1.1 leaf02 ansible_host=10.1.1.2 leaf03 ansible_host=10.1.1.3 Playbook(任务剧本) - name: Config VLAN hosts: leafs tasks: - huawei_vlan: ... Module/Plugin(模块/插件) huawei_switch_interface huawei_bgp huawei_vlan Connection(连接方式) network_cli (SSH) httpapi (RESTCONF)
SSH / RESTCONF / NETCONF
┌──────────┴───────────────────────────────┐
│ 受管节点(Managed Nodes) │
│ leaf01 leaf02 leaf03 ... leafN │
└──────────────────────────────────────────┘

1.3 环境准备

安装 Ansible(Python 3.8+):

  python -m pip install ansible

网络自动化常用集合(Collection):

  # 华为设备
  ansible-galaxy collection install community.network

  # 通用网络模块
  ansible-galaxy collection install ansible.netcommon

  验证安装:
  ansible --version
  ansible-doc -l | grep huawei

二、Inventory 主机清单

2.1 静态 Inventory

Inventory 文件:inventory/hosts.ini

# 按角色分组
[leafs]
leaf01 ansible_host=192.168.1.11 ansible_user=admin
leaf02 ansible_host=192.168.1.12 ansible_user=admin
leaf03 ansible_host=192.168.1.13 ansible_user=admin

[spines]
spine01 ansible_host=192.168.1.21 ansible_user=admin
spine02 ansible_host=192.168.1.22 ansible_user=admin

[dc:children]       # 子组
leafs
spines

[all:vars]          # 全局变量
ansible_connection=ansible.netcommon.network_cli
ansible_network_os=community.network.ce
ansible_password=Huawei@123
ansible_become=yes
ansible_become_method=enable

YAML 格式:inventory/hosts.yml

all:
  children:
    leafs:
      hosts:
        leaf01:
          ansible_host: 192.168.1.11
        leaf02:
          ansible_host: 192.168.1.12
    spines:
      hosts:
        spine01:
          ansible_host: 192.168.1.21
  vars:
    ansible_connection: ansible.netcommon.network_cli
    ansible_network_os: community.network.ce
    ansible_user: admin

2.2 变量定义

变量定义方式:

# 1. 组变量:inventory/group_vars/leafs.yml
---
vxlan_vni: 10001
bgp_as: 65001
loopback_ip: "{{ ansible_host }}"
ntp_server: 192.168.1.100
snmp_community: public

# 2. 主机变量:inventory/host_vars/leaf01.yml
---
interface_config:
  - name: 10GE1/0/1
    mode: trunk
    vlan: 100-200
  - name: 10GE1/0/2
    mode: access
    vlan: 10

# 3. 变量使用
vars:
  vlan_id: 100
  vlan_name: "VLAN_{{ vlan_id }}_DATA"

三、Playbook 实战

3.1 VLAN 批量配置

# playbooks/vlan_config.yml
---
- name: Bulk VLAN configuration on leaf switches
  hosts: leafs
  gather_facts: no
  vars:
    vlans:
      - { id: 100, name: DATA_VLAN }
      - { id: 200, name: VOICE_VLAN }
      - { id: 300, name: MGMT_VLAN }

  tasks:
    - name: Create VLANs
      community.network.ce_vlan:
        vlan_id: "{{ item.id }}"
        name: "{{ item.name }}"
        state: present
      loop: "{{ vlans }}"

    - name: Verify VLANs
      community.network.ce_command:
        commands:
          - display vlan summary
      register: vlan_output

    - name: Show VLAN summary
      debug:
        var: vlan_output.stdout_lines

运行 Playbook:
  ansible-playbook playbooks/vlan_config.yml -i inventory/hosts.ini

3.2 接口批量配置

# playbooks/interface_config.yml
---
- name: Configure interfaces on leaf switches
  hosts: leafs
  gather_facts: no

  tasks:
    - name: Configure uplink trunks to spines
      community.network.ce_interface:
        interface: "{{ item.interface }}"
        description: "{{ item.desc }}"
        mode: trunk
        pvid: 1
      loop:
        - { interface: 40GE1/0/1, desc: "To-Spine01-40GE1/0/1" }
        - { interface: 40GE1/0/2, desc: "To-Spine02-40GE1/0/1" }
        - { interface: 40GE1/0/3, desc: "To-Spine01-40GE1/0/2" }
        - { interface: 40GE1/0/4, desc: "To-Spine02-40GE1/0/2" }

    - name: Configure server-facing access ports
      community.network.ce_interface:
        interface: "{{ item.interface }}"
        description: "Server-{{ item.server }}"
        mode: access
        default_vlan: "{{ item.vlan }}"
      loop:
        - { interface: 10GE1/0/1, server: "SVR01-eth0", vlan: 100 }
        - { interface: 10GE1/0/2, server: "SVR02-eth0", vlan: 100 }
        - { interface: 10GE1/0/3, server: "SVR03-eth0", vlan: 200 }

    - name: Save configuration
      community.network.ce_command:
        commands:
          - save
          - Y

3.3 BGP EVPN 配置

# playbooks/bgp_evpn_config.yml
---
- name: Configure BGP EVPN on leaf switches
  hosts: leafs
  gather_facts: no
  vars:
    bgp_as: 65001
    router_id: "{{ lookup('ansible.builtin.env', 'HOSTNAME') }}"

  tasks:
    - name: Enable EVPN
      community.network.ce_evpn:
        state: present

    - name: Configure BGP
      community.network.ce_bgp:
        bgp_as: "{{ bgp_as }}"
        router_id: 10.1.1.1
        peer_group:
          - { name: UNDERLAY, type: ebgp }
        peers:
          - { address: 10.0.12.1, remote_as: 65000, group: UNDERLAY }
          - { address: 10.0.13.1, remote_as: 65000, group: UNDERLAY }

    - name: Configure EVPN address family
      community.network.ce_evpn_bgp:
        bgp_as: "{{ bgp_as }}"
        peer_group:
          - { name: UNDERLAY, advertise_evpn: yes }

    - name: Configure VXLAN
      community.network.ce_vxlan:
        vni: 10001
        bridge_domain: 10
        state: present

3.4 配置备份

# playbooks/backup_config.yml
---
- name: Backup configurations of all devices
  hosts: dc
  gather_facts: no
  vars:
    backup_dir: "./backups/{{ ansible_date_time.date }}"

  tasks:
    - name: Create backup directory
      ansible.builtin.file:
        path: "{{ backup_dir }}"
        state: directory
      delegate_to: localhost
      run_once: yes

    - name: Backup running config
      community.network.ce_command:
        commands:
          - display current-configuration
      register: config_output

    - name: Save to file
      ansible.builtin.copy:
        content: "{{ config_output.stdout[0] }}"
        dest: "{{ backup_dir }}/{{ inventory_hostname }}_running.cfg"
      delegate_to: localhost

    - name: Show backup status
      ansible.builtin.debug:
        msg: "{{ inventory_hostname }} backup saved to {{ backup_dir }}"

四、多厂商兼容

Ansible 多厂商支持:

  华为 CloudEngine(CE 系列):
  ┌─ Collection: community.network
  ├─ Module: ce_*, huawei_*
  ├─ ansible_network_os: community.network.ce
  └─ Connection: network_cli

  思科 Nexus(NX-OS):
  ┌─ Collection: cisco.nxos
  ├─ Module: nxos_*
  ├─ ansible_network_os: cisco.nxos.nxos
  └─ Connection: network_cli / httpapi

  华三(Comware V7):
  ┌─ Collection: community.network
  ├─ Module: hp_comware_*
  ├─ ansible_network_os: community.network.hp_comware
  └─ Connection: network_cli

  Playbook 兼容两厂商示例:
  ---
  - name: Config VLAN on multi-vendor
    hosts: all
    tasks:
      - name: Create VLAN
        community.network.ce_vlan:
          vlan_id: 100
          name: DATA
        when: ansible_network_os == 'community.network.ce'

      - name: Create VLAN on Cisco
        cisco.nxos.nxos_vlan:
          vlan_id: 100
          name: DATA
        when: ansible_network_os == 'cisco.nxos.nxos'

五、生产环境最佳实践

5.1 Playbook 结构规范

推荐的项目结构:

  dc-automation/
  ├── ansible.cfg                 # 全局配置
  ├── inventory/
  │   ├── hosts.ini               # 主机清单
  │   ├── group_vars/
  │   │   ├── all.yml             # 全局变量
  │   │   ├── leafs.yml           # Leaf 组变量
  │   │   └── spines.yml          # Spine 组变量
  │   └── host_vars/
  │       ├── leaf01.yml          # 主机变量
  │       └── leaf02.yml
  ├── playbooks/
  │   ├── site.yml                # 主入口
  │   ├── vlan_config.yml
  │   ├── bgp_evpn.yml
  │   ├── backup.yml
  │   └── rollback.yml            # 回滚 Playbook
  ├── roles/
  │   ├── common/                  # 通用角色:NTP/SNMP/AAA
  │   ├── vxlan/                   # VXLAN 配置角色
  │   ├── bgp/                     # BGP 配置角色
  │   └── monitoring/              # 监控配置角色
  ├── templates/                   # Jinja2 模板
  │   ├── interface.j2
  │   └── bgp.j2
  └── backups/                     # 备份目录

5.2 安全注意事项

Ansible 生产安全实践:

  1. 密码管理
     ┌─ 不在 Inventory 明文写密码
     ├─ 使用 ansible-vault 加密敏感变量
     ├─ 集成 HashiCorp Vault
     └─ 使用 SSH Key 认证

     ansible-vault encrypt inventory/group_vars/all.yml
     ansible-playbook playbook.yml --ask-vault-pass

  2. 变更控制
     ┌─ Playbook 存储在 Git
     ├─ 分支策略:feature → dev → staging → prod
     ├─ 变更需要 PR 审批
     └─ 每次变更留审计日志

  3. 先验证再执行
     ┌─ 使用 --check 模式做干运行
     ├─ 使用 --diff 显示变更内容
     ├─ 分批执行(serial: 1 逐台执行)
     └─ 配置自动回滚

     ansible-playbook playbook.yml --check --diff
     ansible-playbook playbook.yml --serial 1  # 逐台执行

5.3 执行策略

执行策略配置:ansible.cfg

[defaults]
# 连接超时
timeout = 30
# SSH 参数
host_key_checking = False
# 并行数
forks = 20
# 日志
log_path = ./ansible.log
# 角色路径
roles_path = ./roles

[ssh_connection]
# SSH 参数优化
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

六、效果对比

维度 手工 CLI Ansible 自动化
50 台加 VLAN ~2 小时 ~2 分钟
配置备份 ~1 小时 ~3 分钟
BGP 全量配置 ~8 小时 ~10 分钟
人工错误率 ~3-5% ~0%(可重复执行)
可审计性 需人工记录 Git + 日志自动记录
学习成本 中(Playbook 语法)

总结

关键点 说明
Ansible 优势 无 Agent,纯 SSH,批量执行
核心组件 Inventory + Playbook + Module
网络模块 community.network 覆盖华为/思科/华三
安全实践 ansible-vault 加密,--check 预检,Git 版本控制
典型任务 加 VLAN、配置接口、BGP EVPN、配置备份
生产建议 角色化 Playbook,分批执行,自动回滚

思考

  1. Ansible 相比手工 CLI 有哪些优势?效率提升多少?
  2. Inventory 的组变量和主机变量有什么区别?优先级如何?
  3. 如何一次性在 50 台交换机上批量创建 VLAN?
  4. ansible-vault 有什么作用?如何使用?
  5. --check 和 --diff 参数有什么用处?
  6. 如何让 Playbook 支持华为和思科两个厂商?

下篇预告:第283篇 - ZTP(零接触部署)在数据中心的实践,介绍如何实现交换机上电即用的零接触部署。