网络自动化秘籍(三)
原文:
zh.annas-archive.org/md5/9FD2C03E57DE97FDB15C42452017B0E9译者:飞龙
第五章:使用 F5 LTM 和 Ansible 自动化应用交付
在本章中,我们将概述如何自动化运行作为负载均衡器(LBs)或本地流量管理器(LTM)设备的 F5 BIG-IP 平台。我们将探讨如何使用 Ansible 与 F5 LTM 节点交互,以及如何通过各种 Ansible 模块对这些设备进行加速应用部署。我们将以以下样本网络图为基础进行说明。该图显示了连接到直流(DC)交换机的单个 F5 LTM 节点:
本章主要涵盖的配方如下:
-
构建 Ansible 网络清单
-
连接和认证到 BIG-IP 设备
-
在 BIG-IP 设备上配置通用系统选项
-
在 BIG-IP 设备上配置接口和干线
-
在 BIG-IP 设备上配置虚拟局域网(VLANs)和自身互联网协议(self-IPs)
-
在 BIG-IP 设备上配置静态路由
-
在 BIG-IP 设备上部署节点
-
在 BIG-IP 设备上配置负载均衡池
-
在 BIG-IP 设备上配置虚拟服务器
-
从 BIG-IP 节点检索操作数据
技术要求
本章中使用的所有代码都可以在以下 GitHub 存储库中找到:github.com/PacktPublishing/Network-Automation-Cookbook/tree/master/ch5_f5。
本章基于以下软件版本:
-
运行 CentOS 7 的 Ansible 机器
-
Ansible 2.9
-
运行 BIG-IP 13.1.1,Build 0.0.4 final 的 F5 BIG-IP 设备
查看以下视频以查看代码的实际操作:
构建 Ansible 网络清单
在这个示例中,我们将概述如何构建和组织我们的 Ansible 清单,以描述我们的样本 F5 BIG-IP 节点。构建 Ansible 清单是告诉 Ansible 如何连接到受管设备的强制步骤。
做好准备
我们将创建一个新的文件夹,用于存放本章中创建的所有文件。新文件夹名为ch5_f5。
如何操作...
- 在新文件夹
ch5_f5中,我们创建一个hosts文件,其中包含以下内容:
$ cat hosts
[ltm]
ltm01 Ansible_host=172.20.1.34
- 创建一个
Ansible.cfg文件,如下所示:
$ cat Ansible.cfg
[defaults]
inventory=hosts
retry_files_enabled=False
gathering=explicit
host_key_checking=False
工作原理...
由于我们的网络拓扑中只有一个 LTM 节点,这简化了我们的 Ansible 清单文件。在我们的hosts文件中,我们创建一个单一组(称为ltm),并在其中指定一个单一节点,名为ltm01。我们使用Ansible_host参数指定节点的管理 IP 地址。
BIG-IP 设备的管理端口必须配置此 IP 地址,并且 Ansible 控制机与 BIG-IP 节点之间的 IP 连接必须通过此管理端口建立。
最后,我们创建Ansible.cfg文件并配置它指向我们的hosts文件,以用作 Ansible 清单文件。我们禁用了 setup 模块,在针对网络节点运行 Ansible 时不需要它。
连接和认证到 BIG-IP 设备
在这个示例中,我们将概述如何通过 BIG-IP 设备暴露的表述状态转移(REST)API 从 Ansible 连接到 BIG-IP 节点,以便从 Ansible 开始管理这些设备。我们将使用用户名和密码来对我们拓扑中的 BIG-IP 节点进行身份验证。
做好准备
为了按照这个示例进行操作,应该按照前面的示例构建一个 Ansible 清单文件。Ansible 控制机和网络中所有设备之间必须建立 IP 可达性。
如何操作...
-
在
ch5_f5文件夹中,创建一个group_vars文件夹。 -
创建一个新的
group_vars/all.yml文件,其中包含以下连接参数设置:
conn_parameters:
user: admin
password: admin
server: "{{ Ansible_host }}"
server_port: 443
validate_certs: no
admin_passwd: NewP@sswd
users:
- name: Ansible
passwd: Ansible123
role: all:admin
state: present
- 创建一个名为
pb_f5_onboard.yml的新的 playbook,其中包含以下任务来创建新的系统用户:
- name: Onboarding a New LTM
hosts: ltm01
connection: local
tasks:
- name: "P1T1: Create new Users"
bigip_user:
username_credential: "{{ item.name }}"
password_credential: "{{ item.passwd }}"
partition_access: "{{ item.role }}"
state: "{{ item.state | default('present')}}"
provider: "{{ conn_parameters }}"
loop: "{{ users }}"
- 使用以下任务更新
pb_f5_onboard.ymlplaybook 以更新管理员用户帐户:
- name: "P1T1: Update admin Password"
bigip_user:
username_credential: admin
password_credential: "{{ admin_passwd }}"
state: present
provider: "{{ conn_parameters }}"
它是如何工作的...
Ansible 使用 F5 LTM 节点上的 REST API 来管理 BIG-IP 节点。Ansible 建立一个 HTTPS 连接到 BIG-IP 节点,并将其用作调用 BIG-IP 节点上的 REST API 的传输机制。为了与 BIG-IP 系统建立 HTTPS 连接,我们需要提供一些参数,以便 Ansible 启动并与 BIG-IP 节点建立连接。这些参数包括以下内容:
-
用于认证 BIG-IP REST API 的用户名/密码
-
IP 地址和端口,通过这些我们可以访问 BIG-IP 节点上的 REST API 端点
-
我们验证通过 HTTPS 会话协商的 BIG-IP 节点的证书
我们将所有这些参数包含在一个名为conn_parameters的字典中,并将其包含在group_vars/all.yml文件中,以便应用于任何 BIG-IP 节点。
默认情况下,新的 LTM 设备使用admin/admin默认用户名和密码用于图形用户界面(GUI)和 REST API 访问。我们将这些凭据作为conn_parameters字典中的用户和密码变量,并将Ansible_host变量指定为可以通过端口443建立 REST API 的 IP 地址。最后,我们禁用证书验证,因为 BIG-IP 节点上的证书是自签名的。
我们创建一个名为users的新变量,其中包含我们要在 LTM 上配置的所有新用户,以及他们的角色/权限。在这种情况下,我们希望为 Ansible 用户在 LTM 节点上的所有分区提供管理权限。
我们为新的 LTM 节点创建一个新的 playbook。在第一个任务中,我们使用bigip_user模块创建新用户,并使用provider属性提供参数以建立 HTTPS 连接。我们循环遍历users变量中的所有用户以进行配置。
第二个任务还使用bigip_user模块来更新 LTM 上的默认admin配置,并将此默认密码更改为admin_passwd变量中指定的新密码。
在 playbook 级别上,我们将连接设置为local。这是因为我们将从 Ansible 控制机建立 HTTPS 连接,并且我们希望阻止 Ansible 使用Secure Shell(SSH)连接到 LTM 节点。
以下截图显示了在 BIG-IP 节点上创建的新的 Ansible 用户:
以下截图显示了使用 playbook 创建的 Ansible 用户的详细信息:
出于简单起见,我们使用明文密码;但是,永远不应该使用明文密码。应该使用 Ansible Vault 来保护密码。
还有更多...
在添加新的 Ansible 用户后,我们使用新创建的用户更新conn_parameters字典。我们可以使用这个用户开始管理 LTM 节点,如下所示:
$ cat group_vars/all.yml
conn_parameters:
user: Ansible
password: Ansible123
server: "{{ Ansible_host }}"
server_port: 443
validate_certs: no
< -- Output Omitted for brevity -->
在 BIG-IP 设备上配置通用系统选项
在这个步骤中,我们将概述如何在 BIG-IP 节点上配置一些基本的系统选项,如主机名、域名系统(DNS)和网络时间协议(NTP)。我们将了解如何使用各种可用的 Ansible 模块设置所有这些系统级参数。
准备工作
为了按照这个步骤进行操作,假定已经设置了 Ansible 清单。Ansible 和 BIG-IP 节点之间已经建立了 IP 连接,并且具有正确的用户凭据。
如何做...
- 使用以下系统级参数更新
group_vars/all.yml文件:
$ cat group_vars/all.yml
< -- Output Omitted for brevity -->
domain: lab.net
nms_servers:
- 172.20.1.250
- 创建一个名为
tasks的新文件夹,并创建一个名为f5_system.yml的文件,其中包含以下内容:
$ cat tasks/f5_system.yml
---
- name: "Setup BIG-IP Hostname"
bigip_hostname:
hostname: "{{ inventory_hostname }}.{{ domain }}"
provider: "{{ conn_parameters }}"
- name: "Setup BIG-IP DNS Servers"
bigip_device_dns:
ip_version: '4'
name_servers: "{{ nms_servers }}"
provider: "{{ conn_parameters }}"
- name: "Setup BIG-IP NTP Servers"
bigip_device_ntp:
ntp_servers: "{{ nms_servers }}"
provider: "{{ conn_parameters }}"
- 在
pb_f5_onboard.yml文件中,添加以下突出显示的任务:
$ cat pb_f5_onboard.yml
< -- Output Omitted for brevity -->
- name: "P1T3: Configure System Parameters"
import_tasks: "tasks/f5_system.yml"
tags: system
它是如何工作的...
为了在 BIG-IP 节点上配置各种系统参数,我们为每个任务使用单独的模块。我们将所有这些任务分组在一个名为f5_system.yml的文件夹下,并在此文件夹中使用三个单独的任务/模块,如下所示:
-
bigip_hostname来设置主机名 -
bigip_device_dns来设置 BIG-IP 节点将使用的 DNS 服务器 -
bigip_device_ntp来设置 BIG-IP 节点上的 NTP 服务器
所有这些模块都使用conn_parameters字典来正确设置如何与 BIG-IP 节点的 REST API 进行通信。在我们的示例拓扑中,我们使用单个服务器作为 DNS 和 NTP。我们在group_vars/all.yml文件中使用nms_servers变量来描述它,以应用于我们 Ansible 清单中的所有节点。
为了配置主机名,我们需要为设备提供一个完全合格的域名(FQDN)。因此,我们在group_vars/all.yml文件中再次配置我们的域,并与设备名称一起使用以设置其主机名。
运行此 playbook 后,我们可以看到配置已应用到 BIG-IP 节点。以下截图显示主机名已正确配置:
NTP 配置已正确部署,如下截图所示:
DNS 已正确配置,如下截图所示:
在 BIG-IP 设备上配置接口和干线
在这个步骤中,我们将概述如何在 BIG-IP 设备上设置干线。BIG-IP 节点上的干线端口用于通过将多个接口组合成单个逻辑接口来为设备提供增加的冗余性。这与传统网络供应商中的端口通道非常相似。
准备工作
为了按照这个步骤进行,假定已经设置了 Ansible 清单。Ansible 和 BIG-IP 节点之间已经建立了 IP 连接,并且具有正确的用户凭据。
如何做...
- 创建一个
host_vars文件夹,并创建一个名为ltm01.yml的文件,其中包含以下内容:
$ cat host_vars/ltm01.yml
----
phy_interfaces:
- 1.1
- 1.2
trunks:
- name: po1
members: "{{ phy_interfaces }}"
- 在
tasks文件夹下,添加一个名为f5_interfaces.yml的新文件,其中包含以下内容:
$ cat tasks/f5_interfaces.yml
---
- name: Create a Port channel on BIG-IP
bigip_trunk:
name: "{{ item.name}}"
interfaces: "{{ item.members }}"
link_selection_policy: maximum-bandwidth
frame_distribution_hash: destination-mac
lacp_enabled: no
provider: "{{ conn_parameters }}"
state: present
loop: "{{ trunks }}"
- 使用以下新任务更新
pb_f5_onboard.ymlplaybook:
$ cat pb_f5_onboard.yml
< -- Output omitted for brevity -->
- name: "P1T4: Configure Interfaces"
import_tasks: "tasks/f5_interfaces.yml"
tags: intfs
它是如何工作的...
我们在host_vars文件夹下的一个名为ltm01.yml的文件中为 LTM 设备定义特定于主机的数据。在这个文件中,我们在phy_interfaces变量下定义了 LTM 节点上的物理接口。我们定义了另一个名为trunks的变量,以定义设备上可用的干线。在trunks变量中,我们引用phy_interfaces变量,以限制数据重复。
在f5_interfaces.yml任务文件中,我们添加了一个新任务,使用bigip_trunk模块在 BIG-IP 节点上配置所需的干线。我们循环遍历trunks数据结构,以配置所有所需的干线端口。在这个任务中,我们提供了不同的参数来调整干线属性(例如禁用链路聚合控制协议(LACP))并设置正确的方法来在干线端口之间分发帧。
运行 playbook 后,我们可以看到所需的干线接口已经配置,如下截图所示:
另请参阅...
有关bigip_trunk Ansible 模块的更多信息,以及如何在 BIG-IP 节点上部署干线端口的不同选项,请参阅以下网址:docs.Ansible.com/Ansible/latest/modules/bigip_trunk_module.html。
在 BIG-IP 设备上配置 VLAN 和自 IP
在本教程中,我们将概述如何在 BIG-IP 节点上配置 VLAN。 BIG-IP 节点上的 VLAN 对于通过 BIG-IP LTM 节点托管的不同应用程序的流量分离至关重要。它们对于指定外部(面向互联网)和内部(面向服务器)域至关重要。我们还将概述如何在我们配置的 VLAN 接口上分配 IP 地址。
准备就绪
为了按照本教程进行操作,假定已经设置了 Ansible 清单。 Ansible 与 BIG-IP 节点之间已经建立了 IP 连接,并且具有正确的用户凭据。由于此设置中的所有 VLAN 都将部署在干线端口上,因此我们需要根据先前的教程已经配置好干线端口。
操作步骤...
- 在
host_vars文件夹下的host_vars/ltm01.yml文件中使用以下 VLAN 数据进行更新:
$ cat host_vars/ltm01.yml
< -- Output Omitted for brevity -->
vlans:
- vlan: 100
description: Extrnal VLAN (Internet)
ip: 10.1.100.254/24
tagged_intf: po1
- vlan: 10
description: Server VLAN10 (Internal)
ip: 10.1.10.254/24
tagged_intf: po1
- 在
tasks文件夹下的f5_interfaces.yml文件中更新任务,以配置 VLAN,如下所示:
$ cat tasks/f5_interfaces.yml
< -- Output Omitted for brevity -->
- name: Create VLANs on BIG-IP
bigip_vlan:
tagged_interfaces: "{{ item.tagged_intf }}"
name: "VL{{item.vlan}}"
description: "{{ item.description }}"
tag: "{{item.vlan}}"
provider: "{{ conn_parameters }}"
state: present
loop: "{{ vlans }}"
- 在
tasks文件夹下的f5_interfaces.yml文件中更新任务,以在相应的 VLAN 上配置 IP 地址,如下所示:
$ cat tasks/f5_interfaces.yml
< -- Output Omitted for brevity -->
- name: Provision IP addresses on BIG-IP
bigip_selfip:
address: "{{ item.ip | ipv4('address') }}"
name: "VL{{ item.vlan }}_IP"
netmask: "{{ item.ip | ipv4('netmask') }}"
vlan: "VL{{ item.vlan }}"
provider: "{{ conn_parameters }}"
state: present
loop: "{{ vlans }}"
工作原理...
我们在host_vars/ltm01.yml中添加vlans数据结构,以声明我们需要在 LTM 节点上配置的所有 VLAN,以及与该 VLAN 相关联的 IP 地址。
我们使用bigip_vlan模块更新f5_interfaces.yml文件,以在 BIG-IP 节点上配置 VLAN,并循环遍历vlans数据结构以提取设置所需 VLAN 的所有必要参数。接下来,我们使用bigip_selfip Ansible 模块添加另一个任务,以在 VLAN 上部署 IP 地址。
再次运行 playbook 后,我们可以看到 BIG-IP 节点上的 VLAN 和自身 IP,如下截图所示:
正确的 IP 地址已根据以下截图正确配置在 VLAN 接口上:
另请参阅...
有关如何在 BIG-IP 节点上部署 VLAN 和自身 IP 的更多选项,请参考以下网址:
bigip-vlan
docs.ansible.com/ansible/latest/modules/bigip_vlan_module.html
bigip-selfip
docs.Ansible.com/Ansible/latest/modules/bigip_selfip_module.html#bigip-selfip-module
在 BIG-IP 设备上配置静态路由
在在 BIG-IP 设备上部署 VLAN 和 IP 地址后,我们需要配置 BIG-IP 节点上的路由,以便到达外部目的地。我们在我们的拓扑中使用静态路由,以在 LTM 节点上配置所需的路由。在本教程中,我们将概述如何在 BIG-IP 设备上配置静态路由。
准备就绪
为了按照本教程进行操作,假定已经设置了 Ansible 清单,并且 Ansible 与 BIG-IP 节点之间已经建立了 IP 连接,并且具有正确的用户凭据。此外,我们需要根据先前的教程在 BIG-IP 节点上部署 VLAN 和 IP 地址。
操作步骤...
- 使用以下路由数据更新
host_vars/ltm01.yml文件:
$ cat host_vars/ltm01.yml
< -- Output Omitted for brevity -->
routes:
- dst: 0.0.0.0/0
gw: 10.1.100.1
name: default_route
- 使用以下任务更新
pb_f5_onboard.yml文件:
$ cat pb_f5_onboard.yml
< -- Output Omitted for brevity -->
- name: "P1T5: Setup External Routing"
bigip_static_route:
destination: "{{ item.dst.split('/')[0] }}"
netmask: "{{item.dst | ipv4('prefix')}}"
gateway_address: "{{ item.gw }}"
name: "{{ item.name }}"
provider: "{{ conn_parameters }}"
loop: "{{ routes }}"
tags: routing
工作原理...
我们在host_vars/ltm01.yml文件下添加routes数据结构,以声明需要在 LTM 节点上配置的所有静态路由。
我们使用bigip_static_route模块更新pb_f5_onboard.yml playbook,以配置静态路由,并循环遍历routes数据结构,以在设备上配置所有所需的路由。
再次运行 playbook 后,我们可以看到正确的静态路由,如下截图所示:
在 BIG-IP 设备上部署节点
使用 BIG-IP LTM 部署应用程序需要跨多个服务器对应用程序流量进行负载均衡。这要求我们定义托管应用程序的服务器/实例。在 BIG-IP 中,这些实例称为节点,并且它们使用唯一的 IP 地址标识每个服务器。在这个教程中,我们将开始在 BIG-IP 设备上部署一个新的应用程序(Web 服务器),并使用 Ansible 来配置承载此服务的节点。
准备工作
BIG-IP 的基本设置应该已经按照之前的教程完成,必须部署正确的 VLAN 以到达这些节点(物理服务器)。
如何操作...
- 创建一个名为
web_app.yml的新的 YAML 文件,内容如下:
---
vip: 10.1.100.100
vip_port: 443
endpoint: dev.internet.net
pool_name: dev_web_app
pool_members:
- ip: 10.1.10.10
name: "dev01.internal.net"
port: 443
- ip: 10.1.10.11
name: "dev01\. internal.net"
port: 443
- 创建一个名为
pb_f5_deploy_app.yml的新的 Ansible playbook,内容如下:
---
- name: Deploying a New App on BIG-IP
hosts: ltm01
connection: local
vars_file: web_app.yml
tasks:
- name: "Create Nodes on BIG-IP"
bigip_node:
address: "{{ item.ip }}"
name: "{{ item.name }}"
provider: "{{ conn_parameters }}"
state: present
loop: "{{ pool_members }}"
工作原理...
我们在名为web_app.yaml的 YAML 文件中定义了新的 Web 应用程序的所有参数,该应用程序应该托管在 BIG-IP LTM 设备上。在这个文件中,我们包括了一个pool_members参数,用来概述将承载应用程序的 Web 服务器。我们使用这个参数在 BIG-IP LTM 上创建节点。
我们为应用程序部署创建一个新的 playbook,名为pb_f5_deploy_app.yml。我们包括web_app.yml文件,以便访问为此应用程序定义的所有参数。我们使用bigip_node模块创建一个新的节点,并循环遍历从web_app.yml文件中派生的pool_members参数,以在 BIG-IP 设备上创建所有必需的节点。为了连接到 BIG-IP 节点,我们使用与之前相同的提供者属性,并使用group_vars/all.yml文件中定义的conn_parameters参数来建立与 BIG-IP 的连接。
运行这个 playbook,我们创建了所有必需的节点,如下截图所示:
在 BIG-IP 设备上配置负载均衡池
在 BIG-IP 上创建节点后,我们需要为我们部署的应用程序创建一个负载均衡池,并将我们创建的节点中的池成员分配到这个池中。在这个教程中,我们将概述如何在 BIG-IP 节点上配置负载均衡池,以及如何将成员分配到负载均衡池中。
准备工作
这个教程假设之前的所有教程都已经实施,并且 BIG-IP 上的节点已经按照之前的教程进行了配置。
如何操作...
- 更新
pb_f5_deploy_app.ymlplaybook,添加以下任务以创建一个新的池:
- name: Create New LB Pool
bigip_pool:
name: "POOL_{{ website }}_{{ vip_port }}"
lb_method: round-robin
state: present
provider: "{{ conn_parameters }}"
- 更新
pb_f5_deploy_app.ymlplaybook,添加以下任务以将池成员分配给新创建的池:
- name: Add Members to the Pool
bigip_pool_member:
pool: "POOL_{{ website }}_{{ vip_port }}"
host: "{{ item.ip }}"
name: "{{ item.name }}"
port: "{{ item.port }}"
description: "Web Server for {{ website }}"
provider: "{{ conn_parameters }}"
loop: "{{ pool_members }}"
工作原理...
在这个教程中,我们使用bigip_pool模块在 BIG-IP 系统上创建一个负载均衡池,并指定应该在此池上使用的负载均衡技术。在这个例子中,我们使用round-robin技术。我们使用从web_app.yml文件中提取的不同参数(主要是网站和vip_port)来创建池名称。
接下来,我们使用bigip_pool_member模块将池成员分配给这个新创建的池,并循环遍历web_app.yml文件中定义的所有pool_members。
我们可以看到,所有这些过程都创建了一种一致的方法来定义池名称,并将所需的池成员分配给正确的池成员。所有信息都是从一个单一的定义文件中检索出来,该文件描述并概述了服务的部署方式。
运行这两个任务,我们将看到池已经正确创建,并且具有正确的池成员,如下截图所示:
以下截图显示了当前的成员:
另请参阅...
在这个食谱中,我们概述了使用 Ansible 模块在 BIG-IP 节点上提供负载均衡池的基本用法。然而,这些模块还有更多的选项,例如为每个成员指定负载均衡比率,以及为整个池附加监视器。请参考以下链接以获取更多选项:
-
bigip_pool:docs.Ansible.com/Ansible/latest/modules/bigip_pool_module.htmlb -
bigip_pool_member:docs.Ansible.com/Ansible/latest/modules/bigip_pool_member_module.html
在 BIG-IP 设备上配置虚拟服务器
在 BIG-IP LTM 上部署应用程序的最后一部分是在 BIG-IP LTM 节点上配置虚拟服务器,并为该虚拟服务器在 BIG-IP 节点上创建虚拟 IP(VIP)。在这个食谱中,我们概述了如何使用 Ansible 部署虚拟服务器。
准备工作
这个食谱假设所有先前的食谱都已完成,并且负载均衡池和池成员已经配置好。
如何操作...
- 使用以下任务更新
pb_f5_deploy_app.ymlplaybook:
- name: Create Virtual Server
bigip_virtual_server:
name: "{{ website }}_{{ vip_port }}_VS"
destination: "{{ vip }}"
port: "{{ vip_port}}"
pool: "POOL_{{ website }}_{{ vip_port }}"
description: "VIP for {{ website }}"
profiles:
- http
- name: clientssl
context: client-side
- name: serverssl
context: server-side
state: present
provider: "{{ conn_parameters }}"
工作原理...
我们使用bigip_virtual_server模块在 BIG-IP 设备上提供所需的虚拟服务器,通过指定web_app.yml文件中定义的参数。我们还定义并提供需要应用于新创建的虚拟服务器的配置文件。这些配置文件是 HTTP 和 SSL 配置文件。这些配置文件已经默认在 BIG-IP 节点上创建,在需要创建自定义配置文件的情况下,我们需要使用适当的 Ansible 模块在单独的任务中创建这些配置文件。
运行最后一个任务,我们可以看到虚拟服务器已创建,如下面的截图所示:
在最后一个任务中,我们在 LTM 节点上创建了一个功能性的服务 VIP,以便开始处理我们新网站的 HTTP 请求,并将流量在负载均衡组中的所有实例之间进行负载均衡。
另请参阅...
在这个食谱中,我们讨论了使用 Ansible 模块在 BIG-IP 节点上提供虚拟服务器的基本用法。然而,还有更多的选项可用于调整需要部署的虚拟服务器的配置。
还有更多的 Ansible 模块可以让您创建可以附加到虚拟服务器的配置文件,以下是一些这些模块的链接:
-
bigip_virtual_server:docs.Ansible.com/Ansible/latest/modules/bigip_virtual_server_module.html -
bigip_profile_http:docs.Ansible.com/Ansible/latest/modules/bigip_profile_http_module.html -
bigip_profile_client_ssl:docs.Ansible.com/Ansible/latest/modules/bigip_profile_client_ssl_module.html -
bigip_profile_server_ssl:docs.Ansible.com/Ansible/latest/modules/bigip_profile_server_ssl_module.html
从 BIG-IP 节点检索操作数据
在这个食谱中,我们概述了如何检索 BIG-IP 设备上不同组件的操作数据,例如 BIG-IP 节点的网络状态,如接口和 VLAN,以及与应用程序交付相关的组件的数据,如虚拟服务器和池。
准备工作
为了按照这个步骤进行操作,假设已经设置了 Ansible 清单,并且 Ansible 与 BIG-IP 节点之间已经建立了 IP 连接,并且具有正确的用户凭据。
如何做...
- 创建一个新的 Ansible playbook,
pb_f5_validate.yml,内容如下:
---
- name: Validating BIG-IP Health
hosts: ltm01
connection: local
tasks:
- name: Collect Device Facts from BIG-IP
bigip_device_facts:
gather_subset:
- interfaces
provider: "{{ conn_parameters }}"
register: bigip_facts
- 使用以下内容更新 playbook,以过滤接口事实的新任务:
- name: Set Device Links
set_fact:
net_intfs: "{{ net_intfs | default([]) +
bigip_facts.interfaces | selectattr('name','equalto',item|string) | list }}"
loop: "{{ phy_interfaces }}"
- 使用以下内容更新
pb_f5_validate.ymlplaybook,以验证接口状态的新任务:
- name: Validate All Interface are operational
assert:
that:
- item.enabled == 'yes'
fail_msg: " Interface {{ item.name }} is Down"
loop: "{{net_intfs}}"
它是如何工作的...
在 BIG-IP 节点上支持的 REST API 使用不同的方法从设备中检索操作数据,并以 JSON 格式输出所有这些数据。以下代码段概述了使用bigip_device_facts模块从 BIG-IP 节点收集的接口状态:
"bigip_facts": {
< -- Output Omitted for brevity -->
"interfaces": [
{
"active_media_type": "10000T-FD",
"bundle": "not-supported",
"bundle_speed": "not-supported",
"enabled": "yes",
"flow_control": "tx-rx",
"full_path": "1.1",
"if_index": 48,
"lldp_admin": "txonly",
"mac_address": "00:50:00:00:01:01",
"media_sfp": "auto",
"mtu": 1500,
"name": "1.1",
< -- Output Omitted for brevity -->
}
我们使用bigip_device_facts从 BIG-IP 节点检索操作事实,并且仅使用gather_subset限制从节点检索的数据。我们只包括interfaces选项来获取接口数据。我们将所有检索到的输出保存到bigip_facts变量中。
我们为设备创建了一个名为net_intfs的新事实。这个新事实的唯一用途是过滤从上一个任务中检索到的接口事实,以匹配我们在phy_interfaces参数(在host_vars文件夹下定义)中为我们的设备定义的接口。这个新参数将只包括我们在设计中声明的接口的接口事实。
我们使用assert模块来验证我们为应用程序定义的所有接口是否从检索到的数据中启用和运行,并且我们循环遍历net_intfs变量(它是一个列表)来确认它们都已启用。
还有更多...
如果我们需要获取部署在 LTM 节点上的应用程序的操作数据,我们创建一个新的 playbook 来验证应用程序部署,如下所示,使用bigip_device_facts模块。我们将检索到的数据限制为只有虚拟服务器。我们使用assert语句来验证数据,就像我们在之前的 playbook 中所做的那样。以下代码显示了用于应用程序部署验证的 playbook 内容。
- 我们创建一个新的 playbook,
pb_f5_app_validate.yml,其中包含收集virtual-servers事实的以下任务:
---
- name: Validating BIG-IP App Health
hosts: ltm01
connection: local
vars_files: web_app.yml
tasks:
- name: Collect Virtual-Servers Facts from BIG-IP
bigip_device_facts:
gather_subset:
- virtual-servers
provider: "{{ conn_parameters }}"
register: bigip_app_facts
- 我们使用以下任务更新 playbook,以过滤
virtual-servers事实:
- name: Create Virtual Server Name Fact
set_fact:
vs_name: "{{ website }}_{{ vip_port }}_VS" - name: Create App Virtual Servers
set_fact:
app_vs: "{{ app_vs | default([]) +
bigip_app_facts.virtual_servers | selectattr('name','equalto',vs_name) | list }}"
- 我们使用以下任务更新 playbook 来验证我们应用程序的虚拟服务器的状态:
- name: Validate Virtual Address Status
assert:
that:
- item.enabled == 'yes'
- item.destination_address == vip
- item.destination_port == vip_port
fail_msg: " {{ item.name }} is No Setup Correctly"
loop: "{{app_vs}}"
这些验证 playbook 可以扩展到验证虚拟服务器上的多个参数。此外,我们还可以验证其他组件,如 LTM 负载均衡池,以构建更全面的应用程序部署验证。
另请参阅...
有关 Ansible bigip_device_facts模块以及我们可以从 BIG-IP 节点检索的所有信息的更多信息,请访问以下网站:docs.Ansible.com/Ansible/latest/modules/bigip_device_facts_module.html。
第六章:使用 NAPALM 和 Ansible 管理多供应商网络
Network Automation and Programmability Abstraction Layer with Multivendor support (NAPALM),顾名思义,是一个旨在与不同供应商设备交互的多供应商 Python 库,并且它提供了一种一致的方法来与所有这些设备进行交互,无论使用的是哪种供应商设备。
在之前的章节中,我们已经看到如何使用 Ansible 与不同的网络设备进行交互。然而,对于每个供应商操作系统,我们都必须使用不同的 Ansible 模块来支持特定的操作系统。此外,我们看到从每个供应商操作系统返回的数据完全不同。虽然编写多供应商设备的 playbook 仍然是可能的,但它需要使用多个不同的模块,并且我们需要处理这些设备返回的不同数据结构。这是 NAPALM 试图解决的主要问题。NAPALM 试图提供一个抽象和一致的 API 来与多个供应商操作系统进行交互,而 NAPALM 从这些不同的供应商操作系统返回的数据是规范化和一致的。
NAPALM 根据此节点支持的最常见 API 与每个设备进行交互,并且这个 API 被社区广泛采用。以下图表概述了 NAPALM 如何与最常见的网络设备进行交互,以及 NAPALM 用于与这些设备上的 API 进行交互的库:
由于 NAPALM 试图提供一种与网络设备交互的一致方法,它支持特定一组供应商设备。NAPALM 还支持在这些设备上执行的最常见任务,例如设备配置,检索接口的操作状态,Border Gate Protocol (BGP)和Link Layer Discovery Protocol (LLDP)等。有关支持的设备以及与这些设备交互时支持的方法的更多信息,请查看以下链接:napalm.readthedocs.io/en/latest/support/index.html。
在本章中,我们将概述如何使用 NAPALM 和 Ansible 自动化多供应商网络。我们将概述如何管理这些不同供应商操作系统的配置,以及如何从这些设备中检索操作状态。我们将以基本服务提供商网络的以下示例网络图为基础进行说明:
以下表格概述了我们示例拓扑中的设备及其各自的管理Internet Protocols (IPs):
| 设备 | 角色 | 供应商 | 管理(MGMT)端口 | MGMT IP |
|---|---|---|---|---|
mxp01 | P 路由器 | Juniper vMX 14.1 | fxp0 | 172.20.1.2 |
mxp02 | P 路由器 | Juniper vMX 14.1 | fxp0 | 172.20.1.3 |
mxpe01 | PE 路由器 | Juniper vMX 14.1 | fxp0 | 172.20.1.4 |
mxpe01 | PE 路由器 | Juniper vMX 17.1 | fxp0 | 172.20.1.5 |
xrpe03 | PE 路由器 | Cisco XRv 6.1.2 | Mgmt0/0/CPU0/0 | 172.20.1.6 |
本章涵盖的主要内容如下:
-
安装 NAPALM 并与 Ansible 集成
-
构建 Ansible 网络清单
-
使用 Ansible 连接和认证网络设备
-
构建设备配置
-
使用 NAPALM 在网络设备上部署配置
-
使用 NAPALM 收集设备信息
-
使用 NAPALM 验证网络可达性
-
使用 NAPALM 验证和审计网络
技术要求
本章的代码文件可以在此处找到:github.com/PacktPublishing/Network-Automation-Cookbook/tree/master/ch6_napalm。
本章中将需要以下软件:
-
运行 CentOS 7 的 Ansible 机器
-
Ansible 2.9
-
Juniper Virtual MX (vMX) 路由器运行 Junos OS 14.1R8 和 Junos OS 17.1R1 版本
-
运行 IOS XR 6.1.2 的 Cisco XRv 路由器
查看以下视频以查看代码的实际操作:
安装 NAPALM 并与 Ansible 集成
在这个示例中,我们概述了如何安装 NAPALM 并将其集成到与 Ansible 一起工作。这个任务是强制性的,因为 NAPALM Ansible 模块不是默认随 Ansible 一起提供的核心模块的一部分。因此,为了开始使用这些模块,我们需要安装 NAPALM 及其所有 Ansible 模块。然后,我们需要告诉 Ansible 在哪里找到它,并开始使用 NAPALM 团队为 Ansible 开发的特定模块。
准备工作
Ansible 和 Python 3 需要安装在机器上,还需要安装python3-pip包,我们将用它来安装 NAPALM。
如何做…
- 安装
napalm-ansiblePython 包,如下面的代码片段所示:
$ pip3 install napalm-ansible
- 运行
napalm-ansible命令,如下面的代码块所示:
$ napalm-ansible
- 为了确保 Ansible 可以使用 NAPALM 模块,您必须将以下配置添加到您的 Ansible 配置文件(
ansible.cfg)中:
[defaults]
library = /usr/local/lib/python3.6/site-packages/napalm_ansible/modules
action_plugins = /usr/local/lib/python3.6/site-packages/napalm_ansible/plugins/action
有关 Ansible 配置文件的更多详细信息,请访问docs.ansible.com/ansible/latest/intro_configuration.html。
- 创建一个名为
ch6_napalm的新文件夹,并创建ansible.cfg文件,更新如下代码块所示:
$ cat ansible.cfg
[defaults]
inventory=hosts
retry_files_enabled=False
gathering=explicit
host_key_checking=False
library = /usr/local/lib/python3.6/site-packages/napalm_ansible/modules
action_plugins = /usr/local/lib/python3.6/site-packages/napalm_ansible/plugins/action
它是如何工作的…
由于 NAPALM 包和相应的 NAPALM Ansible 模块不是默认随 Ansible 一起提供和安装的核心模块的一部分,我们需要在系统上安装它,以便开始使用 NAPALM Ansible 模块。NAPALM 团队已经发布了一个特定的 Python 包来安装 NAPALM 以及所有 Ansible 模块和所有依赖项,以便从 Ansible 内部开始使用 NAPALM。这个包是napalm-ansible。由于我们使用 Python 3,我们将使用pip3程序来安装这个包。
为了告诉 Ansible 模块安装在哪里,我们需要将这些模块的路径输入到 Ansible 中。NAPALM 团队还提供了如何找到 NAPALM 模块安装路径以及如何通过napalm-ansible程序将其集成到 Ansible 的简单说明。我们执行napalm-ansible命令,它输出了我们需要包含在ansible.cfg文件中的所需配置,以便 Ansible 可以找到我们将要使用的 NAPALM 模块。
我们使用从napalm-ansible命令获得的输出更新ansible.cfg文件。然后,我们更新库和动作插件选项,告诉 Ansible 在搜索模块或动作插件时将这些文件夹包括在其路径中。在ansible.cfg文件中,我们包括了我们在前几章中使用的正常配置。
构建 Ansible 网络清单
在这个示例中,我们将概述如何构建和组织我们的 Ansible 清单,以描述本章中概述的样本服务提供商网络设置。构建 Ansible 清单是一个强制性步骤,为了告诉 Ansible 如何连接到受管设备。在 NAPALM 的情况下,我们需要将网络中的不同节点分类到 NAPALM 支持的正确供应商类型中。
如何做…
- 在新文件夹(
ch6_napalm)中,我们创建一个包含以下内容的hosts文件:
$ cat hosts
[pe]
mxpe01 ansible_host=172.20.1.3
mxpe02 ansible_host=172.20.1.4
xrpe03 ansible_host=172.20.1.5
[p]
mxp01 ansible_host=172.20.1.2
mxp02 ansible_host=172.20.1.6
[junos]
mxpe01
mxpe02
mxp01
mxp02
[iosxr]
xrpe03
[sp_core:children]
pe
p
它是如何工作的…
我们使用hosts文件构建了 Ansible 清单,并定义了多个组,以便对基础设施进行分段,如下所示:
-
我们创建了
PE组,它引用了我们拓扑中的所有多协议标签交换(MPLS)提供者边缘(PE)节点。 -
我们创建了
P组,它引用了我们拓扑中的所有 MPLS 提供者(P)节点。 -
我们创建了
junos组,以引用我们拓扑中的所有 Juniper 设备。 -
我们创建了
iosxr组来引用所有运行 IOS-XR 的节点。
在使用 NAPALM 时,根据供应商或操作系统对组进行分割和定义是一种最佳实践,因为我们使用这些组来指定 NAPALM 识别远程管理节点的供应商所需的参数,并建立与该远程节点的网络连接的方式。在下一个教程中,我们将概述我们将如何使用这些组(junos和iosxr),以及我们将包括哪些参数以便 NAPALM 能够与远程管理节点建立连接。
使用 Ansible 连接和认证网络设备
在这个教程中,我们将概述如何使用 Ansible 连接到 Juniper 和 IOS-XR 节点,以便开始与设备进行交互。
准备工作
为了按照这个教程进行操作,应该按照之前的教程构建一个 Ansible 清单文件。此外,必须配置 Ansible 控制机与网络中所有设备之间的 IP 可达性。
如何做…
- 在 Juniper 设备上,配置用户名和密码,如下所示:
system {
login {
user ansible {
class super-user;
authentication {
encrypted-password "$1$mR940Z9C$ipX9sLKTRDeljQXvWFfJm1"; ## ansible123
}
}
}
}
- 在 Cisco IOS-XR 设备上,配置用户名和密码,如下所示:
!
username ansible
group root-system
password 7 14161C180506262E757A60 # ansible123
!
- 在 Juniper 设备上启用网络配置协议(NETCONF),如下所示:
system {
services {
netconf {
ssh {
port 830;
}
}
}
}
- 在 IOS-XR 设备上,我们需要启用安全外壳(SSH),以及启用
xml-agent,如下所示:
!
xml agent tty
iteration off
!
xml agent
!
ssh server v2
ssh server vrf default
- 在 Ansible 机器上,在
ch6_napalm文件夹中创建group_vars目录,并创建junos.yml和iosxr.yml文件,如下所示:
$ cat group_vars/iosxr.yml
---
ansible_network_os: junos
ansible_connection: netconf
$ cat group_vars/junos.yml
---
ansible_network_os: iosxr
ansible_connection: network_cli
- 在
group_vars文件夹下,创建带有以下登录详细信息的all.yml文件:
$ cat group_vars/all.yml
ansible_user: ansible
ansible_ssh_pass: ansible123
工作原理…
NAPALM 为 NAPALM 支持的每个供应商设备使用特定的传输 API。它使用这个 API 来连接设备,所以在我们的示例拓扑中,我们需要在 Juniper 设备上启用 NETCONF。对于 Cisco IOS-XR 设备,我们需要启用 SSH,并在 IOS-XR 设备上启用 XML agent。
在远程节点上配置用于在 Ansible 控制机上进行身份验证的用户名/密码。我们在设备上执行所有这些步骤,以使它们准备好与 NAPALM 进行通信。
在生产中使用 IOS-XR 设备上的传统xml agent不被推荐,并且需要根据 Cisco 文档进行评估。有关更多详细信息,请参阅www.cisco.com/c/en/us/td/docs/routers/asr9000/software/asr9k_r5-3/sysman/command/reference/b-sysman-cr53xasr/b-sysman-cr53xasr_chapter_01010.html。
在 Ansible 机器上,我们根据每个供应商设置ansible_connection参数(juniper使用netconf,iosxr使用network_cli),并指定ansible_network_os参数来指定供应商操作系统。所有这些参数都在junos.yml和iosxr.yml的group_vars层次结构下定义,对应于我们在清单中定义的用于根据供应商操作系统对设备进行分组的组。最后,我们在all.yml文件中通过ansible_user和ansible_ssh_pass指定用户名和密码,因为我们使用相同的用户来对 Juniper 和 Cisco 设备进行身份验证。
为了测试和验证,我们可以使用 Ansible 的ping模块从 Ansible 控制机与设备进行通信,如下所示:
$ ansible all -m ping
mxpe01 | SUCCESS => {
"changed": false,
"ping": "pong"
}
mxpe02 | SUCCESS => {
"changed": false,
"ping": "pong"
}
mxp02 | SUCCESS => {
"changed": false,
"ping": "pong"
}
mxp01 | SUCCESS => {
"changed": false,
"ping": "pong"
}
xrpe03 | SUCCESS => {
"changed": false,
"ping": "pong"
}
构建设备配置
NAPALM 不提供声明性模块来配置受管设备上的各种系统参数,比如接口的 BGP,服务质量(QoS)等。但是,它提供了一个通用的 API 来将基于文本的配置推送到所有设备,因此需要以文本格式存在设备的配置,以便推送所需的配置。在这个配方中,我们将为所有设备创建配置。这是我们将在下一个配方中使用 NAPALM 推送到设备的配置。
准备工作
作为这个配方的先决条件,必须存在一个 Ansible 清单文件。
如何做…
- 创建一个
roles文件夹,并在此文件夹内创建一个名为build_router_config的新角色,如下所示:
$ mkdir roles && mkdir roles/build_router_config
- 使用与我们在第三章《使用 Ansible 自动化 Juniper 设备的服务提供商》中为 Juniper 设备开发的
build_router_config角色完全相同的内容(Jinja2 模板和任务)来为设备生成配置。目录布局应如下代码块所示:
$ tree roles/build_router_config/
roles/build_router_config/
├── tasks
│ ├── build_config_dir.yml
│ ├── build_device_config.yml
│ └── main.yml
└── templates
└── junos
├── bgp.j2
├── intf.j2
├── mgmt.j2
├── mpls.j2
└── ospf.j2
- 在
templates文件夹下创建一个名为iosxr的新文件夹,并使用 Jinja2 模板填充它,以用于不同的 IOS-XR 配置部分,如下代码块所示:
$ tree roles/build_router_config/templates/iosxr/
roles/build_router_config/templates/iosxr/
├── bgp.j2
├── intf.j2
├── mgmt.j2
├── mpls.j2
└── ospf.j2
- 更新
group_vars/all.yml文件,填入描述我们网络拓扑所需的数据,如下代码块所示:
$ cat group_vars/all.yml
tmp_dir: ./tmp
config_dir: ./configs
p2p_ip:
< -- Output Omitted for brevity -->
xrpe03:
- {port: GigabitEthernet0/0/0/0, ip: 10.1.1.7 , peer: mxp01, pport: ge-0/0/2, peer_ip: 10.1.1.6}
- {port: GigabitEthernet0/0/0/1, ip: 10.1.1.13 , peer: mxp02, pport: ge-0/0/2, peer_ip: 10.1.1.12}
lo_ip:
mxp01: 10.100.1.254/32
mxp02: 10.100.1.253/32
mxpe01: 10.100.1.1/32
mxpe02: 10.100.1.2/32
xrpe03: 10.100.1.3/32
- 在
host_vars目录中为每个主机创建一个特定的目录,并在每个目录中创建带有以下 BGP 对等内容的bgp.yml文件:
$ cat host_vars/xrpe03/bgp.yml
bgp_asn: 65400
bgp_peers:
- local_as: 65400
peer: 10.100.1.254
remote_as: 65400
- 创建一个名为
pb_napalm_net_build.yml的新 playbook,该 playbook 利用build_router_config角色来生成设备配置,如下代码块所示:
$ cat pb_napalm_net_build.yml
---
- name: " Generate and Deploy Configuration on All Devices"
hosts: sp_core
tasks:
- name: Build Device Configuration
import_role:
name: build_router_config
delegate_to: localhost
tags: build
工作原理…
在这个配方中,我们的主要目标是创建设备配置,我们将在示例拓扑中的设备上部署。我们使用了与我们在第三章《使用 Ansible 自动化 Juniper 设备的服务提供商》中用于生成 Juniper 设备配置的相同的 Ansible 角色。这个角色的唯一补充是我们为 IOS XR 添加了所需的 Jinja2 模板。
以下是步骤的快速解释,作为快速回顾:
- 通过 Ansible 变量对网络进行建模
我们在group_vars/all.yml文件中描述了网络拓扑的不同方面,比如点对点(P2P)接口,环回接口和开放最短路径优先(OSPF)参数,这些都是在不同的数据结构下。对于任何特定主机的数据,我们使用host_vars目录来填充所有特定于特定节点的变量/参数,而在我们的情况下,我们使用这种方法来为每个节点的 BGP 数据概述bgp_peers变量。这为我们提供了填充 Jinja2 模板所需的所有必要数据,以生成我们示例网络中每个设备的最终配置。
- 构建 Jinja2 模板
我们将所有的 Jinja2 模板放在我们的角色内的templates文件夹中,并且我们根据供应商 OS 对我们的 Jinja2 模板进行分段,每个都放在一个单独的文件夹中。接下来,我们为配置的每个部分创建一个 Jinja2 模板。以下代码片段概述了模板文件夹的目录结构:
templates/
├── iosxr
│ ├── bgp.j2
│ ├── intf.j2
│ ├── mgmt.j2
│ ├── mpls.j2
│ └── ospf.j2
└── junos
├── bgp.j2
├── intf.j2
├── ospf.j2
├── mgmt.j2
└── mpls.j2
有关此配方中使用的不同 Jinja2 模板的详细解释以及它们如何使用定义的 Ansible 变量来生成最终配置,请参阅本书的第三章《使用 Ansible 自动化 Juniper 设备的服务提供商》,因为我们对 JunOS 和 IOS-XR 设备使用了完全相同的网络拓扑和相同的数据结构。
运行此 playbook 将在configs文件夹中为我们 Ansible 清单中的所有设备生成配置,如下代码块所示:
$ tree configs/
configs/
├── mxp01.cfg
├── mxp02.cfg
├── mxpe01.cfg
├── mxpe02.cfg
└── xrpe03.cfg
使用 NAPALM 在网络设备上部署配置
在这个配方中,我们将概述如何使用 Ansible 和 NAPALM 在不同的供应商设备上推送配置。NAPALM 提供了一个用于配置管理的单个 Ansible 模块,该模块允许我们使用单一的通用方法在 NAPALM 支持的任何供应商设备上推送任何配置,大大简化了 Ansible playbooks。
准备工作
要按照本配方进行操作,您需要已经设置好一个 Ansible 清单,并且在 Ansible 控制器和网络设备之间建立了网络可达性。我们将要推送到设备的配置是我们在上一个配方中生成的配置。
如何做...
- 更新
pb_napalm_net_build.ymlplaybook 文件,并添加以下代码块中显示的任务:
$ cat pb_napalm_net_build.yml
---
- name: " Play 1: Deploy Config on All JunOS Devices"
hosts: sp_core
tasks:
< -- Output Omitted for brevity -->
- name: "P1T5: Deploy Configuration"
napalm_install_config:
hostname: "{{ ansible_host }}"
username: "{{ ansible_user }}"
password: "{{ ansible_ssh_pass }}"
dev_os: "{{ ansible_network_os }}"
config_file: "{{config_dir}}/{{ inventory_hostname }}.cfg"
commit_changes: "{{commit | default('no')}}"
replace_config: yes
tags: deploy, never
工作原理...
正如前面所述,NAPALM 提供了一个单一的 Ansible 模块,用于将配置推送到网络设备。它要求配置存在于一个文本文件中。当它连接到网络设备时,它会将配置推送到相应的设备上。
由于我们使用了一个可以在 NAPALM 支持的所有供应商 OS 设备上使用的单个配置模块,并且 NAPALM 使用不同的连接 API 来管理设备,因此我们需要告诉模块设备的供应商 OS。我们还需要提供其他参数,例如用户名/密码,以便登录并与设备进行身份验证。
napalm_install_config模块需要以下强制参数,以便正确登录到受管设备并将配置推送到设备上:
-
hostname:这是我们可以通过其到达设备的 IP 地址。我们为此参数提供ansible_host的值。 -
username/password:这是连接到设备的用户名和密码。我们需要提供ansible_user和ansible_ssh_pass属性。 -
dev_os:此参数提供了 NAPALM 需要的供应商 OS 名称,以便选择与设备通信的正确 API 和正确库。对于此选项,我们提供ansible_network_os参数。 -
napalm_install_config模块使用以下参数来管理远程设备上的配置: -
config_file:这提供了包含需要推送到受管设备的设备配置的配置文件的路径。 -
commit_changes:这告诉设备是否提交配置。NAPALM 提供了一种一致的配置提交方法,即使对于默认不支持的设备(例如 Cisco IOS 设备)也是如此。 -
replace_config:此参数控制如何在设备上的现有配置和config_file参数中的配置之间进行合并。在我们的情况下,由于我们生成了整个设备配置,并且所有配置部分都在 Ansible 下进行管理,我们用我们生成的配置替换整个配置。这将导致设备上任何不在我们配置文件中的配置被删除。
根据本配方中概述的配置,当我们使用deploy标签运行 playbook 时,NAPALM 将连接到设备并推送配置。但是,它不会在远程设备上提交配置,因为我们已经将commit_changes的默认值指定为no。如果我们需要在远程设备上推送和提交配置,可以在运行 playbook 时将commit参数的值设置为yes,如下面的代码片段所示:
$ ansible-playbook pb_napalm_net_build.yml --tags deploy --e commit=yes
还有更多...
napalm_install_config模块提供了额外的选项来控制如何管理远程设备上的配置,例如配置差异。通过此选项,我们可以收集设备上运行配置与我们将通过 NAPALM 推送的配置之间的差异。可以通过以下方式启用此选项:
- 创建一个名为
config_diff的文件夹,用于存储 NAPALM 捕获的配置差异,如下面的代码块所示:
$ cat group_vars/all.yml
< -- Output Omitted for brevity -->
config_diff_dir: ./config_diff
$ cat tasks/build_config_dir.yml
- name: "Create Config Diff Directory"
file: path={{config_diff_dir}} state=directory
run_once: yes
- 更新
pb_napalm_net_build.ymlplaybook,如下面的代码块所示:
$ cat pb_napalm_net_build.yml
---
- name: "Generate and Deploy Configuration on All Devices"
hosts: sp_core
tasks:
< -- Output Omitted for brevity -->
- name: "Deploy Configuration"
napalm_install_config:
hostname: "{{ ansible_host }}"
username: "{{ ansible_user }}"
password: "{{ ansible_ssh_pass }}"
dev_os: "{{ ansible_network_os }}"
config_file: "{{config_dir}}/{{ inventory_hostname }}.cfg"
diff_file: "{{ config_diff_dir}}/{{ inventory_hostname }}_diff.txt"
commit_changes: "{{commit | default('no')}}"
replace_config: yes
tags: deploy, never
接下来,我们创建一个新的文件夹,用于存放我们为每个设备生成的所有配置差异文件,并将diff_file参数添加到napalm_install_config模块。这将为每个设备收集配置差异,并将其保存到每个设备的config_diff目录中。
当我们在设备上运行具有修改配置的 playbook 时,我们可以看到为每个设备生成的config_diff文件,如下面的代码块所示:
$ tree config_diff/
config_diff/
├── mxp01_diff.txt
├── mxpe01_diff.txt
├── mxpe02_diff.txt
└── xrpe03_diff.txt
使用 NAPALM 收集设备事实
在这个配方中,我们将概述如何使用 NAPALM fact-gathering Ansible 模块从网络设备中收集操作状态。这可以用来验证跨多供应商设备的网络状态,因为 NAPALM Ansible 的事实收集模块在 NAPALM 支持的所有供应商操作系统中返回一致的数据结构。
准备工作
为了跟随这个配方,假设 Ansible 清单已经就位,并且 Ansible 控制器与网络之间的网络可达性已经建立。最后,网络已根据以前的配方进行了配置。
如何做…
- 创建一个名为
pb_napalm_get_facts.yml的 Ansible playbook,内容如下:
$ cat cat pb_napalm_get_facts.yml
---
- name: " Collect Network Facts using NAPALM"
hosts: sp_core
tasks:
- name: "P1T1: Collect NAPALM Facts"
napalm_get_facts:
hostname: "{{ ansible_host }}"
username: "{{ ansible_user }}"
password: "{{ ansible_ssh_pass }}"
dev_os: "{{ ansible_network_os }}"
filter:
- bgp_neighbors
- 使用以下任务更新 playbook 以验证 NAPALM 事实模块返回的数据:
$ cat pb_napalm_get_facts.yml
< -- Output Omitted for brevity -->
- name: Validate All BGP Routers ID is correct
assert:
that: napalm_bgp_neighbors.global.router_id == lo_ip[inventory_hostname].split('/')[0]
when: napalm_bgp_neighbors
- name: Validate Correct Number of BGP Peers
assert:
that: bgp_peers | length == napalm_bgp_neighbors.global.peers.keys() | length
when: bgp_peers is defined
- name: Validate All BGP Sessions Are UP
assert:
that: napalm_bgp_neighbors.global.peers[item.peer].is_up == true
loop: "{{ bgp_peers }}"
when: bgp_peers is defined
工作原理…
我们使用napalm_get_facts Ansible 模块从网络设备中检索操作状态。我们提供与napalm_install_config使用的相同参数(主机名、用户名/密码和dev_os),以便能够连接到设备并从这些设备收集所需的操作状态。
为了控制我们使用 NAPALM 检索的信息,我们使用filter参数并提供我们需要检索的必要信息。在这个例子中,我们将检索的数据限制为bgp_neighbors。
napalm_get_facts模块返回从节点检索的数据作为 Ansible 事实。这些数据可以从napalm_bgp_neighbors变量中检索,该变量存储从设备检索到的所有 NAPALM BGP 事实。
以下片段概述了从 Junos OS 设备检索到的napalm_bgp_neighbors的输出:
ok: [mxpe02] => {
"napalm_bgp_neighbors": {
"global": {
"peers": {
"10.100.1.254": {
"address_family": {
"ipv4": {
"accepted_prefixes": 0,
"received_prefixes": 0,
"sent_prefixes": 0
},
< -- Output Omitted for brevity -->
},
"description": "",
"is_enabled": true,
"is_up": true,
"local_as": 65400,
"remote_as": 65400,
"remote_id": "10.100.1.254",
"uptime": 247307
}
},
"router_id": "10.100.1.2"
}
}
}
以下片段概述了从 IOS-XR 设备检索到的napalm_bgp_neighbors的输出:
ok: [xrpe03] => {
"napalm_bgp_neighbors": {
"global": {
"peers": {
"10.100.1.254": {
"address_family": {
< -- Output Omitted for brevity -->
},
"description": "",
"is_enabled": false,
"is_up": true,
"local_as": 65400,
"remote_as": 65400,
"remote_id": "10.100.1.254",
"uptime": 247330
}
},
"router_id": "10.100.1.3"
}
}
}
正如我们所看到的,不同网络供应商的 NAPALM 返回的 BGP 信息数据在不同网络供应商之间是一致的。这简化了解析这些数据,并允许我们运行更简单的 playbook 来验证网络状态。
我们使用 NAPALM 返回的数据来比较和验证网络的操作状态与我们使用 Ansible 变量(如bgp_peers)定义的网络设计。我们使用assert模块来验证多个 BGP 信息,例如以下内容:
-
正确的 BGP 对数
-
BGP 路由器 ID
-
所有 BGP 会话都是可操作的
在不同的assert模块中使用when语句的情况下,我们的拓扑中有一个不运行 BGP 的路由器(mxp02是一个例子)。因此,我们跳过这些节点上的这些检查。
另请参阅…
napalm_get_fact模块可以根据供应商设备支持的设备和供应商支持的事实级别检索网络设备的大量信息。例如,它支持几乎所有已知网络供应商的接口、IP 地址和 LLDP 对等体的检索。
有关napalm_get_facts模块的完整文档,请查看以下 URL:
napalm.readthedocs.io/en/latest/integrations/ansible/modules/napalm_get_facts/index.html。
有关 NAPALM 支持的完整 facts/getters 及其针对供应商设备的支持矩阵,请参考以下 URL:
napalm.readthedocs.io/en/latest/support/.
使用 NAPALM 验证网络可达性
在这个步骤中,我们将概述如何利用 NAPALM 及其 Ansible 模块来验证网络在整个网络中的可达性。这个验证执行从受管设备到我们指定的目的地的 ping,以确保网络中的转发路径按预期工作。
准备工作
要按照这个步骤进行,假设已经有一个 Ansible 清单,并且 Ansible 控制器与网络之间已建立网络可达性。在这个步骤中,假设网络已根据相关的先前步骤进行了配置。
如何做…
- 创建一个名为
pb_napalm_ping.yml的新 playbook,内容如下:
$ cat pb_napalm_ping.yml
---
- name: " Validation Traffic Forwarding with NAPALM"
hosts: junos:&pe
vars:
rr: 10.100.1.254
max_delay: 5 # This is 5 msec
tasks:
- name: "P1T1: Ping Remote Destination using NAPALM"
napalm_ping:
hostname: "{{ ansible_host }}"
username: "{{ ansible_user }}"
password: "{{ ansible_ssh_pass }}"
dev_os: "{{ ansible_network_os }}"
destination: "{{ rr }}"
count: 2
register: rr_ping
- 更新 playbook,添加如下代码块中所示的验证任务:
$ cat pb_napalm_ping.yml
< -- Output Omitted for brevity -->
- name: Validate Packet Loss is Zero and No Delay
assert:
that:
- rr_ping.ping_results.keys() | list | first == 'success'
- rr_ping.ping_results['success'].packet_loss == 0
- rr_ping.ping_results['success'].rtt_avg < max_delay
工作原理…
NAPALM 提供了另一个 Ansible 模块napalm_ping,它连接到远程受管设备,并从远程受管设备向我们指定的目的地执行 ping。使用这个模块,我们能够验证受管设备与指定目的地之间的转发路径。
这个napalm_ping模块目前不支持 Cisco IOS-XR 设备,这就是为什么我们只选择 Junos OS 组中的所有 PE 设备。在我们的 playbook 中,我们使用junos:&pe模式来做到这一点。
在我们的示例中,我们创建了一个新的 playbook,并在 playbook 本身中使用vars参数指定我们要 ping 的目的地以及我们 ping 数据包的最大延迟。然后,我们使用napalm_ping模块连接到我们拓扑中的 MPLS PE 设备(只有 Junos OS 设备),从所有这些 PE 节点向我们指定的目的地(在我们的情况下,这是我们的路由反射器(RR)路由器的环回)执行ping。我们将所有这些数据存储在一个名为rr_ping的变量中。
以下代码片段显示了从napalm_ping返回的输出:
"ping_results": {
"success": {
"packet_loss": 0,
"probes_sent": 2,
"results": [
{
"ip_address": "10.100.1.254",
"rtt": 2.808
},
{
"ip_address": "10.100.1.254",
"rtt": 1.91
}
],
"rtt_avg": 2.359,
"rtt_max": 2.808,
"rtt_min": 1.91,
"rtt_stddev": 0.449
}
}
最后,我们使用assert模块来验证并比较 NAPALM 返回的结果与我们的要求(ping 成功,没有丢包,延迟小于max_delay)。
使用 NAPALM 验证和审计网络
在这个步骤中,我们将概述如何通过定义网络的预期状态并让 NAPALM 验证网络的实际/操作状态是否与我们的预期状态匹配来验证网络的操作状态。这对于网络审计和网络基础设施的合规性报告非常有用。
准备工作
要按照这个步骤进行,假设已经有一个 Ansible 清单,并且 Ansible 控制器与网络之间已建立网络可达性。最后,网络已根据先前概述的步骤进行了配置。
如何做…
- 创建一个名为
napalm_validate的新文件夹,并为每个设备创建一个 YAML 文件。我们将验证其状态,如下面的代码块所示:
$ cat napalm_validate/mxpe01.yml
---
- get_interfaces_ip:
ge-0/0/0.0:
ipv4:
10.1.1.3:
prefix_length: 31
- get_bgp_neighbors:
global:
router_id: 10.100.1.1
- 创建一个名为
pb_napalm_validation.yml的新 playbook,内容如下:
$ cat pb_napalm_validation.yml
---
- name: " Validating Network State via NAPALM"
hosts: pe
tasks:
- name: "P1T1: Validation with NAPALM"
napalm_validate:
hostname: "{{ ansible_host }}"
username: "{{ ansible_user }}"
password: "{{ ansible_ssh_pass }}"
dev_os: "{{ ansible_network_os }}"
validation_file: "napalm_validate/{{ inventory_hostname}}.yml"
ignore_errors: true
register: net_validate
- 更新 playbook,创建一个文件夹,用于存储每个设备的合规性报告,内容如下代码块所示:
$ cat pb_napalm_validation.yml
< -- Output Omitted for brevity -->
- name: Create Compliance Report Folder
file: path=compliance_folder state=directory
- name: Clean Last Compliance Report
file: path=compliance_folder/{{inventory_hostname}}.txt state=absent
- name: Create Compliance Report
copy:
content: "{{ net_validate.compliance_report | to_nice_yaml }}"
dest: "compliance_folder/{{ inventory_hostname }}.txt"
工作原理…
NAPALM 提供了另一个用于网络验证的模块,即napalm_validate模块。该模块主要用于执行审计并为网络基础设施生成合规性报告。主要思想是声明网络的预期状态,并在 YAML 文档中定义它。这个 YAML 文件有一个特定的格式,遵循不同 NAPALM 事实生成的相同结构。在这个 YAML 文件中,我们指定了我们想要从网络中检索的 NAPALM 事实,以及网络的预期输出。
我们将这些验证文件提供给napalm_validate模块,NAPALM 将连接到设备,检索这些验证文件中指定的事实,并将从网络中检索的输出与这些验证文件中声明的网络状态进行比较。
接下来,NAPALM 生成一个compliance_report对象,其中包含比较的结果以及网络是否符合这些验证文件。我们还设置了ignore_errors参数,以便在设备不符合时继续执行此 playbook 中的其他任务,这样我们就可以在生成的合规性报告中捕获这个合规性问题。
最后,我们将输出保存在一个名为compliance_folder的单独文件夹中,为每个节点复制compliance_report参数的内容,并使用to_nice_yaml过滤器进行格式化。
以下是为mxpe01设备生成的正确合规性报告的代码片段:
complies: true
get_bgp_neighbors:
complies: true
extra: []
missing: []
present:
global:
complies: true
nested: true
get_interfaces_ip:
complies: true
extra: []
missing: []
present:
ge-0/0/0.0:
complies: true
nested: true
skipped: []
另请参阅...
有关验证部署和napalm_validate的其他选项的更多信息,请查看以下网址:
第七章:使用 Ansible 部署和操作 AWS 网络资源
云是改变多个行业的一项技术。它对 IT 的整体基础设施、应用程序的部署方式以及为云采用而进行架构设计都产生了重大影响。
AWS 是主要的云提供商之一。它提供了多个网络资源和服务,以构建可扩展和高可用的网络设计,以在 AWS 云上托管应用程序。
云采用的主要支柱之一是自动化以及我们能够多快地部署工作负载。每个云提供商都有自己的自动化能力。在 AWS 的情况下,这是一个名为 CloudFormation 的服务,它使我们能够使用**基础设施即代码(IaC)**描述 AWS 基础设施,并在 AWS 云上部署基础设施。然而,与 CloudFormation 相比,Ansible 的优势在于其能够描述/部署所有云提供商(包括 AWS)的资源。这使我们能够在多云环境中使用一致的工具来部署我们的工作负载。
Ansible 提供了多个模块来与 AWS 云进行交互,以提供和控制不同的资源。
在本章中,我们将专注于部署 AWS 提供的基本网络服务,这些服务允许我们在 AWS 中构建可扩展的网络设计。我们将在我们的示例中使用以下样本 AWS 网络设计,并概述如何使用 Ansible 构建这个网络:
本章涵盖的主要教程如下:
-
安装 AWS SDK
-
构建 Ansible 清单
-
认证到您的 AWS 账户
-
使用 Ansible 部署 VPC
-
使用 Ansible 部署子网
-
使用 Ansible 部署 IGWs
-
使用 Ansible 控制 VPC 内的路由
-
使用 Ansible 部署网络 ACL
-
使用 Ansible 进行部署验证
-
使用 Ansible 取消部署 AWS 资源
技术要求
本章中使用的 GitHub 代码可以在这里找到:
github.com/PacktPublishing/Network-Automation-Cookbook/tree/master/ch7_aws
本章基于以下软件版本:
-
运行 CentOS 7 的 Ansible 机器
-
Ansible 2.9
-
Python 3.6.8
查看以下视频,了解代码的实际操作:
安装 AWS SDK
在这个教程中,我们将概述如何安装 Ansible 所需的 Python 库,以开始与 AWS 编排系统进行交互。这一步是强制性的,因为这些 Python 库必须安装在 Ansible 控制机器上,以便所有的 Ansible AWS 模块都能正常工作。
准备工作
您需要在机器上拥有 sudo 访问权限,以安装所需的 Python 库。此外,您需要安装python-pip包,因为我们将使用pip来安装所需的 Python 库。
如何操作...
- 我们可以测试任何 Ansible AWS 模块,以检查所需的 Python 库是否已安装:
$ ansible localhost -m aws_az_facts
localhost | FAILED! => {
"changed": false,
"msg": "boto3 required for this module"
}
- 安装
boto和boto3包,如下所示:
$ sudo pip3 install boto3 boto
工作原理...
与 AWS 编排系统 API 交互的 Python SDK 库是boto和boto3。这些 Python 包必须存在于 Ansible 控制机器上,因为在 Ansible 中,所有的 AWS 模块都依赖于这些 Python 包中的一个来操作。我们可以通过使用上述第一步来运行任何 AWS 模块(例如aws_az_facts)使用ansible命令来检查系统上是否已安装了这个包。如果boto3库不存在,我们将收到一条错误消息,告知我们boto3未安装。
我们可以使用 Python pip 程序使用pip3命令安装boto和boto3包,这将安装所有需要安装和运行包的依赖项。在这个阶段,我们已经拥有了运行所有 Ansible AWS 模块所需的一切。
构建 Ansible 清单
在这个示例中,我们将概述如何构建一个 Ansible 清单,以描述我们将在 AWS 公共云中构建的基础设施网络设置。这是为了定义我们将部署基础设施的所有地区中的所有 VPC 而必须的步骤。
如何做到...
- 创建一个新的
ch7_aws文件夹,并在其中创建一个hosts文件,如下所示:
$ cat hosts
[us]
us_prod_vpc
[eu]
eu_prod_vpc
[prod_vpcs]
us_prod_vpc
eu_prod_vpc
- 在
ch7_aws内创建ansible.cfg文件,内容如下所示:
$ cat ansible.cfg
[defaults]
inventory=hosts
vault_password_file=~/.ansible_vault_passwd
gathering=explicit
transport=local
retry_files_enabled=False
action_warnings=False
它是如何工作的...
我们创建了主机的 Ansible 清单文件,现在我们需要在清单中声明我们的 VPC 作为节点,类似于我们如何定义网络节点。唯一的例外是 VPC 没有管理 IP 地址,因此我们不为这些 VPC 指定ansible_host参数。
我们需要在我们的清单文件中创建以下组:
-
一个 US 组,将所有美国的 VPC 分组在一起
-
一个 EU 组,将所有欧洲的 VPC 分组在一起
-
prod_vpcs,将所有我们的生产 VPC 分组在一起
我们还需要定义ansible.cfg文件,其中包含我们在所有先前示例中使用的所有配置选项。我们需要指定包含我们将用于加密所有敏感信息的加密密码的 vault 密码文件。
身份验证到您的 AWS 账户
在这个示例中,我们将概述如何创建所需的凭据,以便以编程方式对我们的 AWS 账户进行身份验证,并且如何使用 Ansible Vault 来保护这些凭据。这是为了能够在所有后续示例中运行任何 Ansible 模块而必须的步骤。
准备工作
Ansible 控制器必须具有互联网访问权限,并且 Ansible 清单必须按照上一个示例中的说明进行设置。此外,执行这些步骤的用户必须具有在 AWS 账户上创建新用户所需的访问权限。
如何做到...
- 使用IAM创建一个新用户,具有编程访问权限,如下所示:
- 为这个新用户分配正确的 IAM 策略,允许他们创建应该管理的所有网络资源(或者为简单起见,使用完全访问策略):
- 完成用户创建,在最后一页,添加用户向导将显示访问密钥 ID 和秘密访问密钥的
.csv文件以供下载。这些参数将用于对该账户的 AWS API 进行身份验证:
- 使用 Ansible Vault 加密访问密钥 ID 和秘密访问密钥,如下所示:
$ ansible-vault encrypt_string <ACCESS_KEY_ID> --name aws_access_key
$ ansible-vault encrypt_string <SECRET_ACCESS_KEY> --name aws_secret_key_id
- 在
ch7_aws内创建group_vars,并在group_vars内创建all.yml文件。使用在上一步中使用ansible-vault加密的密码填充all.yml文件:
ansible_connection: local
aws_access_key: !vault |
$ANSIBLE_VAULT;1.1;AES256
37623631653336633662666138353639653365323637323665353731386661343164393664333434
3430306562623532366137663835636138613633633835660a656235363130303035383965663464
39326130613433643861653933623032393735376466333861326537646563643736356632303435
6631326531666461310a353461396431303765393830636432363430323438373635383132336462
37356163643662623633643965386465656563613533613938666232343063396261
aws_secret_key_id: !vault |
$ANSIBLE_VAULT;1.1;AES256
38353165666437393262303035646531666139623963643066623066346633393964633438626539
6266623937343036376266373463623266316462613139660a336664353564623531393332613433
34336363393962666633363630393631376135656666623862373966643935386665363733376133
6236326462326566320a653364336464363963623136363362666632396133613863376166343135
37373839316430643337353564373062333232656136393564613132333065316638383739326238
3530386534303033636463626664346234653136353534633265
它是如何工作的...
第一步是确保用户帐户通过 API 对 AWS 控制台具有编程访问权限。为了使用户能够对 AWS API 进行身份验证,用户必须被分配两个密码,这些密码在用户创建或用户请求更改密码时由 AWS 生成。这两个密码是访问密钥 ID 和秘密访问密钥。这两个密码仅在创建时可见,并且 AWS 会在 CSV 文件中提供它们供您下载。此外,我们需要确保此用户具有正确的 IAM 权限来创建必要的资源(VPC、子网、路由表等)。因此,在我们的示例中,为这个新用户分配了管理员策略,这使他们可以完全访问 AWS 账户以创建任何资源(如 EC2 实例、VPC、子网等)。我们概述了创建新用户的步骤,如果用户已经具有编程访问权限和所需的 IAM 权限,则这些步骤是可选的;我们只是为了完整性而演示了这一点。
由于我们在 CSV 文件中以明文形式生成了 AWS 为该账户生成的密码,我们可以使用 Ansible Vault 加密这些密码,并将它们存储在group_vars/all.yml文件中,以便在创建 VPC 的所有资源时使用这些凭据。我们将这些密码加密后存储到aws_access_key和aws_secret_key_id参数中,使用ansible-vault encrypt_string命令进行加密。Ansible Vault 使用我们在ansible.cfg文件中声明的 Vault 密码文件,该文件具有我们将用于加密所有这些密码的加密密码。
在下一个教程中,我们将概述如何使用我们创建的这些加密变量来在创建 VPC 时进行 AWS 控制台身份验证。
使用 Ansible 部署 VPC
在这个教程中,我们将概述如何使用 Ansible 部署 AWS VPC。AWS VPC 是 AWS 中的基础网络构造,可以被视为管理员在其 AWS 账户中创建的云中的虚拟数据中心。为了开始构建 AWS 中的任何其他基础设施相关服务,必须首先创建一个 VPC。我们将概述如何描述所有必需的 VPC 以及如何使用 Ansible 自动化它们的创建。
准备就绪
要连接到 AWS API,AWS 控制机器必须连接到互联网。AWS 账户也必须按照前面的教程准备好,具有所需的 AWS 凭据。
如何做...
- 在
group_vars目录下创建us.yml和eu.yml文件,并填写这些文件与 AWS 区域名称定义,如下所示:
$ cat group_vars/eu.yml
aws_region: eu-west-1
$ cat group_vars/eu.yml
aws_region: us-east-1
- 在
host_vars目录下创建eu_prod_vpc.yml和us_prod_vpc.yml文件,并填写 VPC 参数,如下所示:
$ cat host_vars/eu_prod_vpc.yml
vpc_name: EU_Prod_Public_VPC
vpc_cidr: 10.3.0.0/16
vpc_tags:
role: prod
region: eu EU $ cat host_vars/us_prod_vpc.yml
vpc_name: US_Prod_Public_VPC
vpc_cidr: 10.1.0.0/16
vpc_tags:
role: prod
region: US
- 创建一个新的 playbook,
pb_aws_net_build.yml,并填写如下:
$ cat pb_aws_net_build.yml
- name: Create all AWS Networks
hosts: prod_vpcs
environment:
AWS_ACCESS_KEY: "{{ aws_access_key }}"
AWS_SECRET_KEY: "{{ aws_secret_key_id }}"
tasks:
- name: Create New VPC
ec2_vpc_net:
cidr_block: "{{ vpc_cidr }}"
region: "{{ aws_region }}"
name: "{{ vpc_name }}"
state: "{{ vpc_state | default('present') }}"
tags: "{{ vpc_tags }}"
register: create_vpc
它是如何工作的...
AWS 具有全球存在,并将其基础设施的每个部分分隔到世界的每个部分中的区域。AWS 区域是世界某个地区的 AWS 设施集合,AWS 中的每个区域被认为是一个具有自己的编排和管理系统的隔离故障域。因此,当我们创建 VPC 时,我们需要指定将在哪个区域部署此 VPC,因此我们需要在我们的 Ansible 变量中描述这些信息。在我们的情况下,我们将所有 VPC 的 AWS 区域指定为us-east-1和所有 VPC 的 AWS 区域指定为eu-west-1。这是通过在eu.yml和us.yml文件以及group_vars目录下定义aws_region变量来实现的。
AWS 区域的这种逻辑对于大多数 AWS 中特定于区域的服务至关重要,我们将构建的所有网络构造都是特定于区域的。对于几乎所有的 AWS Ansible 模块,我们需要指定 AWS 区域,以便发起正确的 API 调用到指定区域的正确 API 端点。这是因为每个区域的 API 端点具有不同的 FQDN。有关 AWS 在所有区域中所有服务的 API 端点的更多信息,请使用以下链接:
docs.aws.amazon.com/general/latest/gr/rande.html
我们需要在host_vars目录下为每个 VPC 声明变量,并为每个 VPC 创建一个 YAML 文件。我们需要指定 VPC 名称、前缀和应分配给 VPC 的标记。最后,我们需要创建 Ansible 剧本来构建我们的基础设施,并在剧本中使用一个新选项,即环境。此选项在剧本执行期间创建临时环境变量(AWS_ACCESS_KEY和AWS_SECRET_KEY)。这些环境变量的值设置为group_vars/all.yml文件中定义的aws_access_key和aws_secret_key_id变量的相同值。这样,在剧本执行期间,这些环境变量中包含的值可以用于验证每个任务中的 AWS 模块的所有 API 调用。
我们可以使用ec2_vpc_net Ansible 模块在 AWS 云上创建 VPC,并可以使用region属性指定将部署此 VPC 的 AWS 区域。我们需要定义其 IP 前缀、名称和任何相关标记。所有这些信息都来自我们为此 VPC 在host_vars文件中定义的变量。
当模块创建 VPC 时,它会返回创建的 VPC 的所有信息,我们可以将这些信息保存在一个名为create_vpc的新变量中。
以下是 VPC 创建任务返回的数据片段:
"create_vpc": {
"vpc": {
"cidr_block": "10.1.0.0/16",
< -- Output Omitted for brevity -->
"dhcp_options_id": "dopt-b983c8c2",
"id": "vpc-0d179be0eb66847f3",
"instance_tenancy": "default",
"is_default": false,
"owner_id": "955645556619",
"state": "available",
"tags": {
"Name": "US_Prod_Public_VPC",
"region": "US",
"role": "prod"
}
}
}
以下屏幕截图概述了从控制台在 AWS 上创建的 VPC:
另请参阅
有关ec2_vpc_net模块和此模块中可用的其他参数的更多信息,请使用以下 URL:
docs.ansible.com/ansible/latest/modules/ec2_vpc_net_module.html
使用 Ansible 部署子网
在这个配方中,我们将概述如何使用 Ansible 在 AWS VPC 中部署子网。子网是 AWS 中的基本网络构造,以便为部署在 AWS 云上的应用程序提供更弹性。通过将子网映射到不同的可用区,可以实现额外的弹性。使用这种逻辑,我们可以通过将资源分布到不同的可用区来为我们的部署提供高可用性。
准备工作
Ansible 控制机必须具有互联网可达性,并且 VPC 必须根据之前的配方进行预配。
如何做...
- 使用如下所示的子网数据更新
host_vars/eu_prod_vpc.yml文件。对于host_vars/us_prod_vpc.yml也是一样,包括所有子网的数据:
$ cat host_vars/eu_prod_vpc.yml
< -- Output Omitted for brevity -->
vpc_subnets:
eu-prod-public-a:
cidr: 10.3.1.0/24
az: "{{ aws_region }}a"
tags: "{{ vpc_tags }}"
public: true
eu-prod-public-b:
cidr: 10.3.2.0/24
az: "{{ aws_region}}b"
tags: "{{ vpc_tags }}"
public: true
- 更新
pb_aws_net_build.yml剧本,并填充新任务以构建子网:
- name: "set fact: VPC ID"
set_fact:
vpc_id: "{{ create_vpc.vpc.id }}"
- name: create VPC subnets
ec2_vpc_subnet:
region: "{{ aws_region }}"
vpc_id: "{{ vpc_id }}"
cidr: "{{ item.value.cidr }}"
az: "{{ item.value.az }}"
tags: "{{item.value.tags | combine({ 'Name': item.key })}}"
with_dict: "{{ vpc_subnets }}"
register: create_vpc_subnets
工作原理...
可用区是在 AWS 区域内为物理基础设施提供弹性的构造。为了有效使用可用区,我们需要将 VPC 内的基础设施分配到区域内的不同可用区。这是通过使用 AWS 子网来实现的。
在我们的示例部署中,我们使用了两个子网,分布在两个可用区,以提供我们的网络设置高可用性。我们使用vpc_subnets变量声明我们将在每个 VPC 中部署的子网。这些变量包括我们将在每个子网中使用的 CIDR(必须是 VPC CIDR 的子集),我们希望将此子网附加到的可用区,最后,我们希望分配给此子网的标签。我们使用 AWS 区域加上后缀(a,b,c等)构建可用区的名称。这是 AWS 用于命名区域内可用区的命名约定。
为了在 AWS 中创建子网,我们需要将子网与其父 VPC 关联。为了做到这一点,我们需要在创建子网的 API 调用期间指定vpc-id参数。这个vpc-id是 AWS 在创建 VPC 时分配给 VPC 的唯一标识符。我们从创建 VPC 的任务中获取这个值,并将此任务的输出保存到vpc_create变量中。我们可以使用这个变量来检索 VPC 的 ID,并使用set_fact模块将其分配给vpc-id变量。
最后,我们可以使用ec2_vpc_subnet模块构建子网,以在每个 VPC 中创建必要的子网,并循环遍历vpc_subnets数据结构,以构建所有所需的子网。
以下截图显示了在我们的US_Prod VPC 中在 AWS 云上正确规定的子网:
以下是分配给此子网的标签:
另请参阅
有关ec2_vpc_subnet模块和此模块中可用的其他参数的更多信息,请使用以下 URL:
docs.ansible.com/ansible/latest/modules/ec2_vpc_subnet_module.html#ec2-vpc-subnet-module
使用 Ansible 部署 IGWs
在这个配方中,我们将概述如何使用 Ansible 部署Internet Gateways(IGWs)到我们的 AWS VPC。IGWs 是我们从 VPC 到互联网的出口点,以便到达公共外部目的地。由于我们正在构建一个面向公众的服务,我们需要从我们的 VPC 到互联网的可达性。这是通过 AWS 云中的 IGW 构造实现的。
准备工作
Ansible 控制机必须具有互联网可达性,并且 VPC 必须根据先前的配方进行了规定。
如何操作...
- 更新
eu_prod_vpc.yml文件与 IGW 数据,如下所示,并对us_prod_vpc.yml执行相同操作:
$ cat host_vars/eu_prod_vpc.yml
< -- Output Omitted for brevity -->
igw_name: eu_prod_igw
$ cat host_vars/eu_prod_vpc.yml
< -- Output Omitted for brevity -->
igw_name: us_prod_igw
- 更新
pb_aws_net_build.ymlplaybook,并填充新任务以构建 IGW 节点:
- name: Create IGW
ec2_vpc_igw:
region: "{{ aws_region }}"
vpc_id: "{{ vpc_id }}"
state: present
tags: "{{ vpc_tags | combine({'Name': igw_name}) }}"
register: vpc_igw_create
- name: Extract VPC IGW ID
set_fact:
igw_id: "{{ vpc_igw_create.gateway_id }}"
工作原理...
IGW 网络构造是我们从 VPC 到互联网公共目的地的出口点。IGW 附加到 VPC,并为位于 VPC 内的任何资源(如 EC2 或 RDS 实例)提供互联网连接。为了创建 IGW,我们需要指定要将此 IGW 附加到的 VPC。因此,我们需要 VPC 的 ID。
正如我们在前面的配方中讨论的,当我们创建 VPC 时,我们会得到 VPC ID,并且我们可以使用一个单独的任务保存这个变量。我们可以在 IGW 的创建过程中使用这个变量的值。我们可以使用ec2_vpc_igw模块创建 IGW,并指定我们希望将此 IGW 部署到的区域。我们还可以指定 IGW 将附加到的 VPC ID。最后,我们可以指定要分配给 IGW 节点的标签。IGW 标签是可选的,但在使用自动部署时非常重要,因为它们允许我们引用我们创建的对象。我们将在后续的配方中概述在部署验证和事实收集时使用标签。
当我们部署新的 IGW 时,ec2_vpc_igw模块返回在 AWS 内部配置的 IGW 参数。一个特别重要的参数是igw-id。此参数唯一标识了配置的 IGW 节点,我们在引用此 IGW 节点相关的任何操作时必须使用它。
以下是由ec2_vpc_igw返回的 IGW 参数片段,我们在us_prod_vpc中的 IGW 节点中捕获了这些参数,并将其存储在vpc_igw_create变量中:
ok: [us_prod_vpc] => {
"vpc_igw_create": {
"changed": true,
"failed": false,
"gateway_id": "igw-05d3e4c664486790b",
"tags": {
"Name": "us_prod_igw",
"region": "US",
"role": "prod"
},
"vpc_id": "vpc-0abc32281330c9bc6"
}
}
在上一个任务中,我们捕获了ec2_vpc_igw返回的gateway-id变量,并将其存储在一个新变量igw_id中,我们将在后续任务中引用 IGW 节点时使用它。
以下截图概述了在 VPC 中配置和附加的 IGW 节点:
另请参阅
有关ec2_igw_vpc模块和此模块中可用的其他参数的更多信息,请使用以下 URL:
docs.ansible.com/ansible/latest/modules/ec2_vpc_igw_module.html#ec2-vpc-igw-module
使用 Ansible 控制 VPC 内的路由
在这个示例中,我们将概述如何调整 AWS VPC 内的路由,以控制 VPC 内子网中的流量转发。通过控制 VPC 内的路由,我们可以自定义 VPC 设计以及 VPC 内的流量转发方式,以及如何将流量发送到外部目的地。
准备工作
Ansible 控制机必须具有互联网可达性,并且 VPC 必须按照上一个示例进行配置。
操作步骤
- 更新
eu_prod_vpc.yml文件,其中包含路由表数据,如下所示,并对us_prod_vpc.yml执行相同操作:
$ cat host_vars/eu_prod_vpc.yml
< -- Output Omitted for brevity -->
route_table:
tags:
Name: eu_public_rt
igw:
- dest: 0.0.0.0/0
gateway_id: "{{ igw_id }}"
public:
- eu-prod-public-a
- eu-prod-public-b
- 更新
pb_aws_net_build.ymlplaybook,并填充以下任务以将路由表附加到我们创建的 VPC:
- name: Get Default VPC Route Table
ec2_vpc_route_table_facts:
region: "{{ aws_region }}"
filters:
vpc-id: "{{ vpc_id }}"
register: vpc_route_table_facts
tags: rt
- name: Extract Route Table IDs
set_fact:
rt_id: "{{vpc_route_table_facts.route_tables[0].id }}"
tags: rt
- 更新 playbook 并填充以下任务以更新所需路由的路由表:
- name: Update Default VPC Route Table
ec2_vpc_route_table :
region: "{{ aws_region }}"
vpc_id: "{{ vpc_id }}"
route_table_id: "{{ rt_id }}"
routes: "{{ route_table.igw }}"
subnets: "{{ route_table.public }}"
lookup: id
state: present
tags: "{{ vpc_tags | combine(route_table.tags) }}"
工作原理
到目前为止,我们已经设置了 VPC、子网和 IGW。但是,尽管 IGW 节点已连接到互联网并附加到 VPC,但 VPC 相关联的路由表仍未更新,也没有路由指向 IGW,因此 VPC 内的流量都不会使用 IGW 节点。
以下是在更改路由表之前us_prod_vpc的默认路由表的片段:
AWS VPC 具有默认路由表,该路由表分配给 VPC 和所有未分配特定路由表的子网。因此,默认情况下,VPC 中的所有子网都与 VPC 的默认路由表相关联。
以下是一个截图,显示了在us_prod_vpc中创建的子网与默认路由表相关联的情况:
在我们为每个 VPC 声明的 VPC 定义中,我们包含了一个名为route_table的新数据结构,其中包含我们需要调整 VPC 的路由表并将所有子网与其关联的所有信息。
在这个示例中,我们将执行的第一个任务是获取与我们创建的 VPC 相关联的默认路由表的 ID。我们将使用ec2_vpc_route_table_facts模块来获取路由表的信息,并提供 VPC ID 来唯一标识 VPC。我们可以将默认路由表的 ID 存储在新变量rt_id中。
以下是我们从ec2_vpnc_facts模块中检索到的路由表信息的片段:
ok: [us_prod_vpc] => {
"vpc_route_table_facts": {
"route_tables": [
{
< -- Output Omitted for brevity --> ],
"id": "rtb-0b6669ba5fd9eb9c8",
"routes": [
{
"destination_cidr_block": "10.1.0.0/16",
"gateway_id": "local",
< -- Output Omitted for brevity -->
}
],
"tags": {},
"vpc_id": "vpc-005b1dcb981791d86"
}
]
}
}
一旦我们有了与 VPC 关联的路由表的 ID,我们可以使用ec2_vpc_route_table模块来调整与 VPC 关联的默认路由表的路由表。我们必须提供 VPC 和路由表 ID 以唯一标识我们要修改的确切路由表。我们可以指定要在路由表中注入的路由和要与此路由表关联的子网。我们可以注入默认路由并将其指向我们在上一个配方中使用igw-id创建的 IGW。
在调整路由后,以下截图概述了我们 VPC 的路由表:
以下截图概述了我们在 VPC 中有的两个子网现在与此默认路由表关联:
另请参阅
有关与 AWS VPC 路由表交互的多个模块以及相关模块的更多信息,请使用以下链接:
使用 Ansible 部署网络 ACL
在本配方中,我们将概述如何在 AWS 上部署网络 ACLs(NACLs)。NACLs 是 AWS 中可用的安全解决方案之一,用于保护在 AWS 云中部署的计算资源。在本配方中,我们将概述如何描述和自动化在 AWS 中部署 NACLs 的过程。
准备工作
Ansible 控制机必须具有互联网可达性,以便到达 AWS API 端点,并且 VPC 和子网必须根据先前的配方进行预配。
如何操作...
- 使用 NACL 定义数据更新
eu_prod_vpc.yml文件,如下所示,并对us_prod_vpc.yml执行相同操作:
$ cat host_vars/eu_prod_vpc.yml
< -- Output Omitted for brevity -->
network_acls:
- name: EU_Prod_ACLs
subnets: "{{ vpc_subnets.keys() | list }}"
ingress_rules:
- [100,'tcp','allow','0.0.0.0/0',null,null,80,80]
- [200,'tcp','allow','0.0.0.0/0',null,null,443,443]
- 更新
pb_aws_net_build.ymlplaybook,并填充以下任务以创建 NACLs:
- name: Create Network ACLs
ec2_vpc_nacl:
region: "{{ aws_region }}"
vpc_id: "{{ vpc_id }}"
name: "{{ item.name }}"
subnets: "{{ item.subnets }}"
ingress: "{{ item.ingress_rules }}"
tags: "{{ vpc_tags | combine({'Name':item.name}) }}"
loop: "{{ network_acls }}"
工作原理...
AWS NACL 是有能力基于 L3 和 L4 IP 地址信息允许或拒绝 IP 流量的无状态 ACL。它们在子网级别执行,并与子网关联以保护在子网上预配的所有资源。它们可以阻止入口(进入子网的流量)或出口(离开子网的流量)方向的流量。NACL 中的规则是根据规则编号进行处理的,因此第一个匹配的规则将应用于流量流向。
所有子网都附加了默认 NACL,并且 AWS 为默认 NACL 设置了以下规则:
- 在入口处,允许所有流量。以下截图概述了应用于默认 NACL 的规则:
- 在出口处,允许所有流量。以下截图概述了应用于默认 NACL 的规则:
在我们的示例设置中,我们将在所有子网上应用 NACL,强制执行以下安全策略:
-
必须允许所有 TCP 流量到端口
80和443。 -
任何其他流量应该被丢弃。
默认情况下,任何 NACL 的末尾都有一个DENY规则,会丢弃所有流量。
我们定义了network_acls数据结构,其中包含 NACL 定义和设置所有所需字段以在我们的 EU 和 US 地区的所有子网上设置所需的 NACLs。在此数据结构中,我们需要定义以下参数:
-
Name:这是 NACL 的名称,它作为标识符。 -
Subnets:这定义了应与此 NACL 关联的子网。我们使用我们的vpc_subnets定义中的数据来构建此列表。 -
Ingress_rules:这定义了应作为此 NACL 的一部分应用的所有规则,方向为入站。 -
Engress_rules:这定义了应作为此 NACL 的一部分应用的所有规则,方向为出站。
我们可以在 playbook 中创建一个新任务,使用ec2_net_nacl来配置 NACL 并将其附加到我们所有的子网。
以下截图概述了在EU_prod VPC 中部署的新 NACL:
以下截图概述了与我们在EU_prod VPC 中的 NACL 相关联的子网:
另请参阅
有关ec2_net_nacl Ansible 模块以及此模块支持的不同参数的更多信息,请参阅以下 URL:
docs.ansible.com/ansible/latest/modules/ec2_vpc_nacl_module.html
使用 Ansible 进行部署验证
在这个配方中,我们将概述如何收集 AWS 中不同网络组件的操作状态,例如 VPC 和子网,并检查我们的部署是否按照我们的设计实施。
准备工作
Ansible 控制机必须具有互联网可达性,并且我们在先前的配方中概述的所有网络组件都应该就位。
如何做...
- 创建一个新的
pb_vpc_validate.ymlplaybook,并填充它以验证 VPC 的构建任务:
$ cat pb_vpc_validate.yml
- name: Validate VPC Build
hosts: all
gather_facts: no
environment:
AWS_ACCESS_KEY: "{{ aws_access_key }}"
AWS_SECRET_KEY: "{{ aws_secret_key_id }}"
AWS_REGION: "{{ aws_region }}"
tasks:
- name: Get VPC facts
ec2_vpc_net_facts:
filters:
"tag:Name": "{{ vpc_name }}"
register: vpc_facts
- name: Validate VPC Info
assert:
that:
- vpc_facts.vpcs[0].cidr_block == vpc_cidr
- vpc_facts.vpcs[0].tags.Name == vpc_name
when: vpc_facts.vpcs != []
- 使用以下任务更新 playbook 以收集 AWS 子网的信息:
- name: Extract VPC ID
set_fact:
vpc_id: "{{ vpc_facts.vpcs[0].id }}"
- name: Get Subnet facts
ec2_vpc_subnet_facts:
filters:
vpc-id: "{{ vpc_id }}"
register: vpc_subnet_facts
tags: subnet
- 使用以下任务更新 playbook 以验证 AWS 子网的状态:
- name: Validate VPC Subnets Info
assert:
that:
- vpc_subnet_facts.subnets |
selectattr('tags.Name','equalto',item.key) |
map(attribute='cidr_block') |
list | first == item.value.cidr
- vpc_subnet_facts.subnets |
selectattr('tags.Name','equalto',item.key) |
map(attribute='availability_zone') |
list | first == item.value.az
with_dict: "{{ vpc_subnets }}"
工作原理...
我们可以创建一个新的 playbook,使用ec2_vpc_net_facts和ec2_vpc_subnet_facts Ansible 模块来收集 VPC 和子网的信息。我们可以收集这些模块返回的数据,并使用assert模块来验证状态,如下所示:
- VPCs:
-
检查为 VPC 分配的名称是否按照我们的设计进行了配置。
-
检查为 VPC 分配的 CIDR 块是否按照我们的设计部署。
- 子网:
-
检查为子网分配的 CIDR 是否正确配置。
-
检查子网是否在正确的可用区中进行了配置。
我们可以通过将事实模块返回的操作状态与我们在group_vars或host_vars变量中为每个 VPC 定义的元数据进行比较,执行所有前述验证。
在ec2_vpc_net_facts任务中,我们使用filters参数仅基于其Name标签选择我们的 VPC。默认情况下,此模块将返回此区域内所有 VPC 的信息。
在ec2_vpc_subnet_facts任务中,我们使用filters参数仅检索我们的 VPC 的子网数据,因为默认情况下,此模块将返回此区域内所有 VPC 的所有子网信息。
另请参阅
有关 AWS 中不同网络资源的事实收集模块的更多信息,请使用以下链接:
-
docs.ansible.com/ansible/latest/modules/ec2_vpc_net_facts_module.html#ec2-vpc-net-facts-module -
docs.ansible.com/ansible/latest/modules/ec2_vpc_subnet_facts_module.html#ec2-vpc-subnet-facts-module -
docs.ansible.com/ansible/latest/modules/ec2_vpc_igw_facts_module.html#ec2-vpc-igw-facts-module
使用 Ansible 在 AWS 上解除资源
在这个配方中,我们将概述如何在 AWS 中解除完整的网络及其所有相关网络资源。这概述了我们如何可以使用 Ansible 轻松构建和拆除云上的资源,只需执行一个简单的 playbook。
准备工作
Ansible 控制机必须具有互联网可达性,并且我们在前面的配方中概述的所有网络组件都应该就位。
如何做...
- 创建一个新的
pb_delete_vpc.ymlplaybook,包括以下任务来收集 VPC 的事实:
$ cat pb_delete_vpc.yml
- name: Delete all VPC resources
hosts: all
gather_facts: no
environment:
AWS_ACCESS_KEY: "{{ aws_access_key }}"
AWS_SECRET_KEY: "{{ aws_secret_key_id }}"
AWS_REGION: "{{ aws_region }}"
tasks:
- name: Get VPC facts
ec2_vpc_net_facts:
filters:
"tag:Name": "{{ vpc_name }}"
register: vpc_facts
- name: Extract VPC ID
set_fact:
vpc_id: "{{ vpc_facts.vpcs[0].id }}"
- 更新 playbook,添加以下任务以删除 VPC 内的所有子网和 IGW 节点:
- name: Start Delete VPC Resources
block:
- name: Delete Subnets
ec2_vpc_subnet:
cidr: "{{ item.value.cidr }}"
vpc_id: "{{ vpc_id }}"
state: absent
with_dict: "{{ vpc_subnets }}" - name: Delete IGW
ec2_vpc_igw:
vpc_id: "{{ vpc_id }}"
state: absent
- 更新 playbook,添加以下任务以删除所有 NACLs:
- name: Delete NACLs
ec2_vpc_nacl:
name: "{{ item.name }}"
vpc_id: "{{ vpc_id }}"
state: absent
loop: "{{ network_acls }}"
- 更新 playbook,添加最终任务以删除所有 VPCs:
- name: Delete VPC
ec2_vpc_net:
cidr_block: "{{ vpc_cidr }}"
name: "{{ vpc_name }}"
state: absent
when: vpc_id is defined
工作原理...
我们可以从收集 VPC 事实开始我们的新 playbook,以获取部署的 VPC 的 VPC ID。一旦我们获得了这些信息,我们就可以开始删除资源。然而,删除资源的顺序很重要。我们需要先删除任何依赖资源,因此我们必须先删除子网,然后才能删除 VPC。例如,如果有 EC2 实例连接到子网,我们必须先删除这些 EC2 实例,然后才能删除子网。因此,在我们的情况下,我们需要先删除子网,然后是 IGW 节点,最后才是删除 VPC。
在所有这些任务中,我们使用的是前面配方中概述的完全相同的模块。唯一的变化是我们将状态设置为不存在,并且我们提供所需的 VPC ID 来唯一标识我们需要从中删除所需资源的 VPC。
最后,当我们开始删除 VPC 内的资源时,我们首先验证是否存在 VPC ID。如果资源已经被删除并且我们再次运行 playbook,删除步骤将被跳过,因为facts任务不会检索到 VPC ID。