Использование переменных
- Создание допустимых имен переменных
- Определение переменных в инвентарной книге
- Определение переменных в книге задач
- Определение переменных во включенных файлах и ролях
- Использование переменных с Jinja2
- Преобразование переменных с фильтрами Jinja2
- Подождите, проблема с YAML
- Переменные, обнаруженные из систем: Факты
- Регистрация переменных
- Доступ к сложным данным переменных
- Доступ к информации об других узлах с магическими переменными
- Определение переменных в файлах
- Передача переменных в командной строке
- Приоритет переменных: Куда поместить переменную?
- Использование расширенного синтаксиса переменных
Хотя автоматизация существует, чтобы облегчить создание повторяемых задач, не все системы одинаковы; некоторые могут потребовать немного отличающейся конфигурации. В некоторых случаях наблюдаемое поведение или состояние одной системы может повлиять на то, как вы настраиваете другие системы. Например, вам может потребоваться узнать IP-адрес системы и использовать его в качестве значения конфигурации на другой системе.
Ansible использует переменные, чтобы помочь справиться с различиями между системами.
Чтобы понять переменные, вам также следует прочитать Условные выражения и Циклы. Полезные вещи, такие как модуль group_by и условное выражение when, также могут использоваться с переменными и помогут управлять различиями между системами.
В репозитории ansible-examples github содержатся многочисленные примеры использования переменных в Ansible.
Создание допустимых имен переменных
Прежде чем начать использовать переменные, важно знать, какие имена переменных допустимы.
Имена переменных должны состоять из букв, цифр и символов подчеркивания. Переменные всегда должны начинаться с буквы.
foo_port — отличная переменная. foo5 тоже подходит.
foo-port, foo port, foo.port и 12 — недопустимые имена переменных.
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.
Определение переменных в инвентарной книге
Часто вы захотите задать переменные для отдельного хоста или для группы хостов в вашей инвентарной книге. Например, машины в Бостоне могут использовать «boston.ntp.example.com» в качестве NTP-сервера. На странице Работа с инвентарной книгой содержатся подробности о настройке Присвоение переменной одной машине: переменные хоста и Присвоение переменной многим машинам: переменные группы в инвентарной книге.
Определение переменных в книге задач
Вы можете определить переменные непосредственно в книге задач:
- hosts: webservers
vars:
http_port: 80
Это может быть удобно, так как переменные находятся прямо там, когда вы читаете книгу задач.
Определение переменных во включенных файлах и ролях
Как описано в Ролях, переменные также могут быть включены в книгу задач через включенные файлы, которые могут или не могут быть частью роли 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 являются чисто читаемыми YAML-файлами. Это довольно важная функция, так как она означает, что можно генерировать части файлов с помощью кода или использовать другие инструменты экосистемы для чтения файлов Ansible. Не все захотят этого, но это может открыть возможности.
См. также
- Шаблоны (Jinja2)
- Дополнительная информация о шаблонах Jinja2
Преобразование переменных с фильтрами Jinja2
Фильтры Jinja2 позволяют преобразовывать значение переменной в выражении шаблона. Например, фильтр capitalize преобразует любое значение в верхний регистр; фильтры to_yaml и to_json изменяют формат значений переменных. Jinja2 включает много встроенных фильтров, а Ansible предоставляет еще много фильтров.
Подождите, проблема с YAML
Синтаксис YAML требует, что если вы начинаете значение с {{ foo }}, вы должны привести всю строку в кавычки, так как он хочет убедиться, что вы не пытаетесь начать словарь YAML. Это описано в документации по Синтаксису YAML.
Это не сработает:
- 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'] }}
Факты часто используются в условных операторах (см. Условные операторы) и также в шаблонах.
Факты также могут быть использованы для создания динамических групп хостов, которые соответствуют определённым критериям. Подробности см. в документации по Импорту модулей в разделе group_by, а также в обобщённых условных операторах, как описано в разделе Условные операторы.
Отключение фактов
Если вам не нужны данные о фактах ваших хостов и вы знаете всё о своих системах централизованно, вы можете отключить сбор фактов. Это полезно для масштабирования Ansible в режиме push с очень большим количеством систем или если вы используете Ansible на экспериментальных платформах. В любой книге задач сделайте следующее:
- hosts: whatever gather_facts: no
Локальные факты (facts.d)
Новое в версии 1.3.
Как обсуждалось в главе о книгах задач, факты Ansible — это способ получения данных об удалённых системах для использования в переменных книги задач.
Обычно они обнаруживаются автоматически модулем setup в Ansible. Пользователи также могут создавать собственные модули фактов, как описано в руководстве по API. Но что, если вы хотите предоставить простые данные системы или пользователя для использования в переменных Ansible без создания модуля фактов?
«Facts.d» — это механизм для пользователей, позволяющий контролировать некоторые аспекты управления своими системами.
Примечание
Возможно, «локальные факты» — это не совсем корректное название. Это означает «локально заданные значения пользователя» по сравнению с «центрально заданными значениями пользователя» или фактами — «локально динамически определяемые значения».
Если удалённая управляемая система имеет директорию /etc/ansible/facts.d, все файлы в этой директории, заканчивающиеся на .fact, могут быть файлами JSON, INI или исполняемыми файлами, возвращающими JSON, и они могут предоставлять локальные факты в Ansible. Альтернативную директорию можно указать, используя ключевое слово книги задач fact_path.
Например, предположим, что /etc/ansible/facts.d/preferences.fact содержит:
[general] asdf=1 bar=2
Это создаст переменную хэш-факт general с элементами asdf и bar. Для проверки выполните следующее:
ansible <hostname> -m setup -a "filter=ansible_local"
И вы увидите добавленный факт:
"ansible_local": {
"preferences": {
"general": {
"asdf" : "1",
"bar" : "2"
}
}
}
И эти данные можно получить в template/playbook следующим образом:
{{ ansible_local['preferences']['general']['asdf'] }}
Локальное пространство имён предотвращает переопределение пользователем предоставленных фактов системных фактов или переменных, определённых в другом месте книги задач.
Примечание
Ключевая часть в парах «ключ=значение» будет преобразована в нижний регистр внутри переменной ansible_local. Используя пример выше, если ini-файл содержал XYZ=3 в разделе [general], то вы должны получить доступ к нему как к {{ ansible_local['preferences']['general']['xyz'] }}, а не {{ ansible_local['preferences']['general']['XYZ'] }}. Это происходит потому, что Ansible использует ConfigParser Python, который передаёт все имена параметров через метод optionxform, а стандартная реализация этого метода преобразует имена параметров в нижний регистр.
Если у вас есть книга задач, копирующая пользовательский факт и затем выполняющая его, явное повторное выполнение модуля setup может позволить использовать этот факт во время этой конкретной книги задач. В противном случае он будет доступен в следующей книге задач, которая собирает информацию о фактах. Вот пример:
- 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.
Для адаптации поведения книги задач к определённой версии 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 позволяет сохранять факты между запусками книг задач, но эту функцию необходимо включить вручную. Зачем это может быть полезно?
В очень большой инфраструктуре с тысячами хостов кэширование фактов можно настроить на еженощное выполнение. Конфигурация небольшой группы серверов может выполняться в режиме ad-hoc или периодически в течение дня. При включенном кэшировании фактов не нужно «обращаться» ко всем серверам для ссылки на переменные и информацию о них.
При включённом кэшировании фактов машины в одной группе могут ссылаться на переменные машин в другой группе, несмотря на то, что с ними не было взаимодействия во время текущего выполнения /usr/bin/ansible-playbook.
Для использования кэшированных фактов вам нужно изменить параметр gathering на smart или explicit или установить gather_facts на False в большинстве книг задач.
В настоящее время 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.
В текущих реализациях эта функция находится на стадии бета-тестирования, и плагин 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 поддерживаются плагином кэша, который вы настроили.) Зарегистрированные переменные действительны только на хосте до конца текущего выполнения книги задач. Наконец, зарегистрированные переменные и факты имеют разные уровни приоритета.
Когда вы регистрируете переменную в задаче с циклом, зарегистрированная переменная содержит значение для каждого элемента в цикле. Структура данных, помещаемая в переменную во время цикла, будет содержать атрибут results, который является списком всех ответов модуля. Более подробный пример работы см. в разделе Циклы об использовании register с циклом.
Примечание
Если задача завершается неудачно или пропускается, переменная по-прежнему регистрируется с состоянием «неудача» или «пропуск». Единственный способ избежать регистрации переменной — использовать теги.
Доступ к сложным данным переменных
Мы уже немного описали факты выше в документации.
Некоторые предоставленные факты, такие как сетевая информация, доступны в виде вложенных структур данных. Для доступа к ним простой {{ foo }} недостаточно, но это по-прежнему легко сделать. Вот как получить IP-адрес:
{{ ansible_facts["eth0"]["ipv4"]["address"] }}
Или, как альтернатива:
{{ ansible_facts.eth0.ipv4.address }}
Аналогично, вот как мы получаем первый элемент массива:
{{ foo[0] }}
Доступ к информации о других хостах с помощью магических переменных
Независимо от того, определены ли вами переменные, вы можете получить доступ к информации о своих хостах с помощью предоставляемых Ansible специальных переменных, включая «магические» переменные, факты и переменные соединения. Имена магических переменных зарезервированы — не устанавливайте переменные с этими именами. Переменная environment также зарезервирована.
Наиболее часто используемые магические переменные — hostvars, groups, group_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 — путь к исполняемому файлу python, используемому для вызова командной строки Ansible.
Эти переменные могут быть полезны для заполнения шаблонов несколькими именами хостов или для вставки списка в правила балансировщика нагрузки.
Также доступны inventory_dir — путь к каталогу, содержащему файл инвентаря хостов Ansible, inventory_file — путь и имя файла, указывающие на файл инвентаря хостов Ansible.
playbook_dir содержит базовый каталог плейбука.
Затем у нас есть role_path, которая вернет текущий путь к роли (с версии 1.8). Она будет работать только внутри роли.
И, наконец, ansible_check_mode (добавлен в версии 2.1), магическая булева переменная, которая будет установлена в True, если вы запустите Ansible с --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_prompt и vars_files, можно задать переменные в командной строке, используя аргумент --extra-vars (или -e). Переменные можно определить, используя строку в одинарных кавычках (содержащую одну или несколько переменных) с помощью одного из форматов ниже.
Формат key=value:
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"
Это полезно, помимо прочего, для задания группы хостов или пользователя для плейбука.
Обработка кавычек и других специальных символов:
Убедитесь, что вы правильно обрабатываете кавычки как для разметки (например, JSON), так и для оболочки, в которой вы работаете.:
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
Многие люди могут задаваться вопросом о том, как переменные перекрывают друг друга. В конечном итоге философия Ansible заключается в том, что лучше знать, где разместить переменную, а затем меньше думать об этом.
Избегайте определения переменной «x» в 47 местах и затем задавайте вопрос «какая x используется». Почему? Потому что это не философия Ansible в отношении решения задач.
Существует только одно здание Empire State. Одна Мона Лиза и т. д. Определите, где определить переменную, и не усложняйте.
Однако давайте рассмотрим порядок приоритета! Он существует. Это реальная вещь, и у вас может быть для него применение.
Если несколько переменных с одинаковым именем определены в разных местах, они перезаписываются в определенном порядке.
Вот порядок приоритета от наименьшего к наибольшему (переменные, указанные последними, имеют наивысший приоритет):
- значения командной строки (например, «-u user»)
- значения по умолчанию роли [1]
- переменные группы в файлах инвентаря или скриптах [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]
- переменные плейя
- переменные плейя vars_prompt
- переменные плейя vars_files
- переменные роли (определены в role/vars/main.yml)
- переменные блока (только для задач в блоке)
- переменные задачи (только для задачи)
- include_vars
- set_facts / зарегистрированные переменные
- параметры роли (и include_role)
- параметры include
- дополнительные переменные (всегда имеют наивысший приоритет)
В основном, все, что попадает в «значения по умолчанию роли» (папка defaults внутри роли), является наиболее изменчивым и легко перезаписывается. Все в каталоге vars роли перезаписывает предыдущие версии этой переменной в пространстве имен. Здесь идея заключается в том, что чем яснее область действия, тем выше приоритет, при этом переменные командной строки -e и дополнительные переменные всегда имеют наивысший приоритет. Переменные хоста и/или инвентаря могут иметь приоритет над значениями по умолчанию роли, но не над явными включениями, такими как каталог vars или задача include_vars.
Примечания
| [1] | Задачи в каждой роли будут видеть свои значения по умолчанию роли. Задачи, определенные вне роли, будут видеть значения по умолчанию последней роли. |
| [2] | (1, 2) Переменные, определенные в файле инвентаря или предоставляемые динамическим инвентарем. |
| [3] | (1, 2, 3, 4, 5, 6) Включает переменные, добавленные «плагинами переменных», а также host_vars и group_vars, которые добавляются стандартным плагином переменных, поставляемым с Ansible. |
| [4] | При создании с опцией кэширования set_facts переменные будут иметь высокий приоритет в плейе, но будут такими же, как и приоритет фактов хоста, когда они поступают из кэша. |
Примечание
В любом разделе повторное определение переменной перезапишет предыдущий экземпляр. Если несколько групп имеют одинаковую переменную, последняя загруженная побеждает. Если вы определяете переменную дважды в разделе 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 имеет три основные области действия:
- Глобальная: устанавливается конфигурацией, переменными среды и командной строкой
- Пьеса: каждая пьеса и содержащие структуры, записи переменных (vars; vars_files; vars_prompt), значения по умолчанию ролей и переменные.
- Хост: переменные, напрямую связанные с хостом, такие как инвентаризация, include_vars, факты или результаты зарегистрированных задач
Примеры того, где установить переменную
Давайте рассмотрим некоторые примеры и где вы бы выбрали размещение, основываясь на том контроле, который вы хотели бы иметь над значениями.В первую очередь, групповые переменные мощные.
Общие значения по умолчанию для всего сайта должны быть определены как group_vars/all настройка. Групповые переменные обычно размещаются вместе с файлом инвентаризации. Они также могут возвращаться динамическим скриптом инвентаризации (см. Работа с динамической инвентаризацией) или определяться в таких вещах, как Red Hat Ansible Tower из пользовательского интерфейса или 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, безусловно, доступны задачам «что-то» и «что-то_ещё», но если гарантируется, что у «что-то» установлено foo = 12, даже если где-то глубоко в common_settings оно установлено foo = 20.
Итого, это приоритет, объяснённый более прямым способом. Не беспокойтесь о приоритете, просто подумайте, определяет ли ваша роль переменную, которая является значением по умолчанию, или «живой» переменной, которую вы определённо хотите использовать. Инвентаризация занимает место в приоритете прямо посередине, и если вы хотите принудительно переопределить что-либо, используйте -e.
Если вам это было немного сложно понять, взгляните на репозиторий ansible-examples на GitHub для более подробной информации о том, как всё это может взаимодействовать.
Использование расширенного синтаксиса переменных
Для получения информации об расширенном синтаксисе YAML, используемом для объявления переменных и более контролируемого размещения данных в файлах YAML, используемых Ansible, см. Расширенный синтаксис.
См. также
- О плейбуках
- Введение в плейбуки
- Условные операторы
- Условные операторы в плейбуках
- Фильтры
- Фильтры Jinja2 и их применение
- Циклы
- Циклы в плейбуках
- Роли
- Организация плейбуков по ролям
- Рекомендации по наилучшей практике
- Рекомендации по наилучшей практике в плейбуках
- Специальные переменные
- Список специальных переменных
- Пользовательская почтовая рассылка
- У вас есть вопрос? Загляните в группу Google!
- irc.freenode.net
- #ansible IRC чат-канал
© 2012–2018 Michael DeHaan
© 2018–2019 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/2.8/user_guide/playbooks_variables.html