控制 Ansible 的行为:优先级规则

为了在管理环境时为您提供最大的灵活性,Ansible 提供了多种控制其行为的方式:包括如何连接到受管节点,以及连接后如何工作。如果您使用 Ansible 管理大量服务器、网络设备和云资源,您可能会在多个不同地方定义 Ansible 的行为,并以多种不同方式将这些信息传递给 Ansible。这种灵活性虽然方便,但如果您不了解优先级规则,可能会适得其反。

这些优先级规则适用于任何可以通过多种方式(通过配置设置、命令行选项、Playbook 关键字、变量)定义设置的情况。

优先级类别

Ansible 提供了四种控制其行为的来源。按优先级从低(最容易被覆盖)到高(覆盖所有其他项)排列,类别如下:

  • 配置设置

  • 命令行选项

  • Playbook 关键字

  • 变量

  • 直接赋值

每个类别都会覆盖所有低优先级类别中的任何信息。例如,Playbook 关键字将覆盖任何配置设置。

在每个优先级类别内部,适用具体的规则。但一般来说,“最后定义”的值获胜并覆盖之前的任何定义。

配置设置

配置设置 包括来自 ansible.cfg 文件和环境变量的值。在此类别中,在配置文件中设置的值优先级较低。Ansible 使用其找到的第一个 ansible.cfg 文件,忽略所有其他文件。Ansible 按以下顺序搜索 ansible.cfg 的位置:

  • ANSIBLE_CONFIG(如果已设置环境变量)

  • ansible.cfg(在当前目录中)

  • ~/.ansible.cfg(在主目录中)

  • /etc/ansible/ansible.cfg

环境变量的优先级高于 ansible.cfg 中的条目。如果您在控制节点上设置了环境变量,它们将覆盖 Ansible 加载的任何 ansible.cfg 文件中的设置。任何给定环境变量的值遵循标准的 Shell 优先级:最后定义的值覆盖之前的值。

命令行选项

任何命令行选项都将覆盖任何配置设置。

当您直接在命令行输入内容时,您可能会觉得手动输入的值应该覆盖所有其他项,但 Ansible 并不是这样工作的。命令行选项的优先级较低——它们仅覆盖配置。它们不会覆盖 Playbook 关键字、来自清单(inventory)的变量或来自 Playbook 的变量。

您可以通过在命令行中使用 -e 额外变量来覆盖所有其他优先级类别中所有其他来源的所有其他设置,但这不属于命令行选项,而是一种传递变量的方式。

在命令行中,如果您为仅接受单个值的参数传递了多个值,则最后定义的值获胜。例如,这个临时任务(ad hoc task)将以 carol 身份连接,而不是以 mike 身份连接。

ansible -u mike -m ping myhost -u carol

某些参数允许多个值。在这种情况下,Ansible 将追加来自清单文件 inventory1 和 inventory2 中列出的主机的所有值。

ansible -i /path/inventory1 -i /path/inventory2 -m ping all

每个命令行工具的帮助文档中列出了该工具可用选项。

Playbook 关键字

任何Playbook 关键字都将覆盖任何命令行选项和任何配置设置。

在 Playbook 关键字内部,优先级随 Playbook 结构流动;越具体的内容胜过越通用地内容:

  • play (最通用)

  • blocks/includes/imports/roles (可选,且可以包含任务及彼此包含)

  • tasks (最具体)

一个简单的示例

- hosts: all
  connection: ssh
  tasks:
    - name: This task uses ssh.
      ping:

    - name: This task uses paramiko.
      connection: paramiko
      ping:

在这个例子中,connection 关键字在 play 层级被设置为 ssh。第一个任务继承该值,并使用 ssh 连接。第二个任务继承该值,但对其进行了覆盖,并使用 paramiko 连接。同样的逻辑也适用于 blocks 和 roles。play 内的所有任务、blocks 和 roles 都继承 play 层级的关键字;任何任务、block 或 role 都可以通过在其内部定义不同的值来覆盖任何关键字。

请记住,这些是关键字(KEYWORDS),而不是变量。Playbook 和变量文件虽然都定义在 YAML 中,但其意义不同。Playbook 是 Ansible 的命令或“状态描述”结构,而变量是我们用来使 Playbook 更加动态的数据。

变量

Ansible 变量在优先级栈中处于非常高的位置。它们将覆盖任何 Playbook 关键字、任何命令行选项、环境变量以及任何配置文件设置。

具有等效 Playbook 关键字、命令行选项和配置设置的变量被称为连接变量(Connection variables)。这类变量最初为连接参数设计,现在已扩展到包括其他核心变量,如临时目录和 Python 解释器。

连接变量与所有变量一样,可以通过多种方式和在多个地方设置。您可以在清单(inventory)中为主机和组定义变量。您可以在Playbookvars: 块中为任务和 play 定义变量。然而,它们仍然是变量——它们是数据,而不是关键字或配置设置。覆盖 Playbook 关键字、命令行选项和配置设置的变量遵循与任何其他变量相同的变量优先级规则。

当在 Playbook 中设置时,变量遵循与 Playbook 关键字相同的继承规则。您可以为 play 设置一个值,然后在任务、block 或 role 中覆盖它。

- hosts: cloud
  gather_facts: false
  become: true
  vars:
    ansible_become_user: admin
  tasks:
    - name: This task uses admin as the become user.
      dnf:
        name: some-service
        state: latest
    - block:
        - name: This task uses service-admin as the become user.
          # a task to configure the new service
        - name: This task also uses service-admin as the become user, defined in the block.
          # second task to configure the service
      vars:
        ansible_become_user: service-admin
    - name: This task (outside of the block) uses admin as the become user again.
      service:
        name: some-service
        state: restarted

变量作用域:值在多久时间内有效?

在 Playbook 中设置的变量值仅存在于定义它们的 Playbook 对象内部。这些“Playbook 对象作用域”变量对后续对象(包括其他 plays)不可见。

直接与主机或组关联的变量值,包括在清单中定义、由 vars 插件定义,或使用 set_factinclude_vars 等模块定义的变量,对所有 plays 均可见。这些“主机作用域”变量也可以通过 hostvars[] 字典访问。

通过 extra vars 设置的变量在当前运行期间具有全局作用域,并将同时作为“Playbook 对象变量”和“主机变量”存在。

在命令行中使用 -e 额外变量

要覆盖所有其他变量,您可以使用额外变量:在命令行中使用 --extra-vars-e。通过 -e 传递的值虽然本身也是一个命令行选项,但在变量中具有最高优先级。而且,由于变量本身具有高优先级,因此在大多数配置源中,它也具有最高优先级(这可能有点违反直觉)。例如,此任务将以 brian 身份连接,而不是 carol

ansible -u carol -e 'ansible_user=brian' -a whoami all

使用 --extra-vars 时,您必须同时指定变量名称和值。

直接赋值

此类别仅适用于接受直接选项的内容,通常是模块和某些插件类型。大多数模块和动作插件没有其他方式来分配设置,因此在这种情况下很少涉及优先级问题,但某些模块仍然可能这样做,并应在文档中予以体现。

- debug: msg='this is a direct assignment option to an action plugin'

- ping:
    data: also a direct assignment

在任务动作之外,最典型的“直接赋值”出现在 lookup、filter 和 test 插件中。

lookup('plugin', direct1='value', direct2='value2')

'value_directly_assigned'|filter('another directly assigned')

'direct value' is testplugin

虽然其中大多数(尤其是 test 插件)没有其他配置方式,但如果插件和过滤器在文档中有所说明,它们可以使用来自其他配置源的输入。

清单插件(Inventory plugins)稍微复杂一些,因为它们使用“清单源”,这些源有时看起来像配置文件,并且作为命令行选项传递,但它们仍被视为“直接赋值”。使用内联源 -i host1, host2, host3 比使用文件源 -i /path/to/inventory_source 更清晰,但两者的优先级相同。