Skip to content

Files

Latest commit

f87fb9e · Dec 29, 2021

History

History
980 lines (690 loc) · 37.2 KB

File metadata and controls

980 lines (690 loc) · 37.2 KB

一、Ansible 构造块

Ansible 是一个非常流行的自动化框架,长期以来一直用于自动化信息技术操作。它简化了不同基础设施节点的管理,并将业务逻辑转换为定义良好的过程,以便实现这种业务逻辑。Ansible 是用 Python 编写的,主要依靠 SSH 与基础设施节点进行通信,在基础设施节点上执行指令。它从 Ansible 1.9 开始支持网络设备,从 Ansible 2.9 开始,它目前对网络设备的支持得到了广泛发展。如果网络供应商在其设备上支持应用编程接口,它可以使用 SSH 或通过应用编程接口与网络设备交互。它还提供了多种优势,包括:

  • **轻松学习曲线:**编写 Ansible 剧本需要 YAML 和 Jinja2 模板的知识,简单易学,其描述性语言通俗易懂。
  • **无代理:**不需要在远程管理的设备上安装代理来控制这个设备。
  • 可扩展: Ansible 配备了多个模块,可以在被管理节点上执行各种任务。它还支持编写自定义模块和插件来扩展 Ansible 的核心功能。
  • 幂等: Ansible 不会改变设备的状态,除非它需要改变设置以达到所需的状态。一旦处于这种理想状态,对设备运行操作手册不会改变其配置。

在本章中,我们将介绍 Ansible 的主要组件,并概述 Ansible 支持的不同功能和选项。以下是将要介绍的主要配方:

  • 安装 Ansible
  • 建立 Ansible 的库存
  • 使用 Ansible 变量
  • 构建 Ansible 的行动手册
  • 使用 Ansible 的条件句
  • 使用 Ansible 的循环
  • 用可隐藏的金库保护机密
  • 将 Jinja2 与 Ansible 一起使用
  • 使用 Ansible 的过滤器
  • 使用 Ansible 标签
  • 自定义 Ansible 的设置
  • 使用 Ansible 角色

本章的目的是对不同的 Ansible 组件有一个基本的了解,我们将在本书中使用这些组件来与网络设备进行交互。因此,本章中的所有示例都不是集中在管理网络设备上。相反,我们将专注于理解 Ansible 中的不同组件,以便在接下来的章节中有效地使用它们。

技术要求

以下是安装 Ansible 和运行我们所有 Ansible 行动手册的要求:

  • Linux 虚拟机 ( 虚拟机)具有以下任一发行版:
    • Ubuntu 18.04 或更高版本
    • CentOS 7.0 或更高版本
  • 虚拟机的互联网连接

Setting up the Linux machine is outside the scope of this recipe. However, the easiest approach to setting up a Linux VM with any OS version is by using *Vagrant *to create and set up the Ansible VM.

安装 Ansible

我们安装 Ansible 的机器(这被称为 Ansible 控制机器)应该运行在任何 Linux 发行版上。在这个食谱中,我们将概述如何在 Ubuntu Linux 机器或 CentOS 机器上安装 Ansible。

准备好

要安装 Ansible,我们需要一个使用 Ubuntu 18.04+操作系统或 CentoS 7+操作系统的 Linux 虚拟机。此外,这台机器需要有互联网接入,以便 Ansible 安装在上面。

怎么做...

Ansible 是用 Python 编写的,它的所有模块都需要 Python 才能安装在 Ansible 控制机上。我们的第一个任务是确保 Python 安装在 Ansible 控制机器上,如以下步骤所示。

  1. 大多数 Linux 发行版默认安装了 Python。但是,如果没有安装 Python,以下是在 Linux 上安装它的步骤:
# Install python3
$sudo apt-get install python3

# validate python is installed 
$python3 --version
Python 3.6.9
# Install python
$sudo yum install pytho3

# validate python is installed 
$python3 --version
Python 3.6.8
  1. 在我们验证安装了 Python 之后,我们可以开始安装 Ansible:
# We need to use ansible repository to install the latest version of Ansible
$ sudo apt-add-repository ppa:ansible/ansible

# Update the repo cache to use the new repo added
$ sudo apt-get update

# We install Ansible
$ sudo apt-get install ansible
# We need to use latest epel repository to get the latest ansible 
$ sudo yum install epel-release

# We install Ansible
$ sudo yum install ansible

它是如何工作的..

安装 Ansible 最简单的方法是使用我们的 Linux 发行版专用的包管理器。我们只需要确保我们已经启用了所需的存储库来安装最新版本的 Ansible。在 Ubuntu 和 CentOS 中,我们都需要启用额外的存储库,为 Ansible 提供最新版本。在 CentOS 中,我们需要安装并启用企业 Linux 存储库的额外软件包(【EPEL】****【回购】),它提供额外的软件包并拥有最新的适用于 CentOS 的 Ansible 软件包。

使用这种方法,我们将安装 Ansible 和运行 Ansible 模块所需的所有必要的系统包。在 Ubuntu 和 CentOS 中,这种方法也将安装 Python 2,并使用 Python 2 运行 Ansible。我们可以通过运行以下命令来验证是否安装了 Ansible 以及使用了哪个版本:

$ ansible --version
ansible 2.9
 config file = /etc/ansible/ansible.cfg
 configured module search path = [u'/home/vagrant/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
 ansible python module location = /usr/lib/python2.7/site-packages/ansible
 executable location = /usr/bin/ansible
 python version = 2.7.5 (default, Aug 7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]

此外,我们可以通过使用如图所示的ping模块尝试连接到本地机器来检查 Ansible 是否按预期工作:

$ ansible -m ping localhost

localhost | SUCCESS => {
 "changed": false,
 "ping": "pong"
}

使用这种方法,我们可以看到它存在以下问题:

  • 它使用 Python 2 作为执行环境,但是我们想使用 Python 3。
  • 它更新安装在系统上的 Python 包,这可能是不可取的。
  • 它没有为我们提供选择使用哪个版本的 Ansible 所需的粒度。使用这种方法,我们将总是安装最新版本的 Ansible,这可能不是我们需要的。

它是如何工作的...

为了在 Python 3 环境中安装 Ansible,并更好地控制 Ansible 的部署版本,我们将使用 pip Python 程序安装 Ansible,如下所示:

  • 如果 Python 3 不存在,请安装它,如下所示:
# Ubuntu
$ sudo apt-get install python3

# CentOS
sudo yum install python3
  • 安装python3-pip包:
# Ubuntu
$ sudo apt-get install python3-pip

# CentOS
$ sudo yum install python3-pip
  • 安装 Ansible:
# Ubuntu and CentOS
# This will install ansible for the current user ONLY
$ pip3 install ansible==2.9 --user

# We Can install ansible on the System Level
$ sudo pip3 install ansible==2.9
  • 我们可以验证 Ansible 是否已成功安装,如下所示:
$$ ansible --version
ansible 2.9
 config file = None
 configured module search path = ['/home/vagrant/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
 ansible python module location = /home/vagrant/.local/lib/python3.6/site-packages/ansible
 executable location = /home/vagrant/.local/bin/ansible
 python version = 3.6.8 (default, Aug 7 2019, 17:28:10) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]

使用这种方法安装 Ansible 确保我们使用 Python 3 作为执行环境,并允许我们控制安装 Ansible 的哪个版本,如所示示例所示。

我们将使用此方法作为我们的 Ansible 安装方法,所有后续章节都将基于此安装过程。

In Chapter 13, Advanced Techniques and Best Practices for Ansible, we will outline yet another method for installing Ansible using Python virtual environments.

请参见...

有关 Ansible 安装的更多信息,请查看以下网址:

https://docs . ansi ble . com/ansi ble/latest/installation _ guide/intro _ installation . html

建立 Ansible 的库存

安装 Ansible 之后,我们需要定义 Ansible 的清单,这是一个文本文件,定义了 Ansible 将要管理的节点。在本食谱中,我们将概述如何创建和构造 Ansible 的库存文件。

准备好

我们需要创建一个文件夹,其中包含我们将在本章中概述的所有代码。我们创建一个名为ch1_ansible的文件夹,如下图所示:

$ mkdir ch1_ansible
$ cd ch1_ansible

怎么做...

执行以下步骤创建清单文件:

  1. 创建名为hosts的文件:
$ touch hosts
  1. 使用任何文本编辑器,打开文件并添加以下内容:
$ cat hosts

[cisco]
csr1 ansible_host=172.10.1.2
csr2 ansible_host=172.10.1.3

[juniper]
mx1 ansible_host=172.20.1.2
mx2 ansible_host=172.20.1.3

[core]
mx1
mx2

[edge]
csr[1:2]

[network:children]
core
edge

The Ansible inventory file can have any name. However, as a best practice, we will use the name hosts to describe the devices in our inventory.

它是如何工作的...

Ansible 清单文件定义了将由 Ansible 管理的主机(在前面的示例中,这是csr1-2mx1-2)以及如何根据不同的标准将这些设备分组到自定义的组中。这些组由[]定义。这种分组有助于我们定义变量,简化设备之间的隔离以及 Ansible 如何与它们交互。我们如何对设备进行分组是基于我们的用例,因此我们可以根据供应商(Juniper 和 IOS)或功能(核心和边缘)对设备进行分组。

我们还可以使用子代为组构建层次结构,这在清单文件中有所概述。下图显示了如何对主机进行分组以及如何构建组层次结构:

使用 Ansible 变量

Ansible 存储它使用 Ansible 变量管理的节点的信息。Ansible 变量可以在多个位置声明。然而,在观察 Ansible 的最佳实践时,我们将概述 Ansible 为清单文件中声明的节点寻找变量的两个主要部分。

准备好

为了遵循该配方,必须已经定义了一个 Ansible 库存文件,如前面的配方所述。

怎么做...

在清单文件中,我们定义主机并将主机分组。我们现在定义了两个目录,Ansible 在其中搜索组变量和主机变量:

  1. 创建两个文件夹,group_varshost_vars:
$ cd ch1_ansible
$ mkdir group_vars host_vars
  1. group_vars内创建ios.ymljunos.yml文件:
$ touch group_vars/cisco.yml group_vars/juniper.yml
  1. host_vars内部创建mx1.ymlcsr1.yml:
$ touch host_vars/csr1.yml host_vars/mx1.yml
  1. 在所有文件中填充变量,如下所示:
$echo 'hostname: core-mx1' >> host_vars/mx1.yml
$echo 'hostname: core-mx2' >> host_vars/mx2.yml
$echo 'hostname: edge-csr1' >> host_vars/csr1.yml
$echo 'hostname: edge-csr2' >> host_vars/csr2.yml
$echo 'os: ios' >> group_vars/cisco.yml
$echo 'os: junos' >> group_vars/juniper.yml

它是如何工作的...

我们创建了以下目录和文件结构来承载我们的变量,如下图所示:

group_vars目录中的所有文件都包含我们在清单中定义的组的组变量,它们适用于该组中的所有主机。至于host_vars内的文件,包含每个主机的变量。使用这种结构,我们可以将来自多个主机的变量分组到一个特定的组文件中,并且特定于主机的变量将被放在一个特定于该主机的单独文件中。

还有更多...

除了host_varsgroup_vars之外,Ansible 还支持使用其他技术定义变量,包括以下技术:

  • 在游戏中使用vars关键字指定多个变量
  • 使用vars_files定义文件中的变量,并让 Ansible 在运行剧本时从该文件中读取这些变量
  • 使用--e选项在命令行指定变量

除了我们可以指定的用户定义变量之外,Ansible 还有一些默认变量,它是为库存动态构建的。下表列出了一些最常用的变量:

| inventory_hostname | 清单中定义的主机名称(例如,csr1mx1) | |  play_hosts | 剧中所有主持人的名单 | | group_names | 特定主机所属的所有组的列表(例如,对于csr1,这将是【边缘、思科、网络】) |

构建 Ansible 的行动手册

Ansible 行动手册是 Ansible 中的基本元素,它声明了我们希望在托管主机上执行的操作(在清单中指定)。Ansible 行动手册是一个 YAML 格式的文件,它定义了将在我们的托管设备上执行的任务列表。在本食谱中,我们将概述如何编写 Ansible 行动手册,以及如何定义本行动手册所针对的主机。

准备好

为了遵循这个方法,必须已经定义了一个 Ansible 清单文件,以及根据以前的方法创建的所有特定于组和主机的变量文件。

怎么做...

  1. ch1_ansible文件夹内创建一个名为playbook.yml的新文件,并在该文件中合并以下行:
$  cat playbook.yml

---
 - name: Initial Playbook
 hosts: all
 gather_facts: no
 tasks:
 - name: Display Hostname
 debug:
 msg: "Router name is {{ hostname }}"
 - name: Display OS
 debug:
 msg: "{{ hostname }} is running {{ os }}"
  1. 运行行动手册,如下所示:
$ ansible-playbook -i hosts playbook.yml

它是如何工作的...

Ansible 行动手册的结构是一个行动列表,每个行动针对一组特定的主机(在清单文件中定义)。每部剧都可以有一个或多个任务来执行。每个任务运行一个特定的 Ansible 模块,该模块有多个参数。以下截图概述了行动手册的总体结构:

在前面的剧本中,我们引用了我们在前面配方中{{ }}括号内定义的变量。Ansible 从group_varshost_vars读取这些变量,我们在本剧本中使用的模块是debug模块,它显示为终端输出的msg参数中指定的自定义消息。此处显示了行动手册运行情况:

我们使用ansible-playbook命令中的-i选项来指向 Ansible 库存文件,我们将使用该文件作为我们构建库存的来源。

In this playbook, I have used the all keyword to specify all the hosts within the inventory. This is a well-known group name that Ansible dynamically constructs for all hosts within the inventory.

使用 Ansible 的条件句

Ansible 的核心特性之一是条件任务执行。这为我们提供了根据我们指定的条件/测试来控制在给定主机上运行哪些任务的能力。在本食谱中,我们将概述如何配置条件任务执行。

准备好

为了遵循此配方,必须存在一个 Ansible 库存文件,并按照前面的配方进行配置。此外,我们所有主机的 Ansible 变量应该按照前面的方法进行定义。

怎么做...

  1. ch1_ansible文件夹中创建一个名为ansible_cond.yml的新剧本。
  2. 将以下内容放入新的行动手册中,如下所示:
---
 - name: Using conditionals
 hosts: all
 gather_facts: no
 tasks:
 - name: Run for Edge nodes Only
 debug:
 msg: "Router name is {{ hostname }}"
 when: "'edge' in group_names"

 - name: Run for Only MX1 node
 debug:
 msg: "{{ hostname }} is running {{ os }}"
 when:
 - inventory_hostname == 'mx1'
  1. 运行行动手册,如下所示:
$ ansible-playbook -i hosts ansible_cond.yml

它是如何工作的...

Ansible 使用when语句为任务提供条件执行。when语句应用于任务级别,如果when语句中的条件评估为true,则为给定主机执行任务。如果false,则跳过该主机的任务。运行上述行动手册的结果如下所示:

when语句可以采用第一个任务中看到的单个条件,也可以采用第二个任务中看到的条件列表。如果when是一个条件列表,那么所有的条件都需要为真才能执行任务。

In the first task, the when statement is enclosed in "" since the statement starts with a string**.** However, in the second statement, we use a normal when statement with no "" since the when statement starts with a variable name.

请参见...

有关 Ansible 条件的更多信息,请查看以下网址:

https://docs . ansi ble . com/ansi ble/latest/user _ guide/playbooks _ conditionals . html

使用 Ansible 的循环

在某些情况下,我们需要在 Ansible 剧本中运行一个任务来循环一些数据。Ansible 的循环允许我们多次循环一个变量(字典或列表)来实现这个行为。在这个食谱中,我们将概述如何使用 Ansible 的循环。

准备好

为了遵循此配方,必须存在并配置一个 Ansible 库存文件,如前面的配方所述。

怎么做...

  1. ch1_ansible文件夹中创建一个名为ansible_loops.yml的新剧本。
  2. group_vars/cisco.yml文件中,包含以下内容:
snmp_servers:
 - 10.1.1.1
 - 10.2.1.1
  1. group_vars/juniper.yml文件中,包含以下内容:
users:
 admin: admin123
 oper: oper123
  1. ansible_loops.yml文件中,包含以下内容:
---
 - name: Ansible Loop over a List
 hosts: cisco
 gather_facts: no
 tasks:
 - name: Loop over SNMP Servers
 debug:
 msg: "Router {{ hostname }} with snmp server {{ item }}"
 loop: "{{ snmp_servers}}"

 - name: Ansible Loop over a Dictionary
 hosts: juniper
 gather_facts: no
 tasks:
 - name: Loop over Username and Passowrds
 debug:
 msg: "Router {{ hostname }} with user {{ item.key }} password {{ item.value }}"
 with_dict: "{{ users}}"
  1. 运行行动手册,如下所示:
$ ansible-playbook ansible_loops.yml -i hosts

它是如何工作的..

Ansible 支持在两个主要的可迭代数据结构上循环:列表和字典。当我们需要遍历列表时,我们使用loops关键字(snmp_servers是列表数据结构),当我们遍历字典时,我们使用with_dicts(users是字典数据结构,其中用户名是关键字,密码是值)。在这两种情况下,我们使用item关键字来指定当前迭代中的值。在with_dicts的情况下,我们使用item.key获得密钥,使用item.value获得值。

上一次行动手册的输出如下所示:

请参见...

有关不同 Ansible循环构造**的更多信息,请参考以下网址:

*https://docs . ansi ble . com/ansi ble/latest/user _ guide/playbooks _ loops . html

用可隐藏的金库保护机密

当我们处理需要在我们的 Ansible 行动手册中引用的敏感材料时,例如密码,我们不应该以纯文本形式保存这些数据。Ansible Vault 提供了一种加密此数据的方法,因此可以在播放手册运行时安全地解密和访问。在本食谱中,我们将概述如何使用 Ansible Vault 来保护 Ansible 中的敏感信息。

怎么做...

  1. 创建一个名为decrypt_passwd的新文件,如图所示:
$ echo 'strong_password' > decrypt_passwd
  1. 使用ansible-vault创建一个名为secrets的新文件,如下所示:
$ ansible-vault create --vault-id=decrypt_passwd secrets
  1. 向这个新的secrets文件中添加以下变量:
ospf_password: ospf_P@ssw0rD
bgp_password: BGP_p@ssw0rd
  1. 创建一个名为ansible_vault.yml的新剧本,如下所示:
---
 - name: Using Ansible vault
 hosts: all
 gather_facts: no
 vars_files:
 - secrets
 tasks:
 - name: Output OSPF passowrd
 debug:
 msg: "Router {{ hostname }} ospf Password {{ ospf_password }}"
 when: inventory_hostname == 'csr1'

 - name: Output BGP passowrd
 debug:
 msg: "Router {{ hostname }} BGP Password {{ bgp_password }}"
 when: inventory_hostname == 'mx1'
  1. 运行行动手册,如下所示:
$ ansible-playbook --vault-id=decrypt_passwd ansible_vault.yml -i hosts

它是如何工作的..

我们使用ansible-vault命令创建一个新文件,该文件使用-- vault-id指定的密钥进行加密。我们将这个密钥/密码放在另一个文件(在我们的例子中称为decrypt_passwd)中,并将这个文件作为参数传递给vault-id。在这个文件中,我们可以放置任意多的变量。最后,我们使用vars_files将该文件作为可变文件包含在剧本中。以下是机密文件的内容,以防我们试图在不解密的情况下读取它:

$ cat secrets
$ANSIBLE_VAULT;1.1;AES256
61383264326363373336383839643834386661343630393965656135666336383763343938313963
3538376230613534323833356237663532666363626462640a663839396230646634353839626461
31336461386361616261336534663137326265363261626536663564623764663861623735633865
3033356536393631320a643561623635363830653236633833383531366166326566623139633838
32633335616663623761313630613134636635663865363563366564313365376431333461623232
34633838333836363865313238363966303466373065356561353638363731616135386164373263
666530653334643133383239633237653034

为了让 Ansible 解密该文件,我们必须通过--vault-id选项提供解密密码(在本例中存储在decrypt_passwd文件中)。当我们运行ansible-playbook时,我们必须提供这个解密密码,否则ansible-playbook会失败,如下图所示:

### Running the Ansible playbook without --vault-id 
$ansible-playbook ansible_vault.yml -i hosts
ERROR! Attempting to decrypt but no vault secrets found

还有更多...

如果我们不想在文本文件中指定加密/解密密码,我们可以在运行剧本时使用--ask-vault-passansible-playbook命令来输入密码,如下所示:

### Running the Ansible playbook with --ask-vault-pass
$ansible-playbook ansible_vault.yml -i hosts --ask-vault-pass
Vault password:

将 Jinja2 与 Ansible 一起使用

Jinja2 是一个强大的 Python 模板引擎,并且得到了 Ansible 的支持。它还用于生成任何基于文本的文件,如 HTML、CSV 或 YAML。我们可以利用带有 Ansible 变量的 Jinja2 来为网络设备生成自定义配置文件。在这个食谱中,我们将概述如何使用带有 Ansible 的 Jinja2 模板。

准备好

为了遵循该配方,必须存在一个 Ansible 库存文件,并按照前面的配方进行配置。

怎么做...

  1. group_vars目录下创建一个名为network.yml的新文件:
$ cat group_vars/network.yml

---
ntp_servers:
 - 172.20.1.1
 - 172.20.2.1
  1. 新建templates目录,新建ios_basic.j2文件,内容如下:
$ cat templates/ios_basic.j2
hostname {{ hostname }}
!
{% for server in ntp_servers %}
ntp {{ server }}
{% endfor %}
!
  1. templates目录中创建新的junos_basic.j2文件,内容如下:
$ cat templates/junos_basic.j2
set system host-name {{ hostname }}
{% for server in ntp_servers %}
set system ntp server {{ server }}
{% endfor %}
  1. 创建名为ansible_jinja2.yml的新剧本,内容如下:
---
 - name: Generate Cisco config from Jinja2
 hosts: localhost
 gather_facts: no
 tasks:
 - name: Create Configs Directory
 file: path=configs state=directory

 - name: Generate Cisco config from Jinja2
 hosts: cisco
 gather_facts: no
 tasks:
 - name: Generate Cisco Basic Config
 template:
 src: "templates/ios_basic.j2"
 dest: "configs/{{inventory_hostname}}.cfg"
 delegate_to: localhost

 - name: Generate Juniper config from Jinja2
 hosts: juniper
 gather_facts: no
 tasks:
 - name: Generate Juniper Basic Config
 template:
 src: "templates/junos_basic.j2"
 dest: "configs/{{inventory_hostname}}.cfg"
 delegate_to: localhost
  1. 运行 Ansible 行动手册,如下所示:
$ ansible-playbook -i hosts ansible_jinja2.yml

它是如何工作的...

我们创建了network.yml文件,以便将应用于该组下所有设备的所有变量分组。之后,我们创建两个 Jinja2 文件,一个用于 Cisco IOS 设备,另一个用于 Juniper 设备。在每个 Jinja2 模板中,我们使用{{}}引用 Ansible 变量。我们还使用由 Jinja2 模板引擎支持的for循环构造{% for server in ntp_servers %} ,以便循环遍历ntp_servers变量(这是一个列表)来访问该列表中的每个项目。

Ansible 提供了采用两个参数的template模块:

  • src:这里引用了 Jinja2 模板文件。
  • dest:指定将要生成的输出文件。

在我们的例子中,我们使用{{inventory_hostname}}变量来使输出配置文件对于我们清单中的每台路由器都是唯一的。

默认情况下,template模块在远程管理的节点上创建输出文件。然而,在我们的例子中这是不可能的,因为被管理的设备是网络节点。因此,我们使用delegate_to: localhost选项在 Ansible 控制机器上本地运行该任务。

剧本中的第一个剧本创建configs目录来存储网络设备的配置文件。第二部在思科设备上运行模板模块,第三部在 Juniper 设备上运行template任务。

以下是其中一台思科设备的配置文件:

$ cat configs/csr1.cfg
hostname edge-csr1
!
ntp 172.20.1.1
ntp 172.20.2.1
!

这是一个 Juniper 设备的配置文件:

$ cat configs/mx1.cfg
set system host-name core-mx1
set system ntp server 172.20.1.1
set system ntp server 172.20.2.1

请参见...

有关 Ansible 模板模块的更多信息,请访问以下网址:

https://docs . ansi ble . com/ansi ble/latest/modules/template _ module . html

使用 Ansible 的过滤器

Ansible 的过滤器主要源自 Jinja2 过滤器,所有 Ansible 过滤器都用于转换和操作数据(Ansible 的变量)。除了 Jinja2 过滤器之外,Ansible 还实现了自己的过滤器来增强 Jinja2 过滤器,同时还允许用户定义自己的自定义过滤器。在本食谱中,我们将概述如何配置和使用 Ansible 过滤器来操作我们的输入数据。

怎么做...

  1. 安装python3-pip和 Python 的netaddr库,因为我们将使用 Ansible IP 过滤器,这需要 Python 的netaddr库:
# On ubuntu
$ sudo apt-get install python3-pip

# On CentOS
$ sudo yum install python3-pip

$ pip3 install netaddr
  1. 创建一个名为ansible_filters.yml的新 Ansible 行动手册,如下所示:
---
 - name: Ansible Filters
 hosts: csr1
 vars:
 interfaces:
 - { port: FastEthernet0/0, prefix: 10.1.1.0/24 }
 - { port: FastEthernet1/0, prefix: 10.1.2.0/24 }
 tasks:
 - name: Generate Interface Config
 blockinfile:
 block: |
 hostname {{ hostname | upper }}
 {% for intf in interfaces %}
 !
 interface {{ intf.port }}
 ip address {{intf.prefix | ipv4(1) | ipv4('address') }} {{intf.prefix | ipv4('netmask') }}
 !
 {% endfor %}
 dest: "configs/csr1_interfaces.cfg"
 create: yes
 delegate_to: localhost

它是如何工作的...

首先,我们使用blockinfile模块在 Ansible 控制机上创建一个新的配置文件。该模块与template模块非常相似。但是,我们可以在block选项的模块中直接编写 Jinja2 表达式。我们使用剧本中的vars参数定义了一个名为interfaces的新变量。这个变量是一个列表数据结构,其中列表中的每个项目都是一个字典数据结构。这种嵌套数据结构指定了每个接口上使用的 IP 前缀。

在 Jinja2 表达式中,我们可以看到我们使用了许多过滤器,如下所示:

  • {{ hostname | upper}} : upper是一个 Jinja2 过滤器,将输入字符串的所有字母转换为大写。这样,我们将主机名变量的值传递给这个过滤器,输出将是这个值的大写版本。
  • {{intf.prefix | ipv4(1) | ipv4('address') }}:这里,我们使用 Ansible IP 地址过滤器两次。ipv4(1)取一个输入的 IP 前缀,输出该前缀中的第一个 IP 地址。然后我们使用另一个 IP 地址过滤器,ipv4('address'),以便只获取 IP 前缀的 IP 地址部分。所以在我们的例子中,我们取10.1.1.0/24并输出10.1.1.1作为第一个接口。
  • {{intf.prefix | ipv4('netmask') }}:这里,我们使用 Ansible IP 地址过滤器来获取 IP 地址前缀的网络掩码,所以在我们的例子中,我们获取/24子网并将其转换为255.255.255.0

本剧本运行后csr1路由器的输出文件如下所示:

$ cat configs/csr1_interfaces.cfg
# BEGIN ANSIBLE MANAGED BLOCK
hostname EDGE-CSR1
!
interface FastEthernet0/0
 ip address 10.1.1.1 255.255.255.0
!
!
interface FastEthernet1/0
 ip address 10.1.2.1 255.255.255.0
!
# END ANSIBLE MANAGED BLOCK

使用 Ansible 标签

Ansible Tags 是一个强大的工具,它允许我们在大型 Ansible 行动手册中标记特定的任务,并为我们提供了根据我们指定的标记选择在给定行动手册中运行哪些任务的灵活性。在本食谱中,我们将概述如何配置和使用 Ansible 标签。

怎么做...

  1. 创建一个名为ansible_tags.yml的新 Ansible 行动手册,如下所示:
---
 - name: Using Ansible Tags
 hosts: cisco
 gather_facts: no
 tasks:
 - name: Print OSPF
 debug:
 msg: "Router {{ hostname }} will Run OSPF"
 tags: [ospf, routing]

 - name: Print BGP
 debug:
 msg: "Router {{ hostname }} will Run BGP"
 tags:
 - bgp
 - routing

 - name: Print NTP
 debug:
 msg: "Router {{ hostname }} will run NTP"
 tags: ntp
  1. 运行行动手册,如下所示:
$ ansible-playbook ansible_tags.yml -i hosts --tags routing
  1. 再次运行行动手册,这次使用标签,如下所示:
$ ansible-playbook ansible_tags.yml -i hosts --tags ospf

$ ansible-playbook ansible_tags.yml -i hosts --tags routing

它是如何工作的...

我们可以使用标签来标记给定标签的任务和游戏,以便使用它来控制执行哪些任务或游戏。这让我们在开发行动手册时有更多的控制权,从而可以运行相同的行动手册。但是,每次运行时,我们都可以控制正在部署的内容。在本食谱的示例剧本中,我们将任务标记为 OSPF、BGP 或 NTP,并将routing标记应用于 OSPF 和 BGP 任务。这允许我们有选择地运行行动手册中的任务,如下所示:

  • 如果没有指定标记,这将运行行动手册中的所有任务,行为没有变化,如下图所示:

  • 使用ospf标记,我们将只运行任何用该标记标记的任务,如下所示:

  • 使用routing标记,我们将运行所有用该标记标记的任务,如下所示:

请参见...

有关 Ansible 标签的更多信息,请访问以下网址:

https://docs . ansi ble . com/ansi ble/latest/user _ guide/playbooks _ tags . html

自定义 Ansible 的设置

Ansible 有许多设置,可以使用名为ansible.cfg的配置文件进行调整和控制。该文件有多个选项,可以控制 Ansible 的许多方面,包括 Ansible 的外观以及它如何连接到托管设备。在本食谱中,我们将概述如何调整这些默认设置。

怎么做...

  1. 新建一个名为ansible.cfg的文件,如下图所示:
[defaults]
inventory=hosts
vault_password_file=decryption_password
gathering=explicit

它是如何工作的...

默认情况下,Ansible 的设置由位于/etc/ansible目录中的ansible.cfg文件控制。这是 Ansible 的默认配置文件,用于控制 Ansible 如何与托管节点交互。我们可以直接编辑这个文件。但是,这将影响我们将在 Ansible 控制机器上使用的任何行动手册,以及该机器上的任何用户。一个更灵活和定制的选项是在项目目录中包含一个名为ansible.cfg的文件,这包括所有需要从默认参数中修改的选项。在前面的示例中,我们仅概述了这些选项的一小部分,如下所示:

  • inventory : 该选项修改 Ansible 搜索的默认库存文件,以查找其库存(默认为/etc/ansible/hosts)。我们调整此选项是为了让 Ansible 使用我们的库存文件,并在每次运行行动手册时停止使用-i操作符来指定我们的库存。

  • vault_password_file:该选项设置具有加密和解密ansible-vault机密的机密密码的文件。当使用ansible-vault加密变量时,该选项无需使用--vault-id操作符运行 Ansible 行动手册。

  • gathering = explicit:默认情况下,Ansible 运行一个设置模块,在剧本运行时收集关于被管理节点的事实。该设置模块与网络节点不兼容,因为该模块需要托管节点上的 Python 解释器。通过将事实收集设置为explicit,我们禁用了这个默认行为。

请参见...

有关 Ansible 配置设置的更多信息,请访问以下网址:

https://docs . ansi ble . com/ansi ble/latest/reference _ appendals/config . html # ansi ble-配置-设置

使用 Ansible 角色

Ansible Roles 促进了代码的可重用性,并提供了一种简单的方法,以一种可以共享和消费的简单方式打包 Ansible 代码。Ansible 角色是所有必需的 Ansible 任务、处理程序和 Jinja2 模板的集合,它们以特定的结构打包在一起。应该设计一个角色来交付特定的功能/任务。在本食谱中,我们将概述如何创建 Ansible 角色以及如何在我们的行动手册中使用它。

怎么做...

  1. ch1_ansible文件夹内,新建一个名为roles的文件夹,新建一个名为basic_config的角色,如下图所示:
$ mkdir roles
$ cd roles
$ ansible-galaxy init basic_config
  1. 用以下变量更新basic_config/vars/main.yml文件:
$ cat roles/basic_config/vars/main.yml

---
config_dir: basic_config
  1. 用以下任务更新basic_config/tasks/main.yml文件:
$ cat roles/basic_config/tasks/main.yml

---
 - name: Create Configs Directory
 file:
 path: "{{ config_dir }}"
 state: directory
 run_once: yes

 - name: Generate Cisco Basic Config
 template:
 src: "{{os}}.j2"
 dest: "{{config_dir}}/{{inventory_hostname}}.cfg"
  1. basic_config/templates文件夹内,创建以下结构:
$ tree roles/basic_config/templates/

roles/basic_config/templates/
├── ios.j2
└── junos.j2

$ cat roles/basic_config/templates/ios.j2
hostname {{ hostname }}
!
{% for server in ntp_servers %}
ntp {{ server }}
{% endfor %}
  1. 创建一个新的行动手册pb_ansible_role.yml,使用以下内容来扮演我们的角色:
$ cat pb_ansible_role.yml
---
 - name: Build Basic Config Using Roles
 hosts: all
 connection: local
 roles:
 - basic_config

它是如何工作的...

在这个食谱中,我们从在主文件夹中创建roles目录开始。默认情况下,使用角色时,Ansible 将按以下顺序在以下位置查找角色:

  • 当前工作目录内的roles文件夹
  • /etc/ansible/roles

因此,我们在当前工作目录(ch1_ansible)中创建roles文件夹,以便托管我们将在该文件夹中创建的所有角色。我们使用带有init选项和角色名称(basic_config)的ansible-galaxy命令创建角色,这将在我们的roles文件夹中创建以下角色结构:

$ tree roles/
roles/
└── basic_config
 ├── defaults
 │   └── main.yml
 ├── files
 ├── handlers
 │   └── main.yml
 ├── meta
 │   └── main.yml
 ├── README.md
 ├── tasks
 │   └── main.yml
 ├── templates
 ├── tests
 │   ├── inventory
 │   └── test.yml
 └── vars
 └── main.yml

从前面的输出可以看出,这个文件夹结构是使用ansible-galaxy命令创建的,该命令按照最佳实践角色布局构建角色。并非所有这些文件夹都需要具有我们可以使用的功能角色,下面的列表概述了常用的主要文件夹:

  • tasks文件夹:这里包含main.yml文件,里面列出了我们使用这个角色时应该执行的所有任务。
  • templates文件夹:这包含了我们将作为这个角色的一部分使用的所有 Jinja2 模板。
  • vars文件夹:这包含了我们想要定义的所有变量,以及我们将在角色中使用的变量。运行剧本时评估变量时,vars文件夹中的变量具有很高的优先级。
  • handlers文件夹:包含main.yml文件,该文件包含作为该角色一部分运行的所有处理程序。

我们创建的角色只有一个目的,那就是为我们的设备构建基本配置。为了完成这项任务,我们需要定义一些可执行的任务,并使用大量 Jinja2 模板来生成设备的基本配置。我们在tasks/main.yml文件中列出了所有需要运行的任务,并且在templates文件夹中包含了所有必要的 Jinja2 模板。我们在vars文件夹中定义了我们将在角色中使用的任何必要变量。

我们创建了一个新的行动手册,将使用我们的新角色来生成设备的配置。我们在roles参数中将所有我们想要运行的角色称为我们剧本的一部分。在我们的例子中,我们有一个想要运行的角色,那就是basic_config角色。

一旦我们运行我们的行动手册,我们可以看到一个名为basic_config的新目录被创建,其内容如下:

$ tree basic_config/
basic_config/
├── csr1.cfg
├── csr2.cfg
├── mx1.cfg
└── mx2.cfg    

请参见

有关 Ansible 角色的更多信息,请访问以下网址:

https://docs . ansi ble . com/ansi ble/latest/user _ guide/playbooks _ reuse _ roles . html*