Ansible 使用变量,有些可能需要与其他配


使用变量

虽然自动化的存在是为了更容易使事情可重复,但并非所有系统都完全相同。有些可能需要与其他配置略有不同的配置。在某些情况下,观察到的一个系统的行为或状态可能会影响您配置其他系统的方式。例如,您可能需要找出一个系统的 IP 地址并将其用作另一个系统上的配置值。

Ansible 使用变量来帮助处理系统之间的差异。

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

该ansible示例中找到包含的变量是如何在Ansible使用的例子很多。

创建有效的变量名

在开始使用变量之前,了解什么是有效的变量名很重要。

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

foo_port是一个很大的变量。 foo5也很好。

foo-port,,而不是有效的变量名。foo portfoo.port12

YAML 还支持将键映射到值的字典。例如:

foo:
  field1: one
  field2: two

然后,您可以使用括号表示法或点表示法引用字典中的特定字段:

foo['field1']
foo.field1

它们都将引用相同的值(“一个”)。但是,如果您选择使用点表示法,请注意某些键可能会导致问题,因为它们会与 Python 字典的属性和方法发生冲突。如果您使用以两个下划线开头和结尾的键(这些在 python 中保留用于特殊含义)或者是任何已知的公共属性,则应使用括号表示法而不是点表示法:

addappendas_integer_ratiobit_lengthcapitalizecenterclearconjugatecopycountdecodedenominatordifferencedifference_updatediscardencodeendswithexpandtabsextendfindformatfromhexfromkeysgethas_keyheximagindexinsertintersectionintersection_updateisalnumisalphaisdecimalisdigitisdisjointis_integerislowerisnumericisspaceissubsetissupersetistitleisupperitemsiteritemsiterkeysitervaluesjoinkeysljustlowerlstripnumeratorpartitionpoppopitem,realremovereplacereverserfindrindexrjustrpartitionrsplitrstripsetdefaultsortsplitsplitlinesstartswithstripswapcasesymmetric_differencesymmetric_difference_updatetitletranslateunionupdateuppervaluesviewitemsviewkeysviewvalueszfill

定义清单中的变量

通常,您需要为单个主机或清单中的一组主机设置变量。例如,波士顿的机器可能都使用“boston.ntp.example.com”作为 NTP 服务器。该如何建立你的库存页有详细的设置为变量赋值给一个机器:主机变量和组变量:指定一个变量来许多机器清单。

在剧本中定义变量

您可以直接在剧本中定义变量:​​​​​​​

- hosts: webservers
  vars:
    http_port: 80

这可能很好,因为当您阅读剧本时它就在那里。

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

如Roles 中所述,变量也可以通过包含文件包含在剧本中,这些文件可能是也可能不是 Ansible 角色的一部分。首选使用角色,因为它提供了一个很好的组织系统。

在 Jinja2 中使用变量

一旦您定义了变量,您就可以使用 Jinja2 模板系统在您的剧本中使用它们。这是一个简单的 Jinja2 模板:​​​​​​​

My amp goes to {{ max_amp_value }}

该表达式提供了最基本的变量替换形式。

您可以在剧本中使用相同的语法。例如:

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

此处变量定义了文件的位置,该位置可能因系统而异。

在模板中,您可以自动访问主机范围内的所有变量。实际上不止这些——您还可以读取有关其他主机的变量。我们稍后将展示如何做到这一点。

ansible 允许在模板中使用 Jinja2 循环和条件,但在剧本中,我们不使用它们。Ansible playbook 是纯机器可解析的 YAML。这是一个相当重要的功能,因为它意味着可以代码生成文件片段,或者让其他生态系统工具读取 Ansible 文件。不是每个人都需要这个,但它可以解锁可能性。

模板 (Jinja2)

更多关于 Jinja2 模板的信息

使用 Jinja2 过滤器转换变量

Jinja2 过滤器允许您在模板表达式中转换变量的值。例如,capitalize过滤器将传递给它的任何值大写;在to_yamlto_json过滤器改变你的变量值的格式。Jinja2 包含许多内置过滤器,而 Ansible 提供更多过滤器。

嘿等等,一个 YAML 问题

YAML 语法要求,如果您以引用整行的方式开始一个值,因为它想确保您没有尝试启动 YAML 字典。这在YAML 语法文档中有介绍。{{ foo }}

这行不通:

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

这样做,你会没事的:

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

从系统中发现的变量:事实

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

事实是从与远程系统对话中得出的信息。您可以在ansible_facts变量下找到一个完整的集合,大多数事实也被“注入”为保留ansible_前缀的顶级变量,但由于冲突而删除了一些。这可以通过INJECT_FACTS_AS_VARS设置禁用。

例如,远程主机的 IP 地址或操作系统是什么。

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

- debug: var=ansible_facts

要查看收集的“原始”信息:

ansible hostname -m setup

这将返回大量可变数据,在 Ansible 2.7 上可能如下所示:

{
    "ansible_all_ipv4_addresses": [
        "REDACTED IP ADDRESS"
    ],
    "ansible_all_ipv6_addresses": [
        "REDACTED IPV6 ADDRESS"
    ],
    "ansible_apparmor": {
        "status": "disabled"
    },
    "ansible_architecture": "x86_64",
    "ansible_bios_date": "11/28/2013",
    "ansible_bios_version": "4.1.5",
    "ansible_cmdline": {
        "BOOT_IMAGE": "/boot/vmlinuz-3.10.0-862.14.4.el7.x86_64",
        "console": "ttyS0,115200",
        "no_timer_check": true,
        "nofb": true,
        "nomodeset": true,
        "ro": true,
        "root": "LABEL=cloudimg-rootfs",
        "vga": "normal"
    },
    "ansible_date_time": {
        "date": "2018-10-25",
        "day": "25",
        "epoch": "1540469324",
        "hour": "12",
        "iso8601": "2018-10-25T12:08:44Z",
        "iso8601_basic": "20181025T120844109754",
        "iso8601_basic_short": "20181025T120844",
        "iso8601_micro": "2018-10-25T12:08:44.109968Z",
        "minute": "08",
        "month": "10",
        "second": "44",
        "time": "12:08:44",
        "tz": "UTC",
        "tz_offset": "+0000",
        "weekday": "Thursday",
        "weekday_number": "4",
        "weeknumber": "43",
        "year": "2018"
    },
    "ansible_default_ipv4": {
        "address": "REDACTED",
        "alias": "eth0",
        "broadcast": "REDACTED",
        "gateway": "REDACTED",
        "interface": "eth0",
        "macaddress": "REDACTED",
        "mtu": 1500,
        "netmask": "255.255.255.0",
        "network": "REDACTED",
        "type": "ether"
    },
    "ansible_default_ipv6": {},
    "ansible_device_links": {
        "ids": {},
        "labels": {
            "xvda1": [
                "cloudimg-rootfs"
            ],
            "xvdd": [
                "config-2"
            ]
        },
        "masters": {},
        "uuids": {
            "xvda1": [
                "cac81d61-d0f8-4b47-84aa-b48798239164"
            ],
            "xvdd": [
                "2018-10-25-12-05-57-00"
            ]
        }
    },
    "ansible_devices": {
        "xvda": {
            "holders": [],
            "host": "",
            "links": {
                "ids": [],
                "labels": [],
                "masters": [],
                "uuids": []
            },
            "model": null,
            "partitions": {
                "xvda1": {
                    "holders": [],
                    "links": {
                        "ids": [],
                        "labels": [
                            "cloudimg-rootfs"
                        ],
                        "masters": [],
                        "uuids": [
                            "cac81d61-d0f8-4b47-84aa-b48798239164"
                        ]
                    },
                    "sectors": "83883999",
                    "sectorsize": 512,
                    "size": "40.00 GB",
                    "start": "2048",
                    "uuid": "cac81d61-d0f8-4b47-84aa-b48798239164"
                }
            },
            "removable": "0",
            "rotational": "0",
            "sas_address": null,
            "sas_device_handle": null,
            "scheduler_mode": "deadline",
            "sectors": "83886080",
            "sectorsize": "512",
            "size": "40.00 GB",
            "support_discard": "0",
            "vendor": null,
            "virtual": 1
        },
        "xvdd": {
            "holders": [],
            "host": "",
            "links": {
                "ids": [],
                "labels": [
                    "config-2"
                ],
                "masters": [],
                "uuids": [
                    "2018-10-25-12-05-57-00"
                ]
            },
            "model": null,
            "partitions": {},
            "removable": "0",
            "rotational": "0",
            "sas_address": null,
            "sas_device_handle": null,
            "scheduler_mode": "deadline",
            "sectors": "131072",
            "sectorsize": "512",
            "size": "64.00 MB",
            "support_discard": "0",
            "vendor": null,
            "virtual": 1
        },
        "xvde": {
            "holders": [],
            "host": "",
            "links": {
                "ids": [],
                "labels": [],
                "masters": [],
                "uuids": []
            },
            "model": null,
            "partitions": {
                "xvde1": {
                    "holders": [],
                    "links": {
                        "ids": [],
                        "labels": [],
                        "masters": [],
                        "uuids": []
                    },
                    "sectors": "167770112",
                    "sectorsize": 512,
                    "size": "80.00 GB",
                    "start": "2048",
                    "uuid": null
                }
            },
            "removable": "0",
            "rotational": "0",
            "sas_address": null,
            "sas_device_handle": null,
            "scheduler_mode": "deadline",
            "sectors": "167772160",
            "sectorsize": "512",
            "size": "80.00 GB",
            "support_discard": "0",
            "vendor": null,
            "virtual": 1
        }
    },
    "ansible_distribution": "CentOS",
    "ansible_distribution_file_parsed": true,
    "ansible_distribution_file_path": "/etc/redhat-release",
    "ansible_distribution_file_variety": "RedHat",
    "ansible_distribution_major_version": "7",
    "ansible_distribution_release": "Core",
    "ansible_distribution_version": "7.5.1804",
    "ansible_dns": {
        "nameservers": [
            "127.0.0.1"
        ]
    },
    "ansible_domain": "",
    "ansible_effective_group_id": 1000,
    "ansible_effective_user_id": 1000,
    "ansible_env": {
        "HOME": "/home/zuul",
        "LANG": "en_US.UTF-8",
        "LESSOPEN": "||/usr/bin/lesspipe.sh %s",
        "LOGNAME": "zuul",
        "MAIL": "/var/mail/zuul",
        "PATH": "/usr/local/bin:/usr/bin",
        "PWD": "/home/zuul",
        "SELINUX_LEVEL_REQUESTED": "",
        "SELINUX_ROLE_REQUESTED": "",
        "SELINUX_USE_CURRENT_RANGE": "",
        "SHELL": "/bin/bash",
        "SHLVL": "2",
        "SSH_CLIENT": "REDACTED 55672 22",
        "SSH_CONNECTION": "REDACTED 55672 REDACTED 22",
        "USER": "zuul",
        "XDG_RUNTIME_DIR": "/run/user/1000",
        "XDG_SESSION_ID": "1",
        "_": "/usr/bin/python2"
    },
    "ansible_eth0": {
        "active": true,
        "device": "eth0",
        "ipv4": {
            "address": "REDACTED",
            "broadcast": "REDACTED",
            "netmask": "255.255.255.0",
            "network": "REDACTED"
        },
        "ipv6": [
            {
                "address": "REDACTED",
                "prefix": "64",
                "scope": "link"
            }
        ],
        "macaddress": "REDACTED",
        "module": "xen_netfront",
        "mtu": 1500,
        "pciid": "vif-0",
        "promisc": false,
        "type": "ether"
    },
    "ansible_eth1": {
        "active": true,
        "device": "eth1",
        "ipv4": {
            "address": "REDACTED",
            "broadcast": "REDACTED",
            "netmask": "255.255.224.0",
            "network": "REDACTED"
        },
        "ipv6": [
            {
                "address": "REDACTED",
                "prefix": "64",
                "scope": "link"
            }
        ],
        "macaddress": "REDACTED",
        "module": "xen_netfront",
        "mtu": 1500,
        "pciid": "vif-1",
        "promisc": false,
        "type": "ether"
    },
    "ansible_fips": false,
    "ansible_form_factor": "Other",
    "ansible_fqdn": "centos-7-rax-dfw-0003427354",
    "ansible_hostname": "centos-7-rax-dfw-0003427354",
    "ansible_interfaces": [
        "lo",
        "eth1",
        "eth0"
    ],
    "ansible_is_chroot": false,
    "ansible_kernel": "3.10.0-862.14.4.el7.x86_64",
    "ansible_lo": {
        "active": true,
        "device": "lo",
        "ipv4": {
            "address": "127.0.0.1",
            "broadcast": "host",
            "netmask": "255.0.0.0",
            "network": "127.0.0.0"
        },
        "ipv6": [
            {
                "address": "::1",
                "prefix": "128",
                "scope": "host"
            }
        ],
        "mtu": 65536,
        "promisc": false,
        "type": "loopback"
    },
    "ansible_local": {},
    "ansible_lsb": {
        "codename": "Core",
        "description": "CentOS Linux release 7.5.1804 (Core)",
        "id": "CentOS",
        "major_release": "7",
        "release": "7.5.1804"
    },
    "ansible_machine": "x86_64",
    "ansible_machine_id": "2db133253c984c82aef2fafcce6f2bed",
    "ansible_memfree_mb": 7709,
    "ansible_memory_mb": {
        "nocache": {
            "free": 7804,
            "used": 173
        },
        "real": {
            "free": 7709,
            "total": 7977,
            "used": 268
        },
        "swap": {
            "cached": 0,
            "free": 0,
            "total": 0,
            "used": 0
        }
    },
    "ansible_memtotal_mb": 7977,
    "ansible_mounts": [
        {
            "block_available": 7220998,
            "block_size": 4096,
            "block_total": 9817227,
            "block_used": 2596229,
            "device": "/dev/xvda1",
            "fstype": "ext4",
            "inode_available": 10052341,
            "inode_total": 10419200,
            "inode_used": 366859,
            "mount": "/",
            "options": "rw,seclabel,relatime,data=ordered",
            "size_available": 29577207808,
            "size_total": 40211361792,
            "uuid": "cac81d61-d0f8-4b47-84aa-b48798239164"
        },
        {
            "block_available": 0,
            "block_size": 2048,
            "block_total": 252,
            "block_used": 252,
            "device": "/dev/xvdd",
            "fstype": "iso9660",
            "inode_available": 0,
            "inode_total": 0,
            "inode_used": 0,
            "mount": "/mnt/config",
            "options": "ro,relatime,mode=0700",
            "size_available": 0,
            "size_total": 516096,
            "uuid": "2018-10-25-12-05-57-00"
        }
    ],
    "ansible_nodename": "centos-7-rax-dfw-0003427354",
    "ansible_os_family": "RedHat",
    "ansible_pkg_mgr": "yum",
    "ansible_processor": [
        "0",
        "GenuineIntel",
        "Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
        "1",
        "GenuineIntel",
        "Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
        "2",
        "GenuineIntel",
        "Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
        "3",
        "GenuineIntel",
        "Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
        "4",
        "GenuineIntel",
        "Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
        "5",
        "GenuineIntel",
        "Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
        "6",
        "GenuineIntel",
        "Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
        "7",
        "GenuineIntel",
        "Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz"
    ],
    "ansible_processor_cores": 8,
    "ansible_processor_count": 8,
    "ansible_processor_threads_per_core": 1,
    "ansible_processor_vcpus": 8,
    "ansible_product_name": "HVM domU",
    "ansible_product_serial": "REDACTED",
    "ansible_product_uuid": "REDACTED",
    "ansible_product_version": "4.1.5",
    "ansible_python": {
        "executable": "/usr/bin/python2",
        "has_sslcontext": true,
        "type": "CPython",
        "version": {
            "major": 2,
            "micro": 5,
            "minor": 7,
            "releaselevel": "final",
            "serial": 0
        },
        "version_info": [
            2,
            7,
            5,
            "final",
            0
        ]
    },
    "ansible_python_version": "2.7.5",
    "ansible_real_group_id": 1000,
    "ansible_real_user_id": 1000,
    "ansible_selinux": {
        "config_mode": "enforcing",
        "mode": "enforcing",
        "policyvers": 31,
        "status": "enabled",
        "type": "targeted"
    },
    "ansible_selinux_python_present": true,
    "ansible_service_mgr": "systemd",
    "ansible_ssh_host_key_ecdsa_public": "REDACTED KEY VALUE",
    "ansible_ssh_host_key_ed25519_public": "REDACTED KEY VALUE",
    "ansible_ssh_host_key_rsa_public": "REDACTED KEY VALUE",
    "ansible_swapfree_mb": 0,
    "ansible_swaptotal_mb": 0,
    "ansible_system": "Linux",
    "ansible_system_capabilities": [
        ""
    ],
    "ansible_system_capabilities_enforced": "True",
    "ansible_system_vendor": "Xen",
    "ansible_uptime_seconds": 151,
    "ansible_user_dir": "/home/zuul",
    "ansible_user_gecos": "",
    "ansible_user_gid": 1000,
    "ansible_user_id": "zuul",
    "ansible_user_shell": "/bin/bash",
    "ansible_user_uid": 1000,
    "ansible_userspace_architecture": "x86_64",
    "ansible_userspace_bits": "64",
    "ansible_virtualization_role": "guest",
    "ansible_virtualization_type": "xen",
    "gather_subset": [
        "all"
    ],
    "module_setup": true
}

在上面的第一个磁盘的模型可以在模板或剧本中引用为:

{{ ansible_facts['devices']['xvda']['model'] }}

同样,系统报告的主机名是:

{{ ansible_facts['nodename'] }}

事实经常用于条件(请参阅条件)和模板中。

Facts 也可用于创建匹配特定条件的动态主机组,有关详细信息,请参阅group_by上的导入模块文档,以及条件章节中讨论的通用条件语句。

禁用事实

如果您知道不需要有关主机的任何事实数据,并且集中了解有关系统的所有信息,则可以关闭事实收集。这在推送模式下扩展 Ansible 具有大量系统的优势,主要是,或者如果您在实验平台上使用 Ansible。在任何游戏中,只需这样做:

- hosts: whatever
  gather_facts: no

当地事实 (facts.d)

1.3 版中的新功能。

正如剧本章节中所讨论的,Ansible 事实是一种获取有关远程系统的数据以用于剧本变量的方法。

通常这些是由setupAnsible 中的模块自动发现的。用户还可以编写自定义事实模块,如 API 指南中所述。但是,如果您想要一种简单的方法来提供系统或用户提供的数据以在 Ansible 变量中使用,而无需编写事实模块,该怎么办?

“Facts.d”是一种供用户控制其系统管理方式的某些方面的机制。

也许“本地事实”有点用词不当,它的意思是“本地提供的用户值”而不是“集中提供的用户值”,或者事实是什么——“本地动态确定的值”。

如果远程管理系统有一个/etc/ansible/facts.d目录,则该目录中以.fact,结尾的任何文件可以是 JSON、INI 或返回 JSON 的可执行文件,这些可以在 Ansible 中提供本地事实。可以使用fact_pathplay 关键字指定备用目录。

例如,假设/etc/ansible/facts.d/preferences.fact包含:

[general]
asdf=1
bar=2

这将产生一个名为散列变量实际上generalasdfbar作为成员。要验证这一点,请运行以下命令:

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

您将看到添加了以下事实:

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

并且可以通过以下方式访问此数据template/playbook

{{ ansible_local['preferences']['general']['asdf'] }}

本地命名空间可防止任何用户提供的事实覆盖系统事实或剧本中其他地方定义的变量。

key=value 对中的 key 部分将在 ansible_local 变量中转换为小写。使用上面的示例,如果 ini 文件包含XYZ=3在该[general]部分中,那么您应该期望以:而不是. 这是因为 Ansible 使用 Python 的ConfigParser,它通过optionxform方法传递所有选项名称,并且此方法的默认实现将选项名称转换为小写。{{ ansible_local['preferences']['general']['xyz'] }}{{ ansible_local['preferences']['general']['XYZ'] }}

如果您有一个剧本复制自定义事实然后运行它,则显式调用重新运行设置模块可以允许在该特定播放期间使用该事实。否则,它将在收集事实信息的下一场比赛中可用。下面是一个示例:

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

然而,在这种模式中,您也可以编写一个事实模块,并且可能希望将其视为一个选项。

Ansible 版本

1.8 版中的新功能。

为了使 playbook 行为适应 ansible 的特定版本,可以使用变量 ansible_version,其结构如下:

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

缓存事实

1.8 版中的新功能。

如文档中的其他地方所示,一台服务器可以引用另一台服务器的变量,如下所示:

{{ hostvars['asdf.example.com']['ansible_facts']['os_family'] }}

在禁用“事实缓存”的情况下,为了做到这一点,Ansible 必须已经与当前播放中的“asdf.example.com”或剧本中更高级别的另一个播放进行了对话。这是ansible的默认配置。

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

对于拥有数千台主机的非常大的基础架构,可以将事实缓存配置为每晚运行。一小组服务器的配置可以全天临时或定期运行。启用事实缓存后,就没有必要“命中”所有服务器来引用变量和有关它们的信息。

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

为了从缓存的事实中受益,您需要在大多数播放中将gathering设置更改为smartexplicit或设置gather_factsFalse

目前,Ansible 附带了两个持久缓存插件:redis 和 jsonfile。

要使用 redis 配置事实缓存,请ansible.cfg按如下方式启用它:

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

要启动并运行 redis,请执行等效的操作系统命令:​​​​​​​

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 是可写目录的本地文件系统路径(如果目录不存在,ansible 将尝试创建该目录)。

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

注册变量

变量的另一个主要用途是运行命令并将该命令的结果注册为变量。当您执行任务并将返回值保存在变量中以供以后的任务使用时,您创建了一个注册变量。在“条件”一章中有更多这样的例子 。

例如:

- hosts: web_servers

  tasks:

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

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

结果会因模块而异。每个模块的文档都包含RETURN描述该模块返回值的部分。要查看特定任务的值,请使用-v.

注册变量与事实相似,但有一些关键区别。与事实一样,注册变量是主机级变量。但是,注册的变量只存储在内存中。(Ansible 事实由您配置的任何缓存插件支持。)注册的变量仅在当前 playbook 运行的其余部分在主机上有效。最后,注册变量和事实具有不同的优先级。

当您使用循环在任务中注册变量时,注册的变量包含循环中每个项目的值。循环期间放置在变量中的数据结构将包含一个results属性,即来自模块的所有响应的列表。有关其工作原理的更深入示例,请参阅有关将寄存器与循环一起使用的循环部分。

如果任务失败或被跳过,变量仍会注册为失败或跳过状态,避免注册变量的唯一方法是使用标签。

访问复杂的变量数据

我们已经在文档中描述了更高一点的事实。

一些提供的事实,如网络信息,以嵌套数据结构的形式提供。简单地访问它们是不够的,但它仍然很容易做到。以下是我们获取 IP 地址的方法:{{ foo }}​​​​​​​

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

或者:

{{ ansible_facts.eth0.ipv4.address }}

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

{{ foo[0] }}

 

使用魔术变量访问有关其他主机的信息

无论您是否定义任何变量,您都可以使用Ansible 提供的特殊变量访问有关您的主机的信息,包括“魔术”变量、事实和连接变量。魔术变量名称是保留的 - 不要使用这些名称设置变量。该变量environment也被保留。

最常用的魔术变量是hostvarsgroupsgroup_names,和inventory_hostname

hostvars允许您访问另一台主机的变量,包括已收集到的有关该主机的事实。您可以在剧本中的任何一点访问主机变量。即使您尚未在剧本或剧本集的任何剧本中连接到该主机,您仍然可以获得变量,但您将无法看到事实。

如果您的数据库服务器想要使用来自另一个节点的“事实”的值,或者分配给另一个节点的清单变量,那么在模板甚至操作行中很容易做到这一点:

{{ hostvars['test.example.com']['ansible_facts']['distribution'] }}

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_facts']['eth0']['ipv4']['address'] }}
{% endfor %}

您可以使用此习语将前端代理服务器指向所有应用程序服务器,在服务器之间设置正确的防火墙规则等。您需要确保之前已填充这些主机的事实,例如通过如果最近没有缓存事实(事实缓存是在 Ansible 1.8 中添加的),则运行对它们的游戏。

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

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

inventory_hostname是 Ansible 的清单主机文件中配置的主机名的名称。当您禁用了事实收集,或者您不想依赖发现的主机名时,这会很有用ansible_hostname。如果您的 FQDN 很长,则可以使用inventory_hostname_short,其中包含直到第一个句点的部分,而没有域的其余部分。

其他有用的魔法变量是指当前的剧本或剧本,包括:

2.2 版中的新功能。

ansible_play_hosts 是当前播放中仍处于活动状态的所有主机的完整列表。

2.2 版中的新功能。

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

2.3 版中的新功能。

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

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

也可用的inventory_dir是保存 Ansible 清单主机文件的目录inventory_file的路径名,是指向 Ansible 清单主机文件的路径名和文件名。

playbook_dir 包含剧本基本目录。

然后我们有role_pathwhich 将返回当前角色的路径名(自 1.8 起)。这只会在角色内部起作用。

最后,ansible_check_mode(在 2.1 版中添加),一个布尔魔术变量,True如果您使用--check.

在文件中定义变量

将您的剧本保持在源代码控制之下是一个好主意,但您可能希望将剧本源设为公开,同时将某些重要变量设为私有。同样,有时您可能只想将某些信息保存在不同的文件中,远离主要剧本。

您可以通过使用一个或多个外部变量文件来做到这一点,就像这样:

---

- 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

也可以将每个主机和每个组的变量保存在非常相似的文件中,这在组织主机和组变量中有所介绍。

在命令行上传递变量

除了vars_promptand 之外vars_files,还可以使用--extra-vars(or -e) 参数在命令行中设置变量。可以使用以下格式之一使用单引号字符串(包含一个或多个变量)定义变量

键=值格式:

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

使用key=value语法传入的值被解释为字符串。如果您需要传入任何不应该是字符串的内容(布尔值、整数、浮点数、列表等),请使用 JSON 格式。

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"]}'

来自 JSON 或 YAML 文件的变量:

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

这对于为 playbook 设置主机组或用户等非常有用。

转义引号和其他特殊字符:

确保为标记(例如 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 文件。

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

很多人可能会问变量如何覆盖另一个。归根结底,Ansible 的理念是,你最好知道将变量放在哪里,然后你必须少考虑它。

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

帝国大厦只有一栋。一个蒙娜丽莎等。弄清楚在哪里定义一个变量,不要让它变得复杂。

但是,让我们继续前进,让优先权让开!它存在。这是一个真实的东西,你可能对它有用。

如果在不同的地方定义了多个同名变量,它们会按特定顺序被覆盖。

这是从最小到最大的优先顺序(最后列出的变量赢得优先级):

  1. 命令行值(例如“-u user”)

  2. 角色默认值1

  3. 库存文件或脚本组变量2

  4. 库存 group_vars/all 3

  5. 剧本 group_vars/all 3

  6. 库存组_vars/* 3

  7. 剧本 group_vars/* 3

  8. 库存文件或脚本主机变量2

  9. 库存主机变量/* 3

  10. 剧本host_vars/* 3

  11. 主机事实/缓存 set_facts 4

  12. 玩变种

  13. 播放 vars_prompt

  14. 播放 vars_files

  15. 角色变量(在 role/vars/main.yml 中定义)

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

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

  18. 包含变量

  19. set_facts / 注册变量

  20. 角色(和 include_role)参数

  21. 包含参数

  22. 额外的变量(总是优先)

基本上,进入“角色默认值”(角色内的默认值文件夹)的任何内容都是最具延展性和最容易覆盖的。角色的 vars 目录中的任何内容都会覆盖名称空间中该变量的先前版本。这里要遵循的想法是,您在范围内获得的越明确,命令行-e额外变量总是获胜的优先级越高。主机和/或清单变量可以胜过角色默认值,但不是像 vars 目录或include_vars任务这样的显式包含。

脚注

1

每个角色中的任务将看到自己角色的默认值。在角色之外定义的任务将看到最后一个角色的默认值。

2 ( 1 , 2 )

在清单文件中定义或由动态清单提供的变量。

3 ( 1 , 2 , 3 , 4 , 5 , 6 )

包括由“vars 插件”添加的变量以及由 Ansible 附带的默认 vars 插件添加的 host_vars 和 group_vars。

4

当使用 set_facts 的可缓存选项创建时,变量将在播放中具有高优先级,但当它们来自缓存时将与主机事实优先级相同。

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

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

组加载遵循父/子关系。然后按照字母顺序合并相同“父/子”级别的组。用户可以通过 取代最后一个ansible_group_priority,默认1为所有组。此变量ansible_group_priority只能在库存源中设置,而不能在 group_vars/ 中设置,因为该变量用于加载 group_vars/。

另一个需要考虑的重要事项(对于所有版本)是连接变量覆盖配置、命令行和播放/角色/任务特定选项和关键字。有关更多详细信息,请参阅控制 Ansible 的行为方式:优先规则。例如,如果您的清单指定并运行:ansible_user: ramon

ansible -u lola myhost

这仍然会连接,ramon因为变量的值优先(在这种情况下,变量来自库存,但无论变量在哪里定义都是如此)。

对于播放/任务,这对于remote_user. 假设相同的库存配置,以下播放:

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

将在库存中具有remote_user被覆盖的值ansible_user

这样做是为了使特定于主机的设置可以覆盖常规设置。这些变量通常在清单中按主机或组定义,但它们的行为与其他变量一样。

如果您想全局覆盖远程用户(甚至超过库存),您可以使用额外的变量。例如,如果您运行:

ansible... -e "ansible_user=maria" -u lola

lola值仍然被忽略,但ansible_user=maria优先于所有其他可能设置ansible_user(或remote_user) 的地方。

变量的特定于连接的版本优先于更通用的版本。例如,ansible_ssh_user指定为 group_var 的优先级高于ansible_user指定为 host_var 的优先级。

您还可以在播放中作为普通变量覆盖:

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

范围变量

您可以根据您希望该值具有的范围来决定在哪里设置变量。Ansible 具有三个主要作用域:

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

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

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

在哪里设置变量的例子

让我们展示一些示例,以及根据您可能想要对值进行控制的类型,您将选择放在哪里。

首先,组变量是强大的。

应将站点范围的默认设置定义为group_vars/all设置。组变量通常放置在您的清单文件旁边。它们也可以由动态清单脚本返回(请参阅使用动态清单)或在诸如Red Hat 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

所以这涵盖了库存以及您通常会在那里设置的内容。这是处理地理或行为问题的好地方。由于组通常是将角色映射到主机的实体,因此有时在组上设置变量而不是在角色上定义变量是一种快捷方式。你可以走任何一条路。

请记住:子组会覆盖父组,而主机始终会覆盖其组。

接下来:了解角色变量优先级。

我们几乎假设您此时正在使用角色。您肯定应该使用角色。角色很棒。你在使用角色不是吗?提示提示。

如果您正在编写具有合理默认值的可再发行角色,请将它们放入roles/x/defaults/main.yml文件中。这意味着该角色将带来一个默认值,但 Ansible 中的任何内容都会覆盖它。有关这方面的更多信息,请参阅角色:

---
# 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: 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

在此示例中,多次调用同一角色。很可能根本没有name提供的默认值。Ansible 可以在未定义变量时向您发出警告——实际上这是默认行为。

角色还有其他一些事情。

一般来说,在一个角色中设置的变量可供其他角色使用。这意味着如果你有一个roles/common/vars/main.yml你可以在那里设置变量并在你的剧本中的其他角色和其他地方使用它们:

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

有一些保护措施可以避免需要命名空间变量。在上面,在 common_settings 中定义的变量绝对可用于 'something' 和 'something_else' 任务,但是如果“something's”保证将 foo 设置为 12,即使在公共设置深处的某个地方,它也会将 foo 设置为 20。

所以,这是优先级,以更直接的方式解释。不要担心优先级,只需考虑您的角色是定义一个默认变量,还是您肯定要使用的“实时”变量。库存位于中间的优先级,如果您想强行覆盖某些内容,请使用-e.

如果您发现这有点难以理解,请查看GitHub 上的ansible-examples 存储库,了解更多有关所有这些内容如何协同工作的信息。

使用高级变量语法

有关用于声明变量以及对 Ansible 使用的 YAML 文件中放置的数据进行更多控制的高级 YAML 语法的信息,请参阅高级语法。

相关内容