虽然自动化存在使得更容易使事情重复,但所有的系统可能不完全一样。

在某些系统上,您可能需要设置一些与其他操作略有不同的行为或配置。

此外,一些观察到的远程系统的行为或状态可能需要影响如何配置这些系统。 (例如您可能需要找到系统的IP地址,甚至将其用作另一个系统上的配置值)。

您可能有一些配置文件的模板大部分是相同的,但基于这些变量略有不同。

Ansible中的变量是我们如何处理系统之间的差异。

要了解变量,您还需要挖掘条件循环有用的东西,如group_by模块和when条件也可以与变量一起使用,并帮助管理系统之间的差异。

强烈建议您查阅ansible-examples github仓库,以查看大量有关使用变量的示例。

有关最佳做法建议,请参阅“ 最佳实践”一章中的变量和保险库

What Makes A Valid Variable Name

变量名称应为字母,数字和下划线。 变量应始终以字母开头。

foo_port是一个正确的变量。 foo5也是。

foo-portfoo portfoo.port12都不是有效的变量名。

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.

Variables Defined in Inventory

我们实际上已经在另一部分中介绍了很多变量,到目前为止,这不应该是非常新鲜的,而是一点点复习。

通常,您将需要根据机器所在的组来设置变量。例如,波士顿的机器可能要使用“boston.ntp.example.com”作为NTP服务器。

有关如何在库存中定义变量的多种方法,请参阅库存凭证。

如:

192.168.1.116 key=116

192.168.1.117 key=117

192.168.1.118 key=118

[nginx]

192.168.1.11[6:8]

[nginx:vars]

ansible_python_interpreter=/usr/bin/python2.6

Variables Defined in a Playbook

在一个playbook中,可以直接内嵌定义变量:

- hosts: webservers
  vars:
    http_port: 80

Variables defined from included files and roles

原来我们已经在另一个地方谈过变量了。

如“ 手册角色和包含声明”中所述 ,变量也可以通过包含文件包含在playbook中,这些文件可能是或可能不是“可选角色”的一部分。 使用角色是首选,因为它提供了一个很好的组织系统。

Using Variables: About Jinja2

知道如何定义变量是很好的,但是如何使用它们?

可以使用Jinja2模板系统,您可以在Playbook中引用变量。 虽然你可以在Jinja做许多复杂的事情,但只有基础才是你真正需要学习的东西。

例如,在简单的模板中,您可以执行以下操作:

My amp goes to {{ max_amp_value }}

这将提供最基本的变量替代形式。

这也是直接在playbook中有效的,你偶尔会想做一些事情:

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

在上面的例子中,我们使用一个变量来帮助决定放置文件的位置。
在模板中,您可以自动访问主机范围内的所有变量。 实际上它不止于此 - 您还可以读取有关其他主机的变量。 我们将展示如何做到这一点。

也可以看看
模板(Jinja2)
有关Jinja2模板的更多信息

Hey Wait, A YAML Gotcha

YAML语法要求,如果您使用{{ foo }}启动值,则引用整行,因为它要确保您没有尝试启动YAML字典。 这在YAML Syntax页面中有所描述。

这不行:

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

这样做,你会没事的

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

Information discovered from systems: Facts

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

事实是从与您的远程系统通话中获得的信息。

这方面的一个例子可能是远程主机的IP地址或操作系统的IP地址。

要查看可用的信息,请尝试以下操作:

ansible hostname -m setup

这将返回一个巨大的可变数据,可能看起来像这样,从Ubuntu 12.04系统上的Ansible 1.4

在上文中,第一个硬盘驱动器的型号可以在模板或playbook中被引用为:

 {{ ansible_devices.sda.model }}

同样,系统报告的主机名是:
 {{ ansible_nodename }}
并且不合格的主机名显示第一个句点(.)之前的字符串:
{{ ansible_hostname }}

事实经常用于条件(见条件 )以及模板中。

事实也可用于创建符合特定条件的主机的动态组,有关详细信息,请参阅关于 groups_by关于Modules文档,以及条件一章中讨论的广义条件语句。

Turning Off Facts

如果您知道您不需要有关您的主机的任何事实数据,并且集中了解您的系统的所有内容,您可以关闭事件收集。 这在具有非常大量系统的推送模式中可扩展的可能性主要有以下优点,或者如果您在实验平台上使用可解密的话。 在任何play中,只要这样做:

- hosts: whatever
  gather_facts: no

Local Facts (Facts.d)

1.3版新功能

正如在playbook一章中讨论的那样,可解的facts是一种获取有关远程系统的数据以用于playbook变量的方式。

通常这些由Ansible中的安装模块自动发现。 用户还可以编写自定义facts模块,如API指南中所述。 但是,如果您想要一个简单的方法来提供系统或用户提供的数据,以便在可写变量中使用,而不必写入facts模块?

例如,如果您希望用户能够控制系统管理的一些方面呢? facts”是一种这样的机制。

如果远程管理的系统具有/etc/ansible/facts.d目录,则该目录中以.fact结尾的任何文件都可以是JSON,INI或返回JSON的可执行文件,并且这些文件可以在Ansible中提供本地facts可以使用fact_path play指令指定备用目录。

例如假设一个/etc/ansible/facts.d/preferences.fact

[general]
asdf=1
bar=2

这将产生一个名为general的哈希变量factsasdfbar作为成员。 要验证此操作,请运行以下命令:
 ansible <hostname> -m setup -a“filter = ansible_local”

你会看到以下facts
"ansible_local": {
        "preferences": {
            "general": {
                "asdf" : "1",
                "bar"  : "2"
            }
        }
 }

并且可以在template/playbook访问此数据:
  {{ ansible_local.preferences.general.asdf }}

本地命名空间可防止任何用户提供的facts覆盖系统facts或在该playbook中其他地方定义的变量。
如果您有一个正在复制自定义facts然后运行它的play,则显式调用重新运行安装模块可以允许在该特定play期间使用该facts否则,它将在下一次收集facts信息的play中可用。 这是一个可能是这样的例子:
- 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
然而,在这种模式下,您也可以编写一个facts模块,并且不妨将其视为一个选项。

Ansible version

1.8版新功能

要将playbook行为适应特定版本的ansible,可以使用一个变量ansible_version,具有以下结构:

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

Fact Caching

1.8版新功能

如文档中的其他位置所示,一个服务器可以引用另一个服务器的变量,如下所示:

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

对于“事实缓存”禁用,为了做到这一点,Ansible必须已经在当前的播放中与“asdf.example.com”进行了对话,或者在剧本中再次播放。 这是ansible的默认配置。

为了避免这种情况,Ansible 1.8允许在Playbook运行之间保存事实,但是此功能必须手动启用。 为什么这样会有用?

想象一下,例如,拥有数千台主机的非常大的基础架构。 事实缓存可以配置为每晚运行,但是一小组服务器的配置可以在一天内进行临时或定期运行。 启用事实缓存后,无需“打”所有服务器来引用变量和有关它们的信息。

启用事实缓存后,尽管在/ usr / bin / ansible-playbook的当前执行中没有与其通信,但一组中的机器可能会引用另一组中的机器的变量。

为了从缓存的事实中受益,您将希望将gathering设置更改为smartexplicit或者在大多数播放中将gather_facts设置为False

目前,Ansible具有两个持久缓存插件:redis和jsonfile。

要使用redis配置事实缓存,请在ansible.cfg启用它,如下所示:

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

要重新启动并运行,请执行等效的操作系统命令:
yum install redis
service redis start
pip install redis
请注意,Python redis库应该从pip安装,打包在EPEL中的版本太旧,不能被Ansible使用。

在当前实施例中,该特征处于beta级状态,并且Redis插件不支持端口或密码配置,预计在不久的将来会发生变化。

要使用jsonfile配置事实缓存,请在ansible.cfg启用它,如下所示:

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

fact_caching_connection是到可写目录的本地文件系统路径(如果不存在,可能会尝试创建目录)。

fact_caching_timeout是缓存记录事实的秒数。

Registered Variables

变量的另一个主要用途是运行命令并使用该命令的结果将结果保存到变量中。 结果将因模块而异。 执行playbook时使用-v将显示结果的可能值。

在ansible中执行的任务的值可以保存在变量中,以后使用。 请参阅条件一章中的一些例子。

虽然在该文档的其他地方也提到,这里有一个快速的语法示例:

- hosts: web_servers

  tasks:

     - shell: /usr/bin/foo
       register: foo_result
       ignore_errors: True

     - shell: /usr/bin/bar
       when: foo_result.rc == 5

注册变量在主机上运行的剩余部分是有效的,与Ansible中“facts”的生命周期相同。 有效注册的变量就像facts一样。

当循环使用register ,在循环中放置在变量中的数据结构将包含一个results属性,即该模块的所有响应的列表。 有关如何工作的更深入的示例,请参阅“ 循环”部分,使用带有循环的寄存器。

Accessing Complex Variable Data

我们已经在文档中谈到了一些facts。

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

  {{ ansible_eth0 [ “ipv4” ] [ “address” ] }}

或者:

  {{ ansible_eth0.ipv4.address }}

类似地,这是我们如何访问数组的第一个元素:

  {{ foo [ 0 ] }}

Magic Variables, and How To Access Information About Other Hosts

即使您没有自己定义它们,Ansible会为您自动提供一些变量。 其中最重要的是hostvarsgroup_namesgroups用户不应该自己使用这些名称,因为它们是保留的。 environment也保留。

hostvars可让您询问另一个主机的变量,包括已经收集到关于该主机的事实。 如果在这一点上,你还没有在剧本或剧集中玩过这个主机,那么你仍然可以得到这些变量,但是你将无法看到事实。

如果您的数据库服务器想要使用另一个节点的“事实”值,或者分配给另一个节点的清单变量,那么在一个模板甚至一个动作行中可以轻松实现:

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

另外, group_names是当前主机所有组的列表(数组)。这可以在使用Jinja2语法的模板中使用,以使得根据主机的组成员资格(或角色)而变化的模板源文件

{% 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 %}


一个例子可能包括将前端代理服务器指向所有应用服务器,在服务器之间设置正确的防火墙规则等。您需要确保这些主机的事实已经填充,例如,通过运行如果事实最近还没有被缓存,那么他们就会玩这个游戏(在Ansible 1.8中添加了事实缓存)。

此外, inventory_hostname是在Ansible的清单主机文件中配置的主机名的名称。 当您不想依赖于发现的主机名ansible_hostname或其他神秘原因时,这可能很有用。 如果你有一个很长的FQDN, inventory_hostname_short也包含第一个时间段,没有其余的域。

play_hosts在2.2中已被弃用,它与新的ansible_play_batch变量相同。

新版本2.2。

ansible_play_hosts是当前播放中仍然有效的所有主机的完整列表。

新版本2.2。

ansible_play_batch可用作播放当前“批”的范围内的主机名列表。 批量大小由serial定义,当不设置它相当于整个播放(使其与ansible_play_hosts相同)。

版本2.3中的新功能。

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

这些变量可能对于填写具有多个主机名的模板或将列表注入到负载均衡器的规则中是有用的。

除非你认为你需要,否则不要担心这些。 你会知道你什么时候做的

inventory_dir也是可用的目录,该目录包含Ansible的库存主机文件, inventory_file是路径名,文件名指向Ansible的库存主机文件。

playbook_dir包含playbook基本目录。

然后我们有role_path ,它将返回当前角色的路径名(自1.8)。 这只能在一个角色中发挥作用。

最后, ansible_check_mode (在版本2.1中添加),一个布尔魔术变量,如果您使用--check运行可执行文件,则将其设置为True

Variable File Separation

这是一个很好的主意,让您的Playbook受到源代码的控制,但您可能希望将Playbook的来源公开,同时保留某些重要的变量。 同样,有时您可能只想将某些信息保存在不同的文件中,远离main 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

这可以消除与他人共享您的手册源的敏感数据的风险。

每个变量文件的内容是一个简单的YAML字典,如下所示:

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

Passing Variables On The Command Line

除了vars_promptvars_files ,还可以通过可执行命令行发送变量。 在编写通用发行版手册时,您可能希望通过要部署的应用程序版本,这一点尤其有用:
  ansible-playbook release.yml --extra-vars“version = 1.23.45 other_variable = foo”

这对于设置play的主机组或用户而言非常有用。
---

- hosts: '{{ hosts }}'
  remote_user: '{{ user }}'

  tasks:
     - ...

ansible-playbook release.yml --extra-vars "hosts=vipers user=starbuck"
从Ansible 1.2开始,您还可以传入额外的var,如引用的JSON,如下所示:
 --extra-vars'{“pacman”:“mrs”,“ghosts”:[“inky”,“pinky”,“clyde”,“sue”]}

key=value形式显然更简单,但是如果需要它就在那里!
从Ansible 1.3起,可以使用@语法从JSON文件中加载额外的var:
  --extra-vars“@ some_file.json”

也可以从Ansible 1.3,额外的vars可以格式化为YAML,无论是在命令行还是在上面的文件中。

Variable Precedence: Where Should I Put A Variable?

很多人可能会问,变量如何覆盖另一个。 最终,Ansible的理念是更好地知道在哪里放一个变量,然后你必须考虑一下。

避免在47个位置定义变量“x”,然后询问“哪个x被使用”的问题。 为什么? 因为那不是禅宗的做事哲学。

只有一个帝国大厦。 一个蒙娜丽莎等,找出定义变量的位置,不要复杂。

但是,让我们继续前进吧! 它存在 这是一件真实的事情,你可能会用它。

如果在不同的地方定义了相同名称的多个变量,则它们将按照一定的顺序被覆盖。

在1.x中,优先级如下(最后列出的变量获得优先级排序):

  • “role defaults”,其优先于所有事物,并且是最容易被覆盖的
  • inventory中定义的变量
  • 在有关系统中发现的facts
  • “most everything else”(命令行开关,play中的变量,include变量,角色变量等)
  • 连接变量( ansible_user等)
  • 额外的vars( -e在命令行中)最优先

注意

在1.5.4之前的版本中,有关系统的事实发现在上述的“其他一切”类别中。

在2.x中,我们使优先顺序更具体(最后列出的变量获得优先级排序):

  • role defaults[1]
  • inventory文件或脚本组vars [2]
  • inventory group_vars / all
  • playbook group_vars / all
  • inventory group_vars / *
  • playbook group_vars / *
  • inventory 文件或脚本主机vars [2]
  • inventory host_vars / *
  • playbook host_vars / *
  • host facts
  • play facts
  • play vars_prompt
  • play vars_files
  • role vars(在role / vars / main.yml中定义)
  • block 变量(仅适用于块中的任务)
  • 任务变量(仅用于任务)
  • 角色(和include_role)参数
  • include参数
  • include_vars
  • set_facts / registered vars
  • 额外的vars(总是赢得优先)

基本上,进入“角色默认值”(角色中的默认文件夹)的任何东西都是最有价值的,容易被覆盖的。 该角色的vars目录中的任何内容都会覆盖该命名空间中该变量的先前版本。 这里要遵循的一个想法是,在范围上越明确,命令行-e额外的vars总是胜出越多的优先级。 主机和/或库存变量可以胜过角色默认值,但不显式包括vars目录或include_vars任务。