使用变量
Ansible 使用变量来管理系统之间的差异。使用 Ansible,您可以使用单个命令在多个不同的系统上执行任务和剧本。为了表示这些不同系统之间的差异,您可以使用标准 YAML 语法创建变量,包括列表和字典。您可以在剧本中、在 库存 中、在可重复使用的 文件 或 角色 中,或在命令行中定义这些变量。您也可以在剧本运行期间,通过将任务的返回值或值注册为一个新变量来创建变量。
创建变量后,无论是通过在文件中定义、在命令行中传递还是将任务的返回值或值注册为一个新变量,您都可以在模块参数中、在 条件“when”语句 中、在 模板 中以及在 循环 中使用这些变量。
了解了本页的概念和示例后,请阅读有关 Ansible 事实 的内容,这些是您从远程系统检索的变量。
创建有效的变量名
并非所有字符串都是有效的 Ansible 变量名。变量名只能包含字母、数字和下划线。 Python 关键字 或 剧本关键字 不是有效的变量名。变量名不能以数字开头。
变量名可以以下划线开头。在许多编程语言中,以下划线开头的变量是私有的。这在 Ansible 中并不适用。以下划线开头的变量与任何其他变量的处理方式完全相同。不要依赖此约定来实现隐私或安全性。
此表给出了有效和无效变量名的示例
有效的变量名 |
无效的 |
---|---|
|
|
|
剧本关键字,例如 |
|
|
|
|
简单变量
简单变量将变量名与单个值组合在一起。您可以在多个位置使用此语法(以及下面显示的列表和字典语法)。有关在库存中、在剧本中、在可重复使用的文件中、在角色中或在命令行中设置变量的详细信息,请参阅 在何处设置变量。
定义简单变量
您可以使用标准 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
的默认设置兼容,但您可以使用以下任何内容
有效值 |
描述 |
---|---|
|
真值 |
|
假值 |
列表变量
列表变量将变量名称与多个值组合在一起。多个值可以存储为项目列表或方括号 []
中,并用逗号分隔。
将变量定义为列表
可以使用 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_yaml
和 to_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 确实会应用变量优先级,您可能需要它。以下是优先级的顺序,从最低到最高(最后列出的变量会覆盖所有其他变量)
命令行值(例如
-u my_user
,这些不是变量)清单文件或脚本组变量 [2]
清单 group_vars/all [3]
剧本 group_vars/all [3]
清单 group_vars/* [3]
剧本 group_vars/* [3]
清单文件或脚本主机变量 [2]
清单 host_vars/* [3]
剧本 host_vars/* [3]
主机事实/缓存 set_facts [4]
play 变量
play vars_prompt
play vars_files
角色变量(在 角色目录结构 中定义)
块变量(仅适用于块中的任务)
任务变量(仅适用于该任务)
include_vars
set_facts/已注册变量
角色(和 include_role)参数
include 参数
额外变量(例如
-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 聊天频道