Ansible,第三部分:Playbook

Playbook 使 Ansible 比以往任何时候都更加强大。

老实说,即使 Ansible 只有其临时命令模式,它仍然会是一个强大且有用的工具,用于自动化大量的计算机。事实上,如果不是因为一些功能,我可能会考虑坚持使用临时命令模式,并将一堆临时命令添加到 Bash 脚本中,然后完成学习。然而,这些额外的功能使得持续努力变得非常值得。

用 YAML 驯服野兽

Ansible 特意使用易于阅读的配置文件来制作“playbook”,这些文件充满了独立的 Ansible “任务”。任务基本上是以配置文件形式写出的临时命令,使其更有条理且易于扩展。配置文件使用 YAML,它代表“Yet Another Markup Language”(另一种标记语言)。它是一种易于阅读的标记语言,但它确实依赖于空格,这在大多数配置文件中并不常见。一个简单的 playbook 看起来像这样


---

- hosts: webservers
  become: yes
  tasks:
    - name: this installs a package
      apt: name=apache2 update_cache=yes state=latest

    - name: this restarts the apache service
      service: name=apache2 enabled=yes state=restarted

内容应该很容易识别。它基本上是将两个临时命令分解成一个 YAML 配置文件。有几个重要的事情需要注意。首先,每个文件名都以 .yaml 结尾,并且每个 YAML 文件都必须以三个连字符开头。此外,如上所述,空格很重要。最后,连字符应该在哪个部分之前,以及何时应该适当间隔常常令人困惑。基本上,每个新部分都需要以 - 符号开头,但通常很难判断什么应该是它自己的部分。尽管如此,随着您创建越来越多的 playbook,它开始变得自然而然。

上述 playbook 将通过键入以下内容执行


ansible-playbook filename.yaml

这相当于以下两个命令


ansible webservers -b -m apt -a "name=apache2
 ↪update_cache=yes state=latest"
ansible webservers -b -m service -a "name=apache2
 ↪enabled=yes state=restarted"

处理您的处理程序

但一点组织仅仅是 playbook 如此强大的开始。首先,有“处理程序”的概念,这些处理程序是仅在“通知”任务已更改时才执行的任务。这究竟是如何工作的?让我们重写上面的 YAML 文件,使第二个任务成为处理程序


---

- hosts: webservers
  become: yes
  tasks:
    - name: this installs a package
      apt: name=apache2 update_cache=yes state=latest
      notify: enable apache

  handlers:
    - name: enable apache
      service: name=apache2 enabled=yes state=started

从表面上看,这看起来与仅执行多个任务非常相似。当第一个任务(安装 Apache)执行时,如果进行了更改,它会通知“启用 apache”处理程序,该处理程序确保 Apache 在启动时启用并当前正在运行。其意义在于,如果 Apache 已经安装,并且没有进行任何更改,则永远不会调用处理程序。这使得代码效率更高,但也意味着不会不必要地中断已经运行的 Apache 进程。

处理程序还有其他微妙的节省时间的问题——例如,多个任务可以调用一个处理程序,但无论调用多少次,它都只执行一次。但真正需要记住的重要事情是,处理程序仅在 Ansible 任务在远程系统上进行更改时才执行(通知)。

只说事实,夫人

变量替换在 playbook 内部工作非常简单。这是一个简单的例子


---

- hosts: webservers
  become: yes
  vars:
    package_name: apache2
  tasks:
    - name: this installs a package
      apt: "name={{ package_name }} update_cache=yes state=latest"
      notify: enable apache

  handlers:
    - name: enable apache
      service: "name={{ package_name }} enabled=yes state=started"

应该很容易理解上面发生了什么。请注意,我确实将整个模块操作部分放在引号中。这并非总是必需的,但有时 Ansible 对未加引号的变量替换有点奇怪,所以我总是尝试在涉及变量时将内容放在引号中。

然而,关于变量真正有趣的事情是关于每个主机的“收集的事实”。您可能会注意到,在执行 playbook 时,Ansible 首先执行“收集事实...”,它完成时没有错误,但实际上似乎什么也没做。真正发生的事情是,系统信息正在填充到可以在 playbook 内部使用的变量中。要查看“收集的事实”的完整列表,您可以执行临时命令


ansible webservers -m setup

您将获得从各个主机生成的庞大的事实列表。其中一些特别有用。例如,ansible_os_family 将根据您使用的发行版返回类似“RedHat”或“Debian”的内容。Ubuntu 和 Debian 系统都返回“Debian”,而 Red Hat 和 CentOS 将返回“RedHat”。虽然这当然是有趣的信息,但当不同的发行版使用不同的工具时,它真的很有用——例如,apt 与 yum。

变得详细

从 Ansible 临时命令转移到 playbook 的挫败感之一是,在 playbook 模式下,Ansible 往往对输出保持相当沉默。在临时命令模式下,您通常可以看到正在发生的事情,但在 playbook 中,您只知道它是否完成正常,以及是否进行了更改。有两种简单的方法可以改变这一点。第一种方法是在执行 ansible-playbook 时添加 -v 标志。这会增加详细程度,并在执行某些操作时提供大量反馈。不幸的是,它经常给出如此多的信息,以至于实用性在其中迷失。尽管如此,在紧急情况下,只需添加 -v 标志即可有所帮助。

如果您正在创建 playbook 并希望沿途收到通知,则 debug 模块真的是您的朋友。在临时命令模式下,debug 模块使用起来没有多大意义,但在 playbook 中,它可以充当关于正在发生的事情的“报告”工具。例如


---

- hosts: webservers
  tasks:
   - name: describe hosts
     debug: msg="Computer {{ ansible_hostname }} is running
      ↪{{ ansible_os_family }} or equivalent"

上面将向您展示类似图 1 的内容,当您试图弄清楚您正在使用的系统类型时,这非常有用。debug 模块的好处在于它可以显示您想要的任何内容,因此如果值发生变化,您可以将其显示在屏幕上,以便您可以排除 playbook 无法按预期工作的问题。重要的是要注意,debug 模块除了在屏幕上为您显示信息外,不做任何其他事情。它不是日志记录系统;相反,它只是一种在执行期间显示信息(自定义信息,与 verbose 标志不同)的方式。尽管如此,当您的 playbook 变得更加复杂时,它仍然可能非常宝贵。

图 1. Debug 模式是获取有关 playbook 内部发生情况的一些信息的最佳方式。

如果这样则那样

条件语句几乎是每种编程语言的一部分。Ansible YAML 文件也可以利用条件执行,但格式有点古怪。通常,条件在前,然后如果它评估为真,则执行以下代码。对于 Ansible,它有点倒退。任务完全拼写出来,然后在末尾添加一个 when 语句。这使得代码非常易读,但对于一个整个职业生涯都在使用 if/then 思维的人来说,感觉很奇怪。这是一个稍微复杂的 playbook。看看您是否可以解析出在同时具有 Debian/Ubuntu 和 Red Hat/CentOS 系统的环境中会发生什么


---

- hosts: webservers
  become: yes
  tasks:
    - name: install apache this way
      apt: name=apache2 update_cache=yes state=latest
      notify: start apache2
      when: ansible_os_family == "Debian"

    - name: install apache that way
      yum: name=httpd state=latest
      notify: start httpd
      when: ansible_os_family == "RedHat"

  handlers:
    - name: start apache2
      service: name=apache2 enabled=yes state=started

    - name: start httpd
      service: name=httpd enabled=yes state=started

希望 YAML 格式使其相当容易阅读。基本上,这是一个 playbook,它将根据主机安装的发行版类型,在主机上使用 yum 或 apt 安装 Apache。然后,处理程序确保新安装的软件包已启用并正在运行。

很容易看出收集的事实和条件语句的组合有多么有用。值得庆幸的是,Ansible 并没有止步于此。与其他配置管理系统一样,它包含编程和脚本语言的大多数功能。例如,有循环。

再来一遍,山姆

如果说 Ansible 有一项做得好的事情,那就是循环。坦率地说,它支持如此多种不同类型的循环,我无法在此处全部介绍。找出适合您情况的完美循环类型的最佳方法是直接阅读 Ansible 文档

对于简单列表,playbook 使用一种熟悉的、易于阅读的方法来执行多个任务。例如


---

- hosts: webservers
  become: yes

  tasks:
    - name: install a bunch of stuff
      apt: "name={{ item }} state=latest update_cache=yes"
      with_items:
        - apache2
        - vim
        - chromium-browser

这个简单的 playbook 将使用 apt 模块安装多个软件包。请注意名为 item 的特殊变量,它在 with_items 部分中一次替换一个项目。同样,这很容易理解并在您自己的 playbook 中使用。其他循环的工作方式类似,但它们的格式不同。只需查看文档,了解 Ansible 可以重复类似任务的各种方式。

模板

我经常使用的最后一个模块是模板模块。如果您曾经在文字处理器中使用过邮件合并,模板的工作方式与之类似。基本上,您创建一个文本文件,然后使用变量替换来动态创建自定义版本。我最常这样做是为了创建 HTML 文件或配置文件。Ansible 使用 Jinja2 模板语言,该语言与 playbook 本身中的标准变量替换非常相似。我几乎总是使用的示例是一个自定义 HTML 文件,可以安装在一批远程 Web 服务器上。让我们看一下一个相当复杂的 playbook 和一个随附的 HTML 模板文件。

这是 playbook


---

- hosts: webservers
  become: yes

  tasks:
   - name: install apache2
     apt: name=apache2 state=latest update_cache=yes
     when: ansible_os_family == "Debian"

   - name: install httpd
     yum: name=httpd state=latest
     when: ansible_os_family == "RedHat"

   - name: start apache2
     service: name=apache2 state=started enable=yes
     when: ansible_os_family == "Debian"

   - name: start httpd
     service: name=httpd state=started enable=yes
     when: ansible_os_family == "RedHat

   - name: install index
     template:
       src: index.html.j2
       dest: /var/www/html/index.html

这是模板文件,它必须以 .j2 结尾(它是上面最后一个任务中引用的文件)


<html><center>
<h1>This computer is running {{ ansible_os_family }},
and its hostname is:</h1>
<h3>{{ ansible_hostname }}</h3>
{# this is a comment, which won't be copied to the index.html file #}
</center></html>

这也应该很容易理解。playbook 获取它学到的一些不同的东西,并在远程系统上安装 Apache,无论它们是基于 Red Hat 还是基于 Debian 的。然后,它启动 Web 服务器,并确保 Web 服务器在系统启动时启动。最后,playbook 获取模板文件 index.html.j2,并在将文件复制到远程系统时替换变量。请注意用于注释的 {# #} 格式。这些注释在远程系统上完全擦除,并且仅在 Ansible 机器上的 .j2 文件中可见。

天空才是极限!

我将在我的下一篇文章中完成本系列,我计划在其中介绍如何利用您的 playbook 知识来创建完整的角色并利用可用的社区贡献。Ansible 是一个非常强大的工具,它出奇地简单易懂和使用。如果您一直在尝试使用临时命令,我鼓励您创建 playbook,这将使您能够以最小的努力在大量计算机上执行多个任务。至少,请试用 ansible-playbook 应用程序收集的“事实”,因为这些是 Ansible 的临时命令模式无法获得的东西。下次再见,学习、实验、玩耍并享受乐趣!

资源

第一部分:“Ansible:像系统管理员一样思考的自动化框架” 作者:肖恩·鲍尔斯

第二部分:“Ansible:让事情发生” 作者:肖恩·鲍尔斯

肖恩是Linux Journal的副编辑,并且从一开始就接触 Linux。他对开源充满热情,并且热爱教学。他还喝太多咖啡,这经常在他的写作中体现出来。

加载 Disqus 评论