使用变量

Ansible 使用变量来管理系统之间的差异。使用 Ansible,您可以使用单个命令在多个不同的系统上执行任务和剧本。为了表示这些不同系统之间的差异,您可以使用标准 YAML 语法创建变量,包括列表和字典。您可以在剧本中、在 库存 中、在可重复使用的 文件角色 中,或在命令行中定义这些变量。您也可以在剧本运行期间,通过将任务的返回值或值注册为一个新变量来创建变量。

创建变量后,无论是通过在文件中定义、在命令行中传递还是将任务的返回值或值注册为一个新变量,您都可以在模块参数中、在 条件“when”语句 中、在 模板 中以及在 循环 中使用这些变量。

了解了本页的概念和示例后,请阅读有关 Ansible 事实 的内容,这些是您从远程系统检索的变量。

创建有效的变量名

并非所有字符串都是有效的 Ansible 变量名。变量名只能包含字母、数字和下划线。 Python 关键字剧本关键字 不是有效的变量名。变量名不能以数字开头。

变量名可以以下划线开头。在许多编程语言中,以下划线开头的变量是私有的。这在 Ansible 中并不适用。以下划线开头的变量与任何其他变量的处理方式完全相同。不要依赖此约定来实现隐私或安全性。

此表给出了有效和无效变量名的示例

有效的变量名

无效的

foo

*foo, Python 关键字,例如 asynclambda

foo_env

剧本关键字,例如 environment

foo_port

foo-port, foo port, foo.port

foo5, _foo

5foo, 12

简单变量

简单变量将变量名与单个值组合在一起。您可以在多个位置使用此语法(以及下面显示的列表和字典语法)。有关在库存中、在剧本中、在可重复使用的文件中、在角色中或在命令行中设置变量的详细信息,请参阅 在何处设置变量

定义简单变量

您可以使用标准 YAML 语法定义简单变量。例如

remote_install_path: /opt/my_app_config

引用简单变量

定义变量后,使用 Jinja2 语法引用它。Jinja2 变量使用双花括号。例如,表达式 My amp goes to {{ max_amp_value }} 演示了最基本的变量替换形式。您可以在剧本中使用 Jinja2 语法。例如

ansible.builtin.template:
  src: foo.cfg.j2
  dest: '{{ remote_install_path }}/foo.cfg'

在此示例中,变量定义了文件的路径,该路径可能因系统而异。

注意

Ansible 允许在 模板 中使用 Jinja2 循环和条件语句,但不允许在剧本中使用。您无法创建任务循环。Ansible 剧本是纯机器可解析的 YAML。

何时引用变量(YAML 小技巧)

如果以 {{ foo }} 开头,则必须引用整个表达式以创建有效的 YAML 语法。如果您不引用整个表达式,YAML 解析器将无法解释语法 - 它可能是变量,也可能是 YAML 字典的开头。有关编写 YAML 的指南,请参阅 YAML 语法 文档。

如果像这样使用未加引号的变量

- hosts: app_servers
  vars:
      app_path: {{ base_path }}/22

您将看到:ERROR! Syntax Error while loading YAML. 如果添加引号,Ansible 将正常工作

- hosts: app_servers
  vars:
       app_path: "{{ base_path }}/22"

布尔变量

Ansible 接受布尔变量的广泛范围的值:true/false, 1/0, yes/no, True/False 等等。有效字符串的匹配不区分大小写。虽然文档示例侧重于 true/false 以与 ansible-lint 的默认设置兼容,但您可以使用以下任何内容

有效值

描述

True , 'true' , 't' , 'yes' , 'y' , 'on' , '1' , 1 , 1.0

真值

False , 'false' , 'f' , 'no' , 'n' , 'off' , '0' , 0 , 0.0

假值

列表变量

列表变量将变量名称与多个值组合在一起。多个值可以存储为项目列表或方括号 [] 中,并用逗号分隔。

将变量定义为列表

可以使用 YAML 列表定义包含多个值的变量。例如

region:
  - northeast
  - southeast
  - midwest

引用列表变量

使用定义为列表(也称为数组)的变量时,可以使用该列表中的单个特定字段。列表中的第一个项目是项目 0,第二个项目是项目 1。例如

region: "{{ region[0] }}"

此表达式的值为“northeast”。

字典变量

字典将数据存储为键值对。通常,字典用于存储相关数据,例如 ID 或用户配置文件中包含的信息。

将变量定义为键值字典

可以使用 YAML 字典定义更复杂的变量。YAML 字典将键映射到值。例如

foo:
  field1: one
  field2: two

引用键值字典变量

使用定义为键值字典(也称为哈希)的变量时,可以使用方括号表示法或点表示法使用该字典中的单个特定字段

foo['field1']
foo.field1

这两个示例都引用了相同的值(“one”)。方括号表示法始终有效。点表示法可能会导致问题,因为某些键与 Python 字典的属性和方法冲突。如果您使用以两个下划线开头和结尾的键(这些键在 Python 中保留用于特殊含义)或任何已知公共属性,请使用方括号表示法

add, append, as_integer_ratio, bit_length, capitalize, center, clear, conjugate, copy, count, decode, denominator, difference, difference_update, discard, encode, endswith, expandtabs, extend, find, format, fromhex, fromkeys, get, has_key, hex, imag, index, insert, intersection, intersection_update, isalnum, isalpha, isdecimal, isdigit, isdisjoint, is_integer, islower, isnumeric, isspace, issubset, issuperset, istitle, isupper, items, iteritems, iterkeys, itervalues, join, keys, ljust, lower, lstrip, numerator, partition, pop, popitem, real, remove, replace, reverse, rfind, rindex, rjust, rpartition, rsplit, rstrip, setdefault, sort, split, splitlines, startswith, strip, swapcase, symmetric_difference, symmetric_difference_update, title, translate, union, update, upper, values, viewitems, viewkeys, viewvalues, zfill.

组合变量

要合并包含列表或字典的变量,可以使用以下方法。

组合列表变量

可以使用 set_fact 模块将列表组合成新的 merged_list 变量,如下所示

vars:
  list1:
  - apple
  - banana
  - fig

  list2:
  - peach
  - plum
  - pear

tasks:
- name: Combine list1 and list2 into a merged_list var
  ansible.builtin.set_fact:
    merged_list: "{{ list1 + list2 }}"

组合字典变量

要合并字典,请使用 combine 过滤器,例如

vars:
  dict1:
    name: Leeroy Jenkins
    age: 25
    occupation: Astronaut

  dict2:
    location: Galway
    country: Ireland
    postcode: H71 1234

tasks:
- name: Combine dict1 and dict2 into a merged_dict var
  ansible.builtin.set_fact:
    merged_dict: "{{ dict1 | ansible.builtin.combine(dict2) }}"

有关更多详细信息,请参见 ansible.builtin.combine

使用 merge_variables 查找

要合并与给定前缀、后缀或正则表达式匹配的变量,可以使用 community.general.merge_variables 查找,例如

merged_variable: "{{ lookup('community.general.merge_variables', '__my_pattern', pattern_type='suffix') }}"

有关更多详细信息和使用示例,请参阅 community.general.merge_variables 查找文档

注册变量

可以使用任务关键字 register 从 Ansible 任务的输出创建变量。可以在剧本中的任何后续任务中使用注册的变量。例如

- hosts: web_servers

  tasks:

     - name: Run a shell command and register its output as a variable
       ansible.builtin.shell: /usr/bin/foo
       register: foo_result
       ignore_errors: true

     - name: Run a shell command using output of the previous task
       ansible.builtin.shell: /usr/bin/bar
       when: foo_result.rc == 5

有关在后续任务的条件中使用注册变量的更多示例,请参见 条件。注册的变量可以是简单变量、列表变量、字典变量或复杂的嵌套数据结构。每个模块的文档都包含一个 RETURN 部分,描述了该模块的返回值。要查看特定任务的值,请使用 -v 运行剧本。

注册的变量存储在内存中。无法缓存注册的变量以在将来的剧本运行中使用。注册的变量仅在当前剧本运行的宿主上有效,包括同一剧本运行中的后续剧本。

注册的变量是宿主级变量。当在包含循环的任务中注册变量时,注册的变量将包含循环中每个项目的 value。在循环中放入变量的数据结构将包含一个 results 属性,该属性是模块的所有响应列表。有关其工作原理的更深入示例,请参见 循环 部分,其中介绍了如何将 register 与循环一起使用。

注意

如果任务失败或被跳过,Ansible 仍然会注册一个带有失败或跳过状态的变量,除非任务是根据标签跳过的。有关添加和使用标签的信息,请参见 标签

引用嵌套变量

许多注册的变量(和 事实)是嵌套的 YAML 或 JSON 数据结构。无法使用简单的 {{ foo }} 语法访问这些嵌套数据结构中的值。必须使用方括号表示法或点表示法。例如,要使用方括号表示法从您的事实中引用 IP 地址

{{ ansible_facts["eth0"]["ipv4"]["address"] }}

要使用点表示法从您的事实中引用 IP 地址

{{ ansible_facts.eth0.ipv4.address }}

使用 Jinja2 过滤器转换变量

Jinja2 过滤器允许您在模板表达式中转换变量的值。例如,capitalize 过滤器将传递给它的任何值大写;to_yamlto_json 过滤器会改变变量值的格式。Jinja2 包含许多 内置过滤器,Ansible 还提供了更多过滤器。要查找更多过滤器的示例,请参阅 使用过滤器操作数据

变量设置位置

您可以在多个位置定义变量,例如在清单、剧本、可重用文件、角色以及命令行中。Ansible 将加载它找到的每个可能的变量,然后根据 变量优先级规则 选择要应用的变量。

在清单中定义变量

您可以为每个主机分别定义不同的变量,或者为清单中的主机组设置共享变量。例如,如果 [Boston] 组中的所有机器都使用“boston.ntp.example.com”作为 NTP 服务器,您可以设置一个组变量。在 如何构建您的清单 页面上详细介绍了如何在清单中设置 主机变量组变量

在剧本中定义变量

您可以在剧本的 play 中直接定义变量

- hosts: webservers
  vars:
    http_port: 80

在 play 中定义变量时,这些变量仅对在该 play 中执行的任务可见。

在包含的文件和角色中定义变量

您可以在可重用的变量文件中以及/或者在可重用的角色中定义变量。在可重用的变量文件中定义变量时,敏感变量将与剧本分离。这种分离使您能够将剧本存储在源代码控制软件中,甚至共享剧本,而不会有暴露密码或其他敏感和个人数据的风险。有关创建可重用文件和角色的信息,请参阅 重用 Ansible 工件

此示例展示了如何包含在外部文件中定义的变量

---

- hosts: all
  remote_user: root
  vars:
    favcolor: blue
  vars_files:
    - /vars/external_vars.yml

  tasks:

  - name: This is just a placeholder
    ansible.builtin.command: /bin/echo foo

每个变量文件的内容都是一个简单的 YAML 字典。例如

---
# in the above example, this would be vars/external_vars.yml
somevar: somevalue
password: magic

注意

您可以在类似的文件中保留每个主机和每个组的变量。要了解有关组织变量的信息,请参阅 组织主机和组变量

在运行时定义变量

您可以使用 --extra-vars(或 -e)参数在命令行中传递变量,从而在运行剧本时定义变量。您还可以使用 vars_prompt 请求用户输入(请参阅 交互式输入:提示)。在命令行中传递变量时,请使用单引号字符串,其中包含一个或多个变量,格式如下。

key=value 格式

使用 key=value 语法传递的值被解释为字符串。如果您需要传递非字符串值(例如布尔值、整数、浮点数、列表等),请使用 JSON 格式。

ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"

JSON 字符串格式

ansible-playbook release.yml --extra-vars '{"version":"1.23.45","other_variable":"foo"}'
ansible-playbook arcade.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'

使用 --extra-vars 传递变量时,您必须为您的标记(例如 JSON)和您的 shell 正确地转义引号和其他特殊字符

ansible-playbook arcade.yml --extra-vars "{\"name\":\"Conan O\'Brien\"}"
ansible-playbook arcade.yml --extra-vars '{"name":"Conan O'\\\''Brien"}'
ansible-playbook script.yml --extra-vars "{\"dialog\":\"He said \\\"I just can\'t get enough of those single and double-quotes"\!"\\\"\"}"

来自 JSON 或 YAML 文件的变量

如果您有很多特殊字符,请使用包含变量定义的 JSON 或 YAML 文件。在 JSON 和 YAML 文件名之前加上 @

ansible-playbook release.yml --extra-vars "@some_file.json"
ansible-playbook release.yml --extra-vars "@some_file.yaml"

变量优先级:我应该在哪里放置变量?

您可以在许多不同的位置设置具有相同名称的多个变量。当您这样做时,Ansible 将加载它找到的每个可能的变量,然后根据变量优先级选择要应用的变量。换句话说,不同的变量将按照特定的顺序相互覆盖。

就定义变量的指南(在哪里定义特定类型的变量)达成一致的团队和项目通常可以避免变量优先级的担忧。我们建议您在一个位置定义每个变量:找出在哪里定义变量,并保持简单。有关示例,请参阅 变量设置位置的提示

您可以在变量中设置的一些行为参数,也可以在 Ansible 配置中、作为命令行选项以及使用剧本关键字设置。例如,您可以使用 ansible_user 将 Ansible 用于连接到远程设备的用户定义为变量,在配置文件中使用 DEFAULT_REMOTE_USER,作为命令行选项使用 -u,以及使用剧本关键字 remote_user。如果您在变量和另一种方法中定义了相同的参数,则变量将覆盖其他设置。这种方法允许主机特定设置覆盖更一般的设置。有关这些各种设置的优先级示例和更多详细信息,请参阅 控制 Ansible 的行为:优先级规则

了解变量优先级

Ansible 确实会应用变量优先级,您可能需要它。以下是优先级的顺序,从最低到最高(最后列出的变量会覆盖所有其他变量)

  1. 命令行值(例如 -u my_user,这些不是变量)

  2. 角色默认值(在 角色目录结构 中定义) [1]

  3. 清单文件或脚本组变量 [2]

  4. 清单 group_vars/all [3]

  5. 剧本 group_vars/all [3]

  6. 清单 group_vars/* [3]

  7. 剧本 group_vars/* [3]

  8. 清单文件或脚本主机变量 [2]

  9. 清单 host_vars/* [3]

  10. 剧本 host_vars/* [3]

  11. 主机事实/缓存 set_facts [4]

  12. play 变量

  13. play vars_prompt

  14. play vars_files

  15. 角色变量(在 角色目录结构 中定义)

  16. 块变量(仅适用于块中的任务)

  17. 任务变量(仅适用于该任务)

  18. include_vars

  19. set_facts/已注册变量

  20. 角色(和 include_role)参数

  21. include 参数

  22. 额外变量(例如 -e "user=my_user")(始终优先级最高)

通常,Ansible 会优先考虑定义时间更晚、更主动以及范围更明确的变量。角色内部 defaults 文件夹中的变量很容易被覆盖。角色的 vars 目录中的任何内容都会覆盖名称空间中该变量的先前版本。主机和/或清单变量会覆盖角色默认值,但显式包含(例如 vars 目录或 include_vars 任务)会覆盖清单变量。

Ansible 会合并清单中设置的不同变量,以便更具体的设置会覆盖更一般的设置。例如,指定为 group_var 的 ansible_ssh_user 会被指定为 host_var 的 ansible_user 覆盖。有关在清单中设置的变量优先级的详细信息,请参阅 如何合并变量

脚注

注意

在任何部分中,重新定义 var 会覆盖之前的实例。如果多个组具有相同的变量,则最后加载的组获胜。如果您在 play 的 vars: 部分中两次定义变量,则第二个变量获胜。

注意

以上描述了默认配置 hash_behaviour=replace,切换到 merge 以仅部分覆盖。

变量范围

您可以根据要赋予该值的范围来决定在哪里设置变量。Ansible 有三个主要范围

  • 全局:由配置、环境变量和命令行设置

  • play:每个 play 和包含的结构,vars 条目(vars; vars_files; vars_prompt)、角色默认值和 vars。

  • 主机:直接与主机关联的变量,如清单、include_vars、事实或已注册的任务输出

在模板内部,您自动可以访问主机范围内的所有变量,以及任何已注册的变量、事实和魔术变量。

变量设置位置提示

您应该根据您可能想要对值的控制程度来选择定义变量的位置。

在与地理位置或行为相关的清单中设置变量。由于组通常是将角色映射到主机实体,因此您通常可以在组上设置变量,而不是在角色上定义它们。请记住:子组会覆盖父组,主机变量会覆盖组变量。有关设置主机和组变量的详细信息,请参见 在清单中定义变量

group_vars/all 文件中设置通用默认值。有关如何在清单中组织主机和组变量的详细信息,请参见 组织主机和组变量。组变量通常与您的清单文件放在一起,但它们也可以由动态清单返回(参见 使用动态清单)或在 AWX 或 Red Hat Ansible Automation Platform 中从 UI 或 API 定义。

---
# file: /etc/ansible/group_vars/all
# this is the site wide default
ntp_server: default-time.example.com

group_vars/my_location 文件中设置特定于位置的变量。所有组都是 all 组的子组,因此在此处设置的变量会覆盖在 group_vars/all 中设置的变量。

---
# file: /etc/ansible/group_vars/boston
ntp_server: boston-time.example.com

如果一台主机使用不同的 NTP 服务器,您可以在 host_vars 文件中设置它,这将覆盖组变量。

---
# file: /etc/ansible/host_vars/xyz.boston.example.com
ntp_server: override.example.com

在角色中设置默认值以避免未定义变量错误。如果您共享您的角色,其他用户可以依赖您在 roles/x/defaults/main.yml 文件中添加的合理默认值,或者他们可以轻松地在清单中或命令行中覆盖这些值。有关更多信息,请参见 角色。例如

---
# file: roles/x/defaults/main.yml
# if no other value is supplied in inventory or as a parameter, this value will be used
http_port: 80

在角色中设置变量以确保在该角色中使用值,并且不会被清单变量覆盖。如果您没有与他人共享您的角色,您可以这样在 roles/x/vars/main.yml 中定义特定于应用程序的行为,例如端口。如果您与他人共享角色,将变量放在此处会使它们更难被覆盖,尽管它们仍然可以通过向角色传递参数或使用 -e 设置变量来覆盖。

---
# file: roles/x/vars/main.yml
# this will absolutely be used in this role
http_port: 80

当您调用角色以获得最大清晰度、灵活性和可见性时,将变量作为参数传递。这种方法会覆盖角色存在的任何默认值。例如

roles:
   - role: apache
     vars:
        http_port: 8080

当您阅读此剧本时,很明显您已选择设置变量或覆盖默认值。您还可以传递多个值,这使您能够多次运行同一个角色。有关更多详细信息,请参见 在一个剧本中多次运行角色。例如

roles:
   - role: app_user
     vars:
        myname: Ian
   - role: app_user
     vars:
       myname: Terry
   - role: app_user
     vars:
       myname: Graham
   - role: app_user
     vars:
       myname: John

在一个角色中设置的变量可供后面的角色使用。您可以在角色的 vars 目录(如 角色目录结构 中定义)中设置变量,并在您的剧本中的其他角色和其他地方使用它们。

roles:
   - role: common_settings
   - role: something
     vars:
       foo: 12
   - role: something_else

注意

有一些保护措施可以避免对变量进行命名空间。在这个例子中,在“common_settings”中定义的变量对“something”和“something_else”任务可用,但是“something”中的任务将 foo 设置为 12,即使“common_settings”将 foo 设置为 20。

我们鼓励您在决定变量设置位置时,不要担心变量优先级,而是考虑您希望在多大程度上或多频繁地覆盖变量。如果您不确定定义了哪些其他变量,并且需要特定值,请使用 --extra-vars (-e) 覆盖所有其他变量。

使用高级变量语法

有关用于声明变量并对 Ansible 使用的 YAML 文件中放置的数据进行更多控制的高级 YAML 语法的详细信息,请参见 高级剧本语法

另请参见

Ansible 剧本

剧本简介

条件语句

剧本中的条件语句

使用过滤器来操作数据

Jinja2 过滤器及其用途

循环

剧本中的循环

角色

按角色组织剧本

一般提示

剧本的提示和技巧

特殊变量

特殊变量列表

用户邮件列表

有问题?请访问 Google 群组!

实时聊天

如何加入 Ansible 聊天频道