Spec-Zone.ru › Ansible 2.9

Использование переменных

  • Создание допустимых имен переменных
  • Определение переменных в инвентаре
  • Определение переменных в книге задач
  • Определение переменных во включённых файлах и ролях
  • Использование переменных с Jinja2
  • Преобразование переменных с помощью фильтров Jinja2
  • Подождите, а вот и проблема с YAML
  • Переменные, полученные от систем: Факты
    • Отключение фактов
    • Локальные факты (facts.d)
    • Версия Ansible
    • Кэширование фактов
  • Регистрация переменных
  • Доступ к сложным данным переменных
  • Получение информации о других хостах с помощью магических переменных
  • Определение переменных в файлах
  • Передача переменных через командную строку
  • Приоритет переменных: Куда поместить переменную?
    • Ограничение области действия переменных
    • Примеры того, где задать переменную
  • Использование расширенного синтаксиса переменных

Хотя автоматизация упрощает повторяемость действий, все системы не идентичны; некоторые могут потребовать немного отличающейся конфигурации. В некоторых случаях наблюдаемое поведение или состояние одной системы может повлиять на конфигурацию других систем. Например, вам может потребоваться узнать 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 %}

Вы можете использовать этот прием для указания прокси-сервера frontend на все серверы приложений, для настройки правильных правил брандмауэра между серверами и т. д. Однако необходимо убедиться, что данные фактов этих хостов заполнены, например, путем запуска задания на них, если факты не были недавно кэшированы (кэширование фактов было добавлено в 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 заключается в том, что лучше знать, куда поместить переменную, а затем меньше об этом думать.

Избегайте определения переменной «x» в 47 местах и затем задавайте вопрос «какая x используется». Почему? Потому что это не философия Ansible по поводу выполнения вещей.

Существует только одно Эмпайр-стейт-билдинг. Одна Моне Лизза и т. д. Определите, куда поместить переменную, и не усложняйте.

Однако давайте рассмотрим порядок применения! Он существует. Это реальная вещь, и вам, возможно, пригодится.

Если несколько переменных с одинаковым именем определены в разных местах, они перекрываются в определенном порядке.

Вот порядок применения от наименьшего к наибольшему (последняя переменная получает приоритет):

  1. значения командной строки (например, «-u user»)
  2. значения по умолчанию роли [1]
  3. переменные групп в файле или скрипте инвентаризации [2]
  4. переменные группы инвентаризации/all [3]
  5. переменные группы книги команд/all [3]
  6. переменные группы инвентаризации/* [3]
  7. переменные группы книги команд/* [3]
  8. переменные хостов в файле или скрипте инвентаризации [2]
  9. переменные хоста инвентаризации/* [3]
  10. переменные хоста книги команд/* [3]
  11. данные фактов хоста / кэшированные установленные факты [4]
  12. переменные задания
  13. переменные задания_prompt
  14. переменные задания_файлы
  15. переменные роли (определены в role/vars/main.yml)
  16. переменные блока (только для задач в блоке)
  17. переменные задачи (только для задачи)
  18. include_vars
  19. set_facts / зарегистрированные переменные
  20. параметры роли (и include_role)
  21. параметры include
  22. дополнительные переменные (всегда имеют приоритет)

В основном, все, что попадает в «значения по умолчанию роли» (папка 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; vars_files; vars_prompt), значения по умолчанию роли и vars.
  • Хост: переменные, напрямую связанные с хостом, такие как инвентаризация, 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, безусловно, доступны для задач «something» и «something_else», но если гарантировано, что у «something» будет установлено foo = 12, даже если где-то глубоко в common_settings foo будет установлено равным 20.

Итак, это приоритет, объяснённый более прямым способом. Не беспокойтесь о приоритете, просто подумайте, определяет ли ваша роль переменную, которая является значением по умолчанию, или «живой» переменной, которую вы определённо хотите использовать. Инвентаризация находится в приоритете прямо посередине, и если вы хотите принудительно переопределить что-то, используйте -e.

Если вы нашли это немного сложным для понимания, ознакомьтесь с репозиторием ansible-examples на GitHub, чтобы узнать больше о том, как все эти вещи могут работать вместе.

Использование расширенного синтаксиса переменных

Для получения информации о расширенном синтаксисе YAML, используемом для объявления переменных и получения большего контроля над данными, размещаемыми в файлах YAML, используемых Ansible, см. Расширенный синтаксис.

См. также

О книгах задач
Введение в книги задач
Условные операторы
Условные операторы в книгах задач
Фильтры
Фильтры Jinja2 и их использование
Циклы
Циклы в книгах задач
Роли
Организация книги задач с помощью ролей
Рекомендации по разработке
Рекомендации по разработке книг задач
Специальные переменные
Список специальных переменных
Список рассылки пользователей
У вас есть вопрос? Загляните на эту страницу Google Group!
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.9/user_guide/playbooks_variables.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API