【Ansible 文档】【译文】Playbooks 变量

时间:2022-06-03 14:26:02

Variables 变量

自动化的存在使得重复的做事情变得很容易,但是我们的系统不可能完全一样。

在某些系统中,你可能想要设置一些与其他系统不一样的行为和配置。

同样地,远程系统的行为和状态也可以影响到我们如何配置该系统。(例如,你可能需要找到一个系统的IP地址,并且用它来作为另一个系统的配置值)

你也可能有一些配置文件的模版,其大部分是一样的,但是基于某些值有些许不同。

Ansible 中的变量帮助我们如何处理系统间的不同。

为了理解variables,你会需要深入理解 Conditionals 和Loop  。像 group_by 和 when 条件这样有用的东西,可以和variables一起使用,来帮助管理系统间的不同。

强烈建议你学习 ansible-examples github代码库,里面有大量使用变量的例子。

对于最好的实践建议,参考最佳实践章节: Variables and Vaults

What Makes A Valid Variable Name 一个合法的变量名是什么样的

在我们开始使用variables时,知道什么组成一个合法的变量名是重要的。

变量名应该是字母、数字和下划线。变量始终以字母开头。

“foo_port”是个合法的变量名。”foo5”也是。 “foo-port”, “foo port”, “foo.port” 和 “12”则不是合法的变量名。

YAML 也支持字典,映射key到value。例如:

foo:
field1: one
field2: two

如此,你可以通过括号或者点引用字典中一个特定的字段。

foo['field1']
foo.field1

上面俩种方式都引用到了一个值("one")。然而,如果你使用点符号,需要意识到有些key可能会有问题,因为他们与python的方法或者属性冲突。你应该使用括号而不是点符号如果你使用以俩个下划线开始和结尾(这些在python中有特殊含义而保留)的keys或者任何其他已知的公共属性。

dd, 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.

Variables Defined in Inventory 清单文件中定义的变量

在其他章节,我们实际上已经知道了需要 variables 的知识,到目前为止还没有全新的知识,知识加深以下印象。

我们经常想要根据一台机器在哪个组里而设置变量。例如,在波士顿的机器可能需要使用 ‘boston.ntp.example.com’  作为NTP服务器

查看 Inventory 文档学习在inventory文件中定义变量。

Variables Defined in a Playbook 在playbook中定义变量

在一个Playbook中,可以像下面这样在内部直接定义变量:

- hosts: webservers
vars:
http_port:

当你正在阅读playbook时,你可以直接看到变量,这是很好的。

Variables defined from included files and roles 在included files和roles中定义变量

这是我们已经在其他地方谈论过的variables。

正如  Roles 描述的那样,变量可以被包含在playbook中,通过included files,其可能是也可能不是Ansible Roles中的一部分。roles是更好的方式,因为它提供了一个很好的组织结构体系。

Using Variables: About Jinja2 使用变量:Jinja2

我们已经知道足够多的关于定义变量,那么如何使用它们呢?

Ansible 允许你 在你的playbook中使用Jinja2来使用变量。而且,在Jinja中,你可以做许多复杂的事情,首先你需要学好基础。

例如,在简单的模版中,你可以如下使用:

My amp goes to {{ max_amp_value }}

这提供了变量替换最基础的形式。在playbooks中,你可以在playbook中直接这样用,并且你可能偶尔想这样做:

template: src=foo.cfg.j2 dest={{ remote_install_path }}/foo.cfg

在上面的例子中,我们使用了一个变量来帮助决定文件的存放地址。

在一个模版中,你自动会获取在主机范围之内的所有变量的访问权。事实上更多,你可以读取其它主机的变量。我们将演示如何做。

注意:

Ansible允许在模版里面Jinja2循环和条件,但是在playbook中,我们不使用它们。Ansible Playbooks是纯粹的机器解析的YAML。这是一个相当重要的功能,因为这意味着可以根据文件生成代码,或者其它系统工具能够读取Ansible文件。

虽然并不是所有人都需要这个功能,但我们不能*可能性。

查看 Templating (Jinja2) 获取更多Jinja2 模版的信息。

Jinja2 Filters Jinja2 过滤器

这是一个不常用的工具包。只在合适的时候使用它们,这是一个附加知识点。

Jinja2中的Filters 是一种方式,转换模版表达式从一种数据形式到另一种。Jinja2附带了很多这样的功能。请参见Jinja2官方模板文档中的 builtin filters 。

另外,Ansible还支持更多的其它特性。请看  Filters  文档中关于一系列可用的过滤器及示例。

Hey Wait, A YAML Gotcha 嘿,等等,有一个YAML的陷阱

YAML语法要求如果你以 {{ foo }} 开始一个值,你需要把行引号引起来,因为 YAML想要确定是你不是在开始声明一个字典。在 YAML Syntax  中有所描述。

以下将不会工作:

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

如下将会工作:

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

Information discovered from systems: Facts 系统信息发现:Facts

还有其他地方可以获取变量,但是这些变量是自动发现的,不是由用户设置的。

Facts是通过与远程主机通信获取的的信息。

举个例子,可以获取主机的IP地址或者操作系统。

为了查看可以获取到什么信息,使用如下命令来确定:

ansible hostname -m setup

这会返回大量的变量数据。

在template或者Playbooks中,可以通过如下的方式,获取第一块磁盘的Model:

{{ ansible_devices.sda.model }}

同样地,hostname:

{{ ansible_nodename }}

hostname中第一个点之前的部分:

{{ ansible_hostname }}

Facts 在 conditionals中很常用,在templates中也是。

Facts 也可以用来创建动态主机组,匹配特定条件的。查看  About Modules 中 group_by 更多细节,以及在  Conditionals  章节中描述的条件声明。

Turning Off Facts 关闭 Facts 收集

如果你知道你不需要主机上的任何facts数据,集中地知道主机的任何事情,你可以关闭facts收集。这对扩大Ansible推模式适用的主机规模有利,或者你正在实验性的平台上使用Ansible。通过在任何Playbook中如下做即可:

- hosts: whatever
gather_facts: no

Local Facts (Facts.d) 本地 Facts(Facts.d)

Ansible 1.3 新特性

如在Playbooks章节讨论的那样, Ansible facts 是一种获取远程主机数据的方式。

通常,由Ansible的setup模块自动发现变量。用户可以写自定义的facts模块,在API指导中描述的。然而,如果不借助于fact模块,而是通过一个简单的方式为Ansible变量提供系统或用户数据呢?

比如,如果您希望用户能够控制他们的系统是如何管理的,那该怎么办呢? “Facts.d”是这样的一种机制。

也许 “local facts” 这个词不太恰当,它意思是 “locally supplied user values” ,与  “centrally supplied user values” 相反,或者是  “locally dynamically determined values”.

如果远程主机管理系统有 /etc/ansible/facts.d 目录,任何在该目录中的以 .fact 结尾的文件,可能是json/ini或者可执行的返回json的文件,这些提供了 local facts。可以使用fact_path来指定替代目录。

例如,假设有一个 /etc/ansible/facts.d/perferences.fact 文件:

[general]
asdf=
bar=

这将会产生一个hash变量fact,名为general,包含 asdf and bar 的成员。为了验证是这样的情况,运行如下指令:

ansible <hostname> -m setup -a "filter=ansible_local"

你会看见如下facts添加:

"ansible_local": {
"preferences": {
"general": {
"asdf" : "",
"bar" : ""
}
}
}

这样该数据可以在template/Playbooks中访问:

{{ ansible_local.preferences.general.asdf }}

local 命名空间防止其他用户提供的fact覆盖系统facts或者在Playbooks中定义的变量。

在 ansible_local variable 中, key 会被转成小写的,如果你是大写的话。所以,如果你在ini文件中写的是XYZ=123,你应该只能通过 {{ ansible_local.preferences.general.xyz }} 来访问,并且不是 {{ ansible_local.preferences.general.XYZ }}

这是因为Ansible使用了Python的ConfigParser,其会把所有的选项名转成小写格式

如果你有一个Playbook:拷贝自定义的fact并运行,使用一个显示setup调用来允许那样的fact在这个Playbook运行的时候可用。否则,你会在下一次play中被收集fact信息。下面是这样的一个例子:

- hosts: webservers
tasks:
- name: create directory for ansible custom facts
file: state=directory recurse=yes path=/etc/ansible/facts.d
- name: install custom impi fact
copy: src=ipmi.fact dest=/etc/ansible/facts.d
- name: re-read facts after adding custom fact
setup: filter=ansible_local

Ansible version Ansible 版本变量

Ansible 1.8 新特性

为了适应特定版本Ansible,一个 ansible_version 变量是可用的,结构如下:

"ansible_version": {
"full": "2.0.0.2",
"major": ,
"minor": ,
"revision": ,
"string": "2.0.0.2"
}

Fact Caching Fact 缓存

Ansible 1.8 新特性

正如文档中的某处展示的,一台服务器可以引用另一台的相关变量,像这样:

{{ hostvars['asdf.example.com']['ansible_os_family'] }}

当 “Fact Caching” 被禁用时,为了这样做,Ansible 必须已经和 ‘asdf.example.com’ 在当前的play,或者另外一个play优先级更高的play中 通信过。这是Ansible的默认配置。

为了避免这种情况,Ansible 1.8 允许在playbook运行之间保存facts的能力,但是,这个特性必须手动启用。这个特性会有什么用呢?

想一下,例如,一个非常大的基础设施,有几千hosts。Fact 缓存可能在晚上配置运行,而一个小的服务器集合的配置可以运行ad-hoc或每天定时执行。当fact-caching启用时,没有必要去访问所有的服务器来引用他们的变量。

fact caching 启用时,一个组中的一台机器引用其他组中机器的变量是可能的。尽管他们还没有在当前的 /usr/bin/ansible-playbook 执行过程中通信过。

为了从缓存中的facts中受益,你会想要改变gathering设置为smart或者explicit或者设置gather_facts为False。

当前来说,Ansible封装了俩个持久化缓存插件:redis和jsonfile。

为了配置 fact caching 使用redis:在ansible.cfg中如下来启用:

[defaults]
gathering = smart
fact_caching = redis
fact_caching_timeout =
# seconds

为了使 redis 启用并运行,执行等价的 OS 命令:

yum install redis
service redis start
pip install redis

注意,Python redis 库应该通过pip安装,EPEL中的包太老了。

在当前的情况下,这个特性还处于beta测试阶段,并且Redis插件还不支持端口和密码的配置,后续版本将会支持。

为了配置fact caching使用jsonfile,如下在ansible.cfg中启用:

[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /path/to/cachedir
fact_caching_timeout =
# seconds

fact_caching_connection is a local filesystem path to a writeable directory (ansible will attempt to create the directory if one does not exist).

fact_caching_timeout is the number of seconds to cache the recorded facts.

Registered Variables 注册变量

变量的另一个主要用途是在运行命令时,把命令结果存储到一个变量中。不同模块的执行结果是不同的。运行playbook时使用-v选项可以看到可能的结果值。

在ansible中,任务执行的结果值可以保存在变量中,并且随后可以使用。 See some examples of this in the Conditionals chapter。

在上面的例子中有所提及,这里是一个快速的语法例子:

- hosts: web_servers

  tasks:

     - shell: /usr/bin/foo
register: foo_result
ignore_errors: True - shell: /usr/bin/bar
when: foo_result.rc ==

注册的变量在playbook运行的剩余部分是合法的,其和Ansible中的facts的生命周期是一样的。实际上,注册变量和facts是很类似的。

当在一个loop中使用 register时,这个数据结构再循环中设置变量,其会包含一个results属性,这是一个列表,循环所有值的结果。查看  Loops  获取更深入的细节。

如果一个任务失败或者跳过,变量仍然被注册为一个失败或者跳过的值。避免注册一个变量的唯一方式是使用tags。

Accessing Complex Variable Data 访问复杂变量数据

我们已经在本章早先部分讨论了facts。

一些提供的facts,像网络信息,是一个嵌套的数据结构。为了访问他们,简单的{{ foo }}是不足够的,但是他一直很容易做。以下是我们如何获取IP地址:

{{ ansible_eth0["ipv4"]["address"] }}

或者用下面的方式:

{{ ansible_eth0.ipv4.address }}

类似的,我们可以访问如下的方式访问数组中的第一个元素:

{{ foo[] }}

Magic Variables, and How To Access Information About Other Hosts 魔法变量,以及如何访问其他主机的信息

Ansible会自动提供给你一些变量,即使你并没有定义过它们。这些变量中重要的有 ‘hostvars’、’group_names’ 和 ‘groups’。由于这些变量名是预留的,所以用户不应当覆盖它们。

hostvars 让你可以获取其他主机的变量,包含已经从那台主机收集的facts。在这点上,如果你还没有在playbook或者playbooks中的任何play中与那台主机通信,那么你可以获取变量,但无法看到facts值。

如果你的数据库服务器想要使用其他节点的一个fact的值,或者一个清单文件中赋值给另一个节点的变量,很容易在一个模版中甚至是在action中使用:

{{ hostvars['test.example.com']['ansible_distribution'] }}

此外,group_names 是一个列表(数组),包含了当前主机所在的所有组。这个可以在templates中使用Jinja2语法基于主机的组成员关系的不同而产生不同的template源文件

{% if 'webserver' in group_names %}
# some part of a configuration file that only applies to webservers
{% endif %}

groups 是清单文件中所有组(和主机)的列表。这个可以用来迭代一个组内的所有主机。例如:

{% for host in groups['app_servers'] %}
# something that applies to all app servers.
{% endfor %}

一个常用的使用方式是遍历一个组找到那个组的所有IP地址

{% for host in groups['app_servers'] %}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}

举个例子:指向前端代理服务器到所有的app 服务器,在服务器之间设置正确的firewall rules等等。你需要确保那些主机的facts已经被填充了,例如通过对它们运行一个play。如果facts还没有缓存到的话(fact caching已经在Ansible 1.8中添加)。

此外, inventory_hostname 是Ansible inventory主机文件中配置的主机名称。由于其它一些神秘原因你不想使用自发现的主机名 ansible_hostname 时,你可以使用 inventory_hostname。如果主机的FQDN很长,那么*inventory_hostname_short*则会只包含域名第一个分号之前的部分,而舍弃其它部分。

在2.2版本中,play_hosts ,已经过时了。相同功能的变量是:ansible_play_batch

New in version 2.2.

ansible_play_hosts 是指在当前的play中仍然活跃的完整列表。

New in version 2.2.

ansible_play_batch 是一个hostnames的列表,针对当前play中的一批主机。 The batch size 由 serial 定义,当没有设置时,它等于整个play中的所有主机 (和  ansible_play_hosts 一样)。

New in version 2.3.

ansible_playbook_python 是python 可执行文件的路径,用来调用Ansible命令行工具。

这些 vars 对于填充 多个主机名到 templates 或者注入该列表到一个负载均衡器中是有用的。

不要担心任何问题,除非你需要使用它。你会知道什么时候你会知道。

同样地, inventory_dir 是存放Ansible inventory host 文件的目录路径名, inventory_file 是这个路径名和文件名指向 the Ansible’s inventory host file.

playbook_dir 含有playbook基础目录。

我们然后有role_path会返回当前role的路径名(1.8开始)这仅会在运行role时可用。

最后, ansible_check_mode (added in version 2.1), 一个boolean 魔法值,如果你使用 --check 模式运行 Ansible 时,其会被设置为 True 。

Variable File Separation 变量文件分离

保持你的playbook在源码控制系统是一个好注意,但是你可能希望使得playbook源码公开,同时保持某些重要变量私密性。类似地,有时你可能仅仅想要保持某些信息在不同的文件,与主要的playbook脱离

你也可以使用外部变量文件,或者文件集,如下所示:

---

- hosts: all
remote_user: root
vars:
favcolor: blue
vars_files:
- /vars/external_vars.yml tasks: - name: this is just a placeholder
command: /bin/echo foo

当你分享playbook源码给别人是,就没有暴露敏感数据的风险。

每个变量文件的格式是简单的YAML字典,像这样:

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

保持每个主机和每个组 的变量在非常相似的文件是可能的,这在Splitting Out Host and Group Specific Data 描述

Passing Variables On The Command Line 在命令行上传递变量

除了 vars_prompt and vars_files 之外,通过Ansible命令行发送变量是可以的。如果你想编写一个通用的发布playbook时则特别有用,你可以传递应用的版本以便部署

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

这是有用的,例如,在其他的配置中,设置playbook的hosts组或者用户:

---

- hosts: '{{ hosts }}'
remote_user: '{{ user }}' tasks:
- ... ansible-playbook release.yml --extra-vars "hosts=vipers user=starbuck"

Ansible 1.2 开始,你可以通过extra vars传递JSON,例如:

--extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'

key=value形式非常简单,但很实用!

使用 key=value 语法 传递的值被翻译为字符串。如果你想要传递任何不是strings的值,使用JSON 格式, (Booleans, integers, floats, lists etc)。

Ansible 1.3 起,extra vars 可以从一个JSON文件中加载,使用@语法:

--extra-vars "@some_file.json"

同样在Ansible 1.3中,我们可以为extra-vars传递YAML格式,无论直接通过命令行还是放置在文件中。

Variable Precedence: Where Should I Put A Variable? 变量顺序

许多人可能会问variable2 如何覆盖其他的。归根结底,这是Ansible的哲学:你最好知道把一个变量放到哪里,然后你可以无需怎么考虑变量是如何互相覆盖的了。

避免在47个地方定义变量 “x” ,然后问哪个 “x” 会被使用的问题。为什么?因为这不是Ansible 哲学解决的问题。

世界上只有一个帝国大厦。也只有一个蒙娜丽莎。请弄明白在那里定义变量,而不要把事情搞复杂。

然而,我们还是来讨论一下优先权的问题。它存在。你有可能会用到它。

如果同样名称的变量在多个地方都有定义,他们会以某种顺序覆盖。

在1.x之前,优先顺序是如下这样(越在后面优先级越高):

  • “role 默认值”, 优先级最低,很容易被覆盖。
  • variables inventory中定义的变量。
  • facts 系统自发现变量。
  • “most everything else”  大多数其他的变量: 命令行切换,play中的 vars,included vars,roles vars 等等。
  • connection variables 连接变量,ansible_user 等
  • extra vars (-e in the command line) always win

在1.5.4版本之后,系统自发现的变量也会在  “most everything else” 分类中。

在2.0之后,我们使得优先级的顺序更加的具体,最后面的变量优先级最高,即从低到高的优先级顺序如下:

  • role defaults [1]
  • inventory file or script group vars [2]
  • inventory group_vars/all
  • playbook group_vars/all
  • inventory group_vars/*
  • playbook group_vars/*
  • inventory file or script host vars [2]
  • inventory host_vars/*
  • playbook host_vars/*
  • host facts
  • play vars
  • play vars_prompt
  • play vars_files
  • role vars (defined in role/vars/main.yml)
  • block vars (only for tasks in block)
  • task vars (only for the task)
  • role (and include_role) params
  • include params
  • include_vars
  • set_facts / registered vars
  • extra vars (always win precedence)

基本上,任何在 “role defaults”(在特定角色的 defaults 文件夹) 中的东西是最具可塑性和很容易覆盖的。任何在特定角色的vars 目录的变量会覆盖命名空间中之前版本的变量。理想的确认方法是:你在的范围越具体,优先级越高,同时,命令行 -e 额外参数总是winning。 Host and/or inventory 变量可以赢role defaults,但是不显示包含像vars目录或者include_vars 任务。

1. 在每个role中的task会看见他们自己的role defaults,在role之外的任务会看见最后的role的默认值。

2. 在任何的部分,重复定义一个变量会覆盖先前的定义。如果多个组有相同的变量,最后加载的赢。如果你在play的 vars: 部分定义一个变量俩次,第二次定义的赢

另一个重要的事情是(对于所有的版本):连接变量会覆盖 config,命令行,以及play/role/task 特定的选项和指令。例如:

ansible -u lola myhost

这仍然会按照ramon连接,因为 ansible_ssh_user 在inventory中对 myhost主机 设置为 ramon 。对于plays/tasks 的 remote_user 也是这样的。

- hosts: myhost
tasks:
- command: i'll connect as ramon still
remote_user: lola

这样做是因为特定主机的设置可以覆盖一般设置。这些变量通常在inventory中的每个host或者组定义,但是他们的行为和其他变量一样。如果你想要全局地覆盖远程用户(甚至是inventory)你可以使用extra vars:

ansible... -e "ansible_user=<user>"

你也可以在play中定义一个普通变量来重写:

- hosts: all
vars:
ansible_user: lola
tasks:
- command: i'll connect as lola!

Variable Scopes 变量范围

Ansible 有 3 个主要范围:

  • 全局的:这是由 config、环境变量和命令行设置的
  • Play范围: 每一个play及其包含的结构、vars 条目(vars/vars_files/vars_prompt)、roles defaults和vars。
  • Host范围:直接关联到一个主机的变量,像 inventory、included_vars、facts、或者注册任务输出。

Variable Examples 变量例子

这样看似乎有点理论化了。让我们看看一些例子,同时你可以选择在哪放置那些值,基于你想要的控制类型。

首先,组变量是强有力的一种。

站点范围的默认值应该作为一个group_vars/all被定义。组变量一般和你的inventory文件放在一起。他们可由一个动态的inventory脚本返回。(查看  Dynamic Inventory) 或者定义在类似Ansible Tower中,通过UI或者API。

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

区域信息可以被定义在一个group_vars/region变量。如果该组是all的子组,他会覆盖上层的组并且更具一般性:

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

对于某些疯狂的原因来说,如果我们想要告诉一个特定的主机使用一个特定的NTP 服务器,则其会覆盖组变量:

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

因此,那覆盖了inventory和你通常的设置在那里。对于处理地理或者行为的事情来说,这是很好的地方。因为 groups 常常用作将roles映射到主机的实体,所以一个快捷路径是在组中设置变量而不是在role中定义。你可以随意选择任意一种方式。

记住:子组覆写父组,同时,hosts 总是覆写 他们的组。

更进一步:了解role变量顺序

我们更愿意假设你正在使用roles,你应该要使用roles。roles是很好的。

OK,所以,如果你正在写一个发布角色,其有一些合理的默认值,把他们放在roles/x/defaults/main.yml文件中。这意味着role会使用这个默认值,但是Ansible中任何的地方都可能会覆写它。See Roles for more info about this:

---
# file: roles/x/defaults/main.yml
# if not overridden in inventory or as a parameter, this is the value that will be used
http_port:

因此,对于嵌入一个角色的常量,上面的方式总是极好。如果你不像分享你的role给其他人,应用特定的行为,例如端口放在这里是好的。但是如果想分享角色给其他人,在这里设置一个变量不是坏事。没人能通过inventory覆写他们,但是他们仍然可以传递一个参数值给这个role。

参数化roles是有用的。

如果你正在使用一个role并且想要覆写一个默认值,传递它作为一个参数给该role,像这样:

roles:
- { role: apache, http_port: }

这样的话,playbook的读者就很清晰你有意识的覆写了一些role中的默认值,或者传递一些role不能自己决定的配置。它允许你传递一些特定site的东西,这些东西其实不是真正属于该角色的部分。

这经常被用来对那些可以在一些主机上应用多次的步骤(动作),像这样:

roles:
- { role: app_user, name: Ian }
- { role: app_user, name: Terry }
- { role: app_user, name: Graham }
- { role: app_user, name: John }

这有点武断,但是您可以看到如何多次调用相同的角色。

在那个例子中,“name” 很有可能没有提供默认值。Ansible 可以在变量没有定义的时候告诉你 - 这是默认行为。

role还有一些额外的好处。

通常来说,在一个角色中设置的变量对于其他的角色是可用的。这意味着,如果你有 roles/common/vars/main.yml 你可以在这里设置变量,并且在其他角色或者playbook中的任意地方使用:

roles:
- { role: common_settings }
- { role: something, foo: }
- { role: something_else }

注意:

有一些保护措施避免命令空间变量的需要。在上面的例子中,在common_settings中定义的变量对于“something”和“something_else”任务来说是可用的,但是如果something确保foo设置为12,即使common settings 设置 foo 为20

所以,直接点说,这就是优先顺序的问题。不要担心这个顺序,只要考虑如果你的role正在定义一个有默认值的变量,或者你想要使用的一个“live”变量。inventory files优先级位于俩者之间,如果你想要强制覆盖某些配置,使用 -e 。

如果你发现有一点难以理解,看看  ansible-examples github repo 来查看更多的关于如何更好的一起使用。

Advanced Syntax 高级语法

For information about advanced YAML syntax used to declare variables and have more control over the data placed in YAML files used by Ansible, see Advanced Syntax.