Ansible 的核心组件之一是剧本文件。Ansible 使用 playbook 文件来定义复杂的任务,这些任务在用户参与有限的情况下针对受管节点执行。在本指南中,您将学习如何创建和使用 Ansible 剧本文件在托管节点上执行任务。
什么是 Ansible 剧本?
剧本本质上是一个包含一个或多个剧本的 YAML 文件。play 是针对/etc/ansible/hosts文件中指定的托管主机执行的一组有序任务。剧本中的每个剧本都代表一个独特的任务,具有目标主机的特定于环境的参数。
剧本非常灵活,可以在多个服务器上无限期重复使用以执行相同的任务。Ansible playbook 通常用于服务器配置、网络设备管理和应用程序部署任务。
先决条件
要遵循本指南,您应该:
- 安装了 Ubuntu 20.04 操作系统和 Ansible 的控制节点。如果您之前没有安装过 Ansible,请按照我们关于如何在 Ubuntu 20.04 上安装和配置 Ansible的指南进行操作。
- 在控制节点的主机文件中配置的单个受管节点将用于运行剧本任务。
创建 Ansible 剧本
让我们从制作和运行我们的第一个 Ansible 剧本开始。在控制节点上的/etc/ansible/目录中创建一个简单的 YAML 文件,如下所示:
sudo vim /etc/ansible/playbook-01.yml
现在使用以下代码填充剧本文件:
--- - name: A simple playbook file hosts: all tasks: - name: Print a sample message debug: msg: Hello World. Welcome to Ansible playbooks!
标记 YAML 文件的---开始。
该name指令的第一个实例指定了该剧的名称。第二个实例指定任务的名称。
该hosts指令指定将在其上执行剧本的目标主机。在此示例中,剧本将在清单文件中指定的所有主机上运行。要定位特定主机,请提供主机的 IP 地址或域名。
该tasks指令是要在目标主机上执行的任务列表。在这个剧本中,我们有一个任务将语句打印到标准输出。
关键字是 Ansible 自带的debug内置模块,在 Playbook 运行时打印语句。此外,在不停止 playbook 的情况下调试语句和变量时,它会派上用场。该debug模块带有一些选项,例如msg和var。该msg选项指定要打印到标准输出的字符串。
执行 Ansible 剧本
要运行剧本,请使用ansible-playbook如下所示的命令:
ansible-playbook /path/to/playbook_file
在我们的示例中,您应该运行以下命令:
ansible-playbook /etc/ansible/playbook-01.yml
在 playbook 执行期间,您应该会看到以下输出:
请注意,执行了两项任务,尽管我们在 Playbook 文件中只定义了一项。
第一个任务收集有关受管节点的事实。Ansible facts 是指以 JSON 格式呈现的特定于主机的系统数据,例如 BIOS 信息、系统日期和时间、操作系统类型和版本以及 IP 地址。它还包括硬件数据,例如块设备、CPU、RAM 和交换空间等等。
第二个任务按照 playbook 文件中的指定将一条简单消息打印到标准输出。ok=2表示成功执行了两个任务。
如果您想获得所有 Ansible 事实的列表,请执行以下命令:
ansible -m setup all
Ansible 剧本模块
Ansible 模块是独立的可重复使用的 Python 脚本,在 Playbook 中被引用以帮助在托管节点上执行特定任务。Ansible 模块可以自动执行托管节点上的各种任务,包括包管理、服务管理、文件管理等等。
在本节中,我们将演示如何通过将模块合并到 Playbook 中来完成各种系统管理任务。
包管理模块
管理软件包是一项基本的系统管理任务。它专门处理在 Linux 服务器中安装和删除软件包。如图所示,Ansible 为主要 Linux 发行版提供了内置包管理模块。
模块 | Linux 发行版 |
---|---|
易于 | Debian / Ubuntu 变体 |
百胜/dnf | CentOS / Rocky 等 RHEL 变体 |
吃豆子 | Arch Linux 和 Arch 变体 |
压缩 | 开放SUSE |
以下剧本文件将 Apache 网络服务器安装在清单文件中网络服务器子组下定义的远程目标上。该apt模块提供两个选项:name指定包名称的选项 ( apache2 ) 和state指示 Ansible 安装最新版本的 Apache 的选项。
--- - name: install Apache hosts: webserver tasks: - name: install Apache webserver on Ubuntu apt: name: apache2 state: latest
运行 playbook 文件后,您应该会得到与我们所拥有的类似的输出:
ansible-playbook /etc/ansible/playbook-02-install-apache.yml
在 RHEL 8 和 CentOS 8 上,可以使用该dnf模块完成相同的任务。在这里,用于 RedHat 衍生产品的 Apache 包由httpd定义。
服务模块
您还可以使用模块来启动、停止和重新启动受管节点上正在运行的服务。例如,要重新启动 Apache 网络服务器,我们将使用service显示的剧本中的模块。
--- - name: Restart Apache hosts: webserver tasks: - name: Restart Apachce webserver service: name: apache2 state: restarted
这是 Playbook 执行的输出:
这是一本展示如何停止网络服务器的剧本。注意state参数从restarted到stop的变化。
--- - name: Stop Apache hosts: webserver tasks: - name: Stop Apache webserver service: name: apache2 state: stopped
从 playbook 执行的输出可以看出任务成功:
要启动网络服务器,请将state参数设置为started。
--- - name: Start Apache hosts: webserver tasks: - name: Start Apache webserver service: name: apache2 state: started
再一次,这里是剧本执行的输出:
复制模块
另一个有用的模块是复制模块。顾名思义,该模块用于将文件从一个位置复制到另一个位置。您可以将文件从 Ansible 控制器复制到远程节点或将文件从远程节点中的一个位置复制到另一个位置。
在下面的 Playbook 文件中,我们将sales_report.txt文件从 Ansible 控制节点复制到/tmp/reports/目录中的远程服务器。此外,我们已使用和选项将所有者和组所有权分配给cherry用户。该选项将八进制文件权限0644分配给文件。ownergroupmode
--- - name: Ansible copy module example hosts: webserver tasks: - name: Copy files from control node to remote node copy: src: /home/user/Documents/sales_report.txt dest: /tmp/reports/ owner: cherry group: cherry mode 0644
Playbook 执行的结果打印如下:
要在远程节点内复制文件,请使用该remote_src选项并将其设置为yes。在下面的示例中,我们正在远程节点上制作 apache2.conf 配置文件的备份副本。简而言之,我们正在复制该文件并将其重命名为apache2.conf.back。
--- - name: Ansible copy module example hosts: webserver tasks: - name: Copy files within the remote node copy: src: /etc/apache2/apache2.conf dest: /etc/apache2/apache2.conf.bak remote_src: yes
剧本运行成功,如图:
行文件模块
该lineinfile模块是用于在一行上执行多种任务的模块,例如修改、替换或向文件添加一行。它可以与正则表达式结合使用以匹配特定行并进行更改。
为了演示其功能,让我们举几个例子。该剧本通过更改两个参数修改远程目标上的 SSH 服务配置。第一个任务将ClientAliveInterval指令设置为
15而第二个任务将ClientAliveCountMax指令设置为4。该regexp选项匹配包含我们试图修改的参数的行。
--- - name: Configure SSH hosts: webserver tasks: - name: Ensure ClientAliveInterval is set to 15 lineinfile: path: /etc/ssh/sshd_config regexp: "^ClientAliveInterval" line: ClientAliveInterval=15 - name: Ensure ClientAliveCountMax is set to 4 lineinfile: path: /etc/ssh/sshd_config regexp: "^ClientAliveCountMax" line: ClientAliveCountMax=4
剧本按照出现的顺序执行这两个任务——从第一个到最后一个:
?专业提示: 在上面的示例中,我们试图匹配以ClientAliveInterval字符串开头的行。Linux 配置文件中的某些行可能被注释掉(即#ClientActiveInterval)。在这种情况下,正则表达式将不匹配,因此lineinfile模块将为您创建一个包含指定字符串的新行。
要向文件添加一行,请指定文件的完整路径、要添加到文件的行,然后将create选项设置为yes。
显示的剧本将新行添加173.82.120.115 cherry.localdomain到远程节点上的/etc/hosts文件。
--- - name: Add a new line to a file hosts: webserver tasks: - name: Add a new line to a file lineinfile: path: /etc/hosts line: 173.82.120.115 cherry.localdomain create: yes
这是剧本执行:
命令模块
Command 模块采用一个命令名称,后跟一个参数列表。该命令在目标节点上执行,但输出不显示到标准输出。
显示的剧本在目标模式上运行"uptime"和"date"命令。
--- - name: Execute commands on remote targets hosts: webserver tasks: - name: Execute the uptime command command: "uptime" - name: Execute the date command command: "date"
该剧本成功运行,但是没有打印出命令的输出。
要将结果打印到标准输出,请使用该shell模块。register使用我们定义的uptime_var和date_var变量的选项捕获这两个命令的输出。这些变量最终由msg选项引用,并将值打印到标准输出。
--- - name: Execute commands on remote targets hosts: webserver tasks: - name: Execute the uptime command shell: "uptime" register: uptime_var - debug: msg: "{{uptime_var.stdout}}" - name: Execute the date command shell: "date" register: date_var - debug: msg: "{{date_var.stdout}}"
在 playbook 执行输出中,您可以看到打印的两个命令的输出:
到目前为止,我们只演示了几个模块。有成百上千的 Ansible 模块用于执行不同的任务。如需更全面的 Ansible 模块列表,请访问Ansible 模块文档页面。
Ansible 剧本变量
如果您是开发人员或程序员,您很可能在代码中无数次使用过变量。与许多编程语言一样,Playbook 中使用变量来存储值。您可以为变量赋值并在剧本中的任何位置引用它。
变量也可以来自外部源,例如变量文件,然后在 Playbook 中引用。处理来自多个来源且名称相同的变量时,将应用特殊的优先级规则。
为了演示在实践中如何使用变量,让我们创建一个 playbook 文件,该文件将打印出两个变量的值:greetings和topic。
--- - name: Ansible variables in practice hosts: webserver vars: greetings: Hello World! topic: Ansible Playbooks tasks: - name: Ansible basic variable example debug: msg: "{{ greetings }}, let's learn {{ topic }}."
该vars部分定义了调试模块将在播放范围内引用的变量列表。剧本文件中指定的所有任务和文件都可以访问这些变量。
在输出中,分配给变量的值已打印到 stdout 以代替变量名称。
或者,您可以有一个可变项目列表。在下面的剧本中,让我们定义一个名为的变量oceans,其中包含代表五大洋的五个值的列表。
--- - name: Ansible list variable example hosts: webserver vars: oceans: - Indian - Atlantic - Pacific - Artic - Southern Antarctic tasks: - name: Ansible list variables example debug: msg: "The five oceans in the world are {{ oceans }}"
该剧本遍历该部分下的值列表,并使用该选项vars将它们打印到标准输出。msg
此外,您可以使用指令中的index [ x ]属性访问变量中的每个值,msg其中 x 是列表中项目的值。第一项用 表示index[0]。例如,要访问列表中的第三项,我们将引用修改为
Ansible 剧本条件
当您需要根据特定条件执行一组任务时,将使用条件语句。在 Ansible Playbooks 中 when 是一个广泛使用的条件语句,它与ORandAND运算符一起使用。
为了更好地阐述条件语句的工作原理,我们将使用两个不同操作系统系列的托管节点进行简单设置:
服务器IP:173.82.120.115 Ubuntu 20.04
服务器IP:173.82.255.207 CentOS 8.3
使用 'when' 语句
考虑下面的剧本。该when语句指示 Playbook 在属于 Debian 操作系统系列的所有服务器上安装 Nginx 网络服务器。我们在ansible_os_family这里使用属于 Ansible facts 对象的变量,因此您不需要在您的剧本中定义它。
--- - name: Ansible when conditional statement hosts: all tasks: - name: install nginx webserver apt: name: nginx state: latest when: ansible_os_family == "Debian"
从 playbook 执行的输出中,我们可以看到 CentOS 主机已被排除,因为它不满足我们的条件。
在“when”语句中使用“AND”运算符
使用and运算符时,必须满足这两个条件。在此示例中,如果受管节点属于 Debian Linux 系列并且其版本号为 20.04,则 playbook 将成功运行任务。
--- - name: Ansible when-and conditional statement hosts: all tasks: - name: install nginx webserver apt: name: nginx state: latest when: ansible_os_family == "Debian" and ansible_distribution_version == "20.04"
由于 Ubuntu 节点符合条件,剧本将成功运行并在其上安装 Nginx,但会跳过 CentOS 8 服务器。
在“when”语句中使用“OR”运算符
如果满足任一条件,则使用or运算符将执行任务。在以下 Playbook 中,data在属于 Debian 或 RedHat Linux 系列的托管节点的主目录中创建了一个名为的新目录。
--- - name: Ansible when-or conditional statement hosts: all tasks: - name: "create a directory if it doesn't exist" file: path: $HOME/data state: directory mode: "0777" when: ansible_os_family == "Debian" or ansible_os_family == "RedHat"
可以预见,该目录是在两个被管节点上创建的,因为它们都属于两个操作系统系列中的任何一个。
为了确认这一点,我们将列出两个节点上主目录的内容。
ssh root@173.82.255.207 "ls -l" ssh root@173.82.120.115 "ls -l"
Ansible 剧本循环
有时,您会发现自己执行重复性任务,这些任务需要您编写执行相同操作的多个任务。举个例子,一个在目标系统上创建新用户的剧本,如图所示。
--- - name: Create new usersr on remote machine hosts: 173.82.120.115 tasks: - name: Create new user Alice user: name: alice state: present - name: Create new user Patric user: name: patric state: present - name: Create new user Tom user: name: tom state: present
显然,这里有很多重复。在处理多个性质相似的任务时,这可能会令人生畏且耗时。这是循环派上用场的地方。
循环提供了一种使用更少代码行执行重复性任务的简化方法。它们遍历使用looporwith_*指令指定的值列表。
该指令列出了由双花括号括起来loop的变量引用的值。item在运行时,剧本遍历loop指令定义的用户列表。然后将每个用户传递给item变量,并以简单而有效的方式创建所有用户。很明显,剧本看起来更简洁,实现相同目标的代码行更少。
--- - name: Create new users in remote machine hosts: 173.82.120.115 tasks: - name: Create new user Alice user: name: '{{ item }}' state: present loop: - alice - patric - tom
这是 Playbook 执行的输出,用于确认用户的创建:
结论
在本指南中,您学习了如何创建和运行 Ansible 剧本。您已经深入研究了各种 Playbook 元素,例如模块、变量、条件语句和循环。