用 Ansible 实现网络自动化(下)

1,217 阅读17分钟
原文链接: zhuanlan.zhihu.com

(接上篇)

Ansible 术语和入门

这一章我们将介绍许多 Ansible 的术语和报告中前面部分出现过的关键概念。比如, 清单文件(inventory file)剧本(playbook)剧集(play)任务(task)模块(module)。我们也会去回顾一些其它的概念,这些术语和概念对我们学习使用 Ansible 去进行网络自动化非常有帮助。

在这一节中,我们将引用如下的一个简单的清单文件和剧本的示例,它们将在后面的章节中持续出现。

清单示例

# sample inventory file
# filename inventory

[all:vars]
user=admin
pwd=admin

[tor]
rack1-tor1   vendor=nxos
rack1-tor2   vendor=nxos
rack2-tor1   vendor=arista
rack2-tor2   vendor=arista

[core]
core1
core2

剧本示例

---
# sample playbook
# filename site.yml

  - name: PLAY 1 - Top of Rack (TOR) Switches
    hosts: tor
    connection: local

    tasks:
      - name: ENSURE VLAN 10 EXISTS ON CISCO TOR SWITCHES
        nxos_vlan:
          vlan_id=10
          name=WEB_VLAN
          host={{ inventory_hostname }}
          username=admin
          password=admin
        when: vendor == "nxos"

      - name: ENSURE VLAN 10 EXISTS ON ARISTA TOR SWITCHES
        eos_vlan:
          vlanid=10
          name=WEB_VLAN
          host={{ inventory_hostname }}
          username={{ user }}
          password={{ pwd }}
        when: vendor == "arista"

  - name: PLAY 2 - Core (TOR) Switches
    hosts: core
    connection: local

    tasks:
      - name: ENSURE VLANS EXIST IN CORE
        nxos_vlan:
          vlan_id={{ item }}
          host={{ inventory_hostname }}
          username={{ user }}
          password={{ pwd }}
        with_items:
          - 10
          - 20
          - 30
          - 40
          - 50

清单文件

使用一个清单文件,比如前面提到的那个,允许我们去为自动化任务指定主机、和使用每个剧集顶部节中(如果存在)的参数 hosts 所引用的主机/组指定的主机组。

它也可能在一个清单文件中存储变量。如这个示例中展示的那样。如果变量在同一行视为一台主机,它是一个具体主机变量。如果变量定义在方括号中([ ]),比如,[all:vars],它的意思是指变量在组中的范围 all,它是一个默认组,包含了清单文件中的 所有 主机。

注意:
清单文件是使用 Ansible 开始自动化的快速方法,但是,你应该已经有一个真实的网络设备源,比如一个网络管理工具或者 CMDB,它可以去创建和使用一个动态的清单脚本,而不是一个静态的清单文件。

剧本

剧本是去运行自动化网络设备的顶级对象。在我们的示例中,它是 site.yml 文件,如前面的示例所展示的。一个剧本使用 YAML 去定义一组自动化任务,并且,每个剧本由一个或多个剧集组成。这类似于一个橄榄球的剧本。就像在橄榄球赛中,团队有剧集组成的剧本,Ansible 的剧本也是由剧集组成的。

注意:
YAML 是一种被所有编程语言支持的数据格式。YAML 本身就是 JSON 的超集,并且,YAML 文件非常易于识别,因为它总是三个破折号(连字符)开始,比如,---

剧集

一个 Ansible 剧本可以存在一个或多个剧集。在前面的示例中,它在剧本中有两个剧集。每个剧集开始的地方都有一个 头部,它定义了具体的参数。

示例中两个剧集都定义了下面的参数:

name

文件 PLAY 1 - Top of Rack (TOR) Switches 是任意内容的,它在剧本运行的时候,去改善剧本运行和报告期间的可读性。这是一个可选参数。

hosts

正如前面讲过的,这是在特定的剧集中要去进行自动化的主机或主机组。这是一个必需参数。

connection

正如前面讲过的,这是剧集连接机制的类型。这是个可选参数,但是,对于网络自动化剧集,一般设置为 local

每个剧集都是由一个或多个任务组成。

任务

任务是以声明的方式去表示自动化的内容,而不用担心底层的语法或者操作是怎么执行的。

在我们的示例中,第一个剧集有两个任务。每个任务确保存在 10 个 VLAN。第一个任务是为 Cisco Nexus 设备的,而第二个任务是为 Arista 设备的:

tasks:
  - name: ENSURE VLAN 10 EXISTS ON CISCO TOR SWITCHES
    nxos_vlan:
      vlan_id=10
      name=WEB_VLAN
      host={{ inventory_hostname }}
      username=admin
      password=admin
    when: vendor == "nxos"

任务也可以使用 name 参数,就像剧集一样。和剧集一样,文本内容是任意的,并且当剧本运行时显示,去改善剧本运行和报告期间的可读性。它对每个任务都是可选参数。

示例任务中的下一行是以 nxos_vlan 开始的。它告诉我们这个任务将运行一个叫 nxos_vlan 的 Ansible 模块。

现在,我们将进入到模块中。

模块

在 Ansible 中理解模块的概念是至关重要的。虽然任何编辑语言都可以用来写 Ansible 模块,只要它们能够返回 JSON 键/值对即可,但是,几乎所有的模块都是用 Python 写的。在我们示例中,我们看到有两个模块被运行: nxos_vlaneos_vlan。这两个模块都是 Python 文件;而事实上,在你不能看到剧本的时候,真实的文件名分别是 eos_vlan.pynxos_vlan.py

让我们看一下前面的示例中第一个剧集中的第一个任务:

- name: ENSURE VLAN 10 EXISTS ON CISCO TOR SWITCHES
    nxos_vlan:
      vlan_id=10
      name=WEB_VLAN
      host={{ inventory_hostname }}
      username=admin
      password=admin
    when: vendor == "nxos"

这个任务运行 nxos_vlan,它是一个自动配置 VLAN 的模块。为了使用这个模块,包含它,你需要为设备指定期望的状态或者配置策略。这个示例中的状态是:VLAN 10 将被配置一个名字 WEB_VLAN,并且,它将被自动配置到每个交换机上。我们可以看到,使用 vlan_idname 参数很容易做到。模块中还有三个其它的参数,它们分别是:hostusername、和 password

host

这是将要被自动化的主机名(或者 IP 地址)。因为,我们希望去自动化的设备已经被定义在清单文件中,我们可以使用内置的 Ansible 变量 inventory_hostname。这个变量等价于清单文件中的内容。例如,在第一个循环中,在清单文件中的主机是 rack1-tor1,然后,在第二个循环中,它是 rack1-tor2。这些名字是进入到模块的,并且包含在模块中的,在每个名字到 IP 地址的解析中,都发生一个 DNS 查询。然后与这个设备进行通讯。

username

用于登入到交换机的用户名。

password

用于登入到交换机的密码。

示例中最后的片断部分使用了一个 when 语句。这是在一个剧集中使用的 Ansible 的执行条件任务。正如我们所了解的,在这个剧集的 tor 组中有多个设备和设备类型。使用 when 基于任意标准去提供更多的选择。这里我们仅自动化 Cisco 设备,因为,我们在这个任务中使用了 nxos_vlan 模块,在下一个任务中,我们仅自动化 Arista 设备,因为,我们使用了 eos_vlan 模块。

注意:
这并不是区分设备的唯一方法。这里仅是演示如何使用 when,并且可以在清单文件中定义变量。

在清单文件中定义变量是一个很好的开端,但是,如果你继续使用 Ansible,你将会为了扩展性、版本控制、对给定文件的改变最小化而去使用基于 YAML 的变量。这也将简化和改善清单文件和每个使用的变量的可读性。在设备准备的构建/推送方法中讲过一个变量文件的示例。

在最后的示例中,关于任务有几点需要去搞清楚:

  • 剧集 1 任务 1 展示了硬编码了 usernamepassword 作为参数进入到具体的模块中(nxos_vlan)。
  • 剧集 1 任务 1 和 剧集 2 在模块中使用了变量,而不是硬编码它们。这掩饰了 usernamepassword 参数,但是,需要值得注意的是,(在这个示例中)这些变量是从清单文件中提取出现的。
  • 剧集 1 中为进入到模块中的参数使用了一个 水平的 的 key=value 语法,虽然剧集 2 使用了垂直的 key=value 语法。它们都工作的非常好。你也可以使用垂直的 YAML “key: value” 语法。
  • 最后的任务也介绍了在 Ansible 中怎么去使用一个循环。它通过使用 with_items 来完成,并且它类似于一个 for 循环。那个特定的任务是循环进入五个 VLAN 中去确保在交换机中它们都存在。注意:它也可能被保存在一个外部的 YAML 变量文件中。还需要注意的一点是,不使用 with_items 的替代方案是,每个 VLAN 都有一个任务 —— 如果这样做,它就失去了弹性!

动手实践使用 Ansible 去进行网络自动化

在前面的章节中,提供了 Ansible 术语的一个概述。它已经覆盖了大多数具体的 Ansible 术语,比如剧本、剧集、任务、模块和清单文件。这一节将继续提供示例去讲解使用 Ansible 实现网络自动化,而且将提供在不同类型的设备中自动化工作的模块的更多细节。示例中的将要进行自动化设备由多个供应商提供,包括 Cisco、Arista、Cumulus、和 Juniper。

在本节中的示例,假设的前提条件如下:

  • Ansible 已经安装。
  • 在设备中(NX-API、eAPI、NETCONF)适合的 APIs 已经启用。
  • 用户在系统上有通过 API 去产生改变的适当权限。
  • 所有的 Ansible 模块已经在系统中存在,并且也在库的路径变量中。
注意:
可以在 ansible.cfg 文件中设置模块和库路径。在你运行一个剧本时,你也可以使用 -M 标志从命令行中去改变它。

在本节中示例使用的清单如下。(删除了密码,IP 地址也发生了变化)。在这个示例中,(和前面的示例一样)某些主机名并不是完全合格域名(FQDN)。

清单文件

[cumulus]
cvx  ansible_ssh_host=1.2.3.4 ansible_ssh_pass=PASSWORD

[arista]
veos1

[cisco]
nx1  hostip=5.6.7.8 un=USERNAME pwd=PASSWORD

[juniper]
vsrx hostip=9.10.11.12 un=USERNAME pwd=PASSWORD
注意:
正如你所知道的,Ansible 支持将密码存储在一个加密文件中的功能。如果你想学习关于这个特性的更多内容,请查看在 Ansible 网站上的文档中的 Ansible Vault 部分。

这个清单文件有四个组,每个组定义了一台单个的主机。让我们详细回顾一下每一节:

Cumulus

主机 cvx 是一个 Cumulus Linux (CL) 交换机,并且它是 cumulus 组中的唯一设备。记住,CL 是原生 Linux,因此,这意味着它是使用默认连接机制(SSH)连到到需要自动化的 CL 交换机。因为 cvx 在 DNS 或者 /etc/hosts 文件中没有定义,我们将让 Ansible 知道不要在清单文件中定义主机名,而是在 ansible_ssh_host 中定义的名字/IP。登陆到 CL 交换机的用户名被定义在 playbook 中,但是,你可以看到密码使用变量 ansible_ssh_pass 定义在清单文件中。

Arista

被称为 veos1 的是一台运行 EOS 的 Arista 交换机。它是在 arista 组中唯一的主机。正如你在 Arista 中看到的,在清单文件中并没有其它的参数存在。这是因为 Arista 为它们的设备使用了一个特定的配置文件。在我们的示例中它的名字为 .eapi.conf,它存在在 home 目录中。下面是正确使用配置文件的这个功能的示例:

[connection:veos1]
host: 2.4.3.4
username: unadmin
password: pwadmin

这个文件包含了定义在配置文件中的 Ansible 连接到设备(和 Arista 的被称为 pyeapi 的 Python 库)所需要的全部信息。

Cisco

和 Cumulus 和 Arista 一样,这里仅有一台主机(nx1)存在于 cisco 组中。这是一台 NX-OS-based Cisco Nexus 交换机。注意在这里为 nx1 定义了三个变量。它们包括 unpwd,这是为了在 playbook 中访问和为了进入到 Cisco 模块去连接到设备。另外,这里有一个称为 hostip 的参数,它是必需的,因为,nx1 没有在 DNS 中或者是 /etc/hosts 配置文件中定义。

注意:
如果自动化一个原生的 Linux 设备,我们可以将这个参数命名为任何东西。ansible_ssh_host 被用于到如我们看到的那个 Cumulus 示例(如果在清单文件中的定义不能被解析)。在这个示例中,我们将一直使用 ansible_ssh_host,但是,它并不是必需的,因为我们将这个变量作为一个参数进入到 Cisco 模块,而 ansible_ssh_host 是在使用默认的 SSH 连接机制时自动检查的。

Juniper

和前面的三个组和主机一样,在 juniper 组中有一个单个的主机 vsrx。它在清单文件中的设置与 Cisco 相同,因为两者在 playbook 中使用了相同的方式。

剧本

接下来的剧本有四个不同的剧集。每个剧集是基于特定的供应商类型的设备组的自动化构建的。注意,那是在一个单个的剧本中执行这些任务的唯一的方法。这里还有其它的方法,它可以使用条件(when 语句)或者创建 Ansible 角色(它在这个报告中没有介绍)。

这里有一个剧本的示例:

---

  - name: PLAY 1 - CISCO NXOS
    hosts: cisco
    connection: local

    tasks:
      - name: ENSURE VLAN 100 exists on Cisco Nexus switches
        nxos_vlan:
          vlan_id=100
          name=web_vlan
          host={{ hostip }}
          username={{ un }}
          password={{ pwd }}

  - name: PLAY 2 - ARISTA EOS
    hosts: arista
    connection: local

    tasks:
      - name: ENSURE VLAN 100 exists on Arista switches
        eos_vlan:
          vlanid=100
          name=web_vlan
          connection={{ inventory_hostname }}

  - name: PLAY 3 - CUMULUS
    remote_user: cumulus
    sudo: true
    hosts: cumulus

    tasks:
      - name: ENSURE 100.10.10.1 is configured on swp1
        cl_interface: name=swp1  ipv4=100.10.10.1/24

      - name: restart networking without disruption
        shell: ifreload -a

  - name: PLAY 4 - JUNIPER SRX changes
    hosts: juniper
    connection: local

    tasks:
    - name: INSTALL JUNOS CONFIG
      junos_install_config:
        host={{ hostip }}
        file=srx_demo.conf
        user={{ un }}
        passwd={{ pwd }}
        logfile=deploysite.log
        overwrite=yes
        diffs_file=junpr.diff

你将注意到,前面的两个剧集是非常类似的,我们已经在最初的 Cisco 和 Arista 示例中讲过了。唯一的区别是每个要自动化的组(cisco and arista) 定义了它们自己的剧集,我们在前面介绍使用 when 条件时比较过。

这里有一个不正确的或者是错误的方式去做这些。这取决于你预先知道的信息是什么和适合你的环境和使用的最佳案例是什么,但我们的目的是为了展示做同一件事的几种不同的方法。

第三个剧集是在 Cumulus Linux 交换机的 swp1 接口上进行自动化配置。在这个剧集中的第一个任务是去确认 swp1 是一个三层接口,并且它配置的 IP 地址是 100.10.10.1。因为 Cumulus Linux 是原生的 Linux,网络服务在改变后需要重启才能生效。这也可以使用 Ansible 的操作来达到这个目的(这已经超出了本报告讨论的范围),这里有一个被称为 service 的 Ansible 核心模块来做这些,但它会中断交换机上的网络;使用 ifreload 重新启动则不会中断。

本节到现在为止,我们已经讲解了专注于特定任务的 Ansible 模块,比如,配置接口和 VLAN。第四个剧集使用了另外的选项。我们将看到一个 pushes 模块,它是一个完整的配置文件并且立即激活它作为正在运行的新的配置。这里将使用 napalm_install_config 来展示前面的示例,但是,这个示例使用了一个 Juniper 专用的模块。

junos_install_config 模块接受几个参数,如下面的示例中所展示的。到现在为止,你应该理解了什么是 userpasswd、和 host。其它的参数定义如下:

file

这是一个从 Ansible 控制主机拷贝到 Juniper 设备的配置文件。

logfile

这是可选的,但是,如果你指定它,它会被用于存储运行这个模块时生成的信息。

overwrite

当你设置为 yes/true 时,完整的配置将被发送的配置覆盖。(默认是 false)

diffs_file

这是可选的,但是,如果你指定它,当应用配置时,它将存储生成的差异。当应用配置时将存储一个生成的差异。当正好更改了主机名,但是,仍然发送了一个完整的配置文件时会生成一个差异,如下的示例:

# filename: junpr.diff
[edit system]
-  host-name vsrx;
+  host-name vsrx-demo;

上面已经介绍了剧本概述的细节。现在,让我们看看当剧本运行时发生了什么:

注意:
-i 标志是用于指定使用的清单文件。也可以设置环境变量 ANSIBLE_HOSTS,而不用每次运行剧本时都去使用一个 -i 标志。
ntc@ntc:~/ansible/multivendor$ ansible-playbook -i inventory demo.yml

PLAY [PLAY 1 - CISCO NXOS] *************************************************

TASK: [ENSURE VLAN 100 exists on Cisco Nexus switches] *********************
changed: [nx1]

PLAY [PLAY 2 - ARISTA EOS] *************************************************

TASK: [ENSURE VLAN 100 exists on Arista switches] **************************
changed: [veos1]

PLAY [PLAY 3 - CUMULUS] ****************************************************

GATHERING FACTS ************************************************************
ok: [cvx]

TASK: [ENSURE 100.10.10.1 is configured on swp1] ***************************
changed: [cvx]

TASK: [restart networking without disruption] ******************************
changed: [cvx]

PLAY [PLAY 4 - JUNIPER SRX changes] ****************************************

TASK: [INSTALL JUNOS CONFIG] ***********************************************
changed: [vsrx]

PLAY RECAP ***************************************************************
           to retry, use: --limit @/home/ansible/demo.retry

cvx                        : ok=3    changed=2    unreachable=0    failed=0
nx1                        : ok=1    changed=1    unreachable=0    failed=0
veos1                      : ok=1    changed=1    unreachable=0    failed=0
vsrx                       : ok=1    changed=1    unreachable=0    failed=0

你可以看到每个任务成功完成;如果你是在终端上运行,你将看到用琥珀色显示的每个改变的任务。

让我们再次运行 playbook。通过再次运行,我们可以校验所有模块的 幂等性;当我们这样做的时候,我们看到设备上 没有 产生变化,并且所有的东西都是绿色的:

PLAY [PLAY 1 - CISCO NXOS] ***************************************************

TASK: [ENSURE VLAN 100 exists on Cisco Nexus switches] ***********************
ok: [nx1]

PLAY [PLAY 2 - ARISTA EOS] ***************************************************

TASK: [ENSURE VLAN 100 exists on Arista switches] ****************************
ok: [veos1]

PLAY [PLAY 3 - CUMULUS] ******************************************************

GATHERING FACTS **************************************************************
ok: [cvx]

TASK: [ENSURE 100.10.10.1 is configured on swp1] *****************************
ok: [cvx]

TASK: [restart networking without disruption] ********************************
skipping: [cvx]

PLAY [PLAY 4 - JUNIPER SRX changes] ******************************************

TASK: [INSTALL JUNOS CONFIG] *************************************************
ok: [vsrx]

PLAY RECAP ***************************************************************
cvx                        : ok=2    changed=0    unreachable=0    failed=0
nx1                        : ok=1    changed=0    unreachable=0    failed=0
veos1                      : ok=1    changed=0    unreachable=0    failed=0
vsrx                       : ok=1    changed=0    unreachable=0    failed=0

注意:这里有 0 个改变,但是,每次运行任务,正如期望的那样,它们都返回 “ok”。说明在这个剧本中的每个模块都是幂等的。

总结

Ansible 是一个超级简单的、无代理和可扩展的自动化平台。网络社区持续不断地围绕 Ansible 去重整它作为一个能够执行一些自动化网络任务的平台,比如,做配置管理、数据收集和报告,等等。你可以使用 Ansible 去推送完整的配置文件,配置具体的使用幂等模块的网络资源,比如,接口、VLAN,或者,简单地自动收集信息,比如,领居、序列号、启动时间、和接口状态,以及按你的需要定制一个报告。

因为它的架构,Ansible 被证明是一个在这里可用的、非常好的工具,它可以帮助你实现从传统的基于 CLI/SNMP 的网络设备到基于 API 驱动 的现代化网络设备的自动化。

在网络社区中,易于使用和无代理架构的 Ansible 的占比持续增加,它将使没有 APIs 的设备(CLI/SNMP)的自动化成为可能。包括独立的交换机、路由器、和 4-7 层的服务应用程序;甚至是提供了 RESTful API 的那些软件定义网络控制器。

当使用 Ansible 实现网络自动化时,不会落下任何设备。


作者简介:

Jason Edelman,CCIE 15394 & VCDX-NV 167,出生并成长于新泽西州的一位网络工程师。他是一位典型的 “CLI 爱好者” 和 “路由器小子”。在几年前,他决定更多地关注于软件、开发实践、以及怎么与网络工程融合。Jason 目前经营着一个小的咨询公司,公司名为:Network to Code(networktocode.com/ ),帮助供应商和终端用户利用新的工具和技术来减少他们的低效率操作…


via: www.oreilly.com/learning/ne…

作者:Jason Edelman 译者:qhwdw 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出