Spec-Zone.ru › Ansible 2.7

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

  • Создание допустимых имён переменных
  • Определение переменных в инвентаре
  • Определение переменных в плейбуке
  • Определение переменных во включённых файлах и ролях
  • Использование переменных с 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": "23.253.245.60 55672 22",
        "SSH_CONNECTION": "23.253.245.60 55672 104.130.127.149 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 позволяет сохранять факты между запусками плейбуков, но эта функция должна быть включена вручную. Зачем это может быть полезно?

В очень большой инфраструктуре с тысячами хостов кэширование фактов можно настроить на ежедневный запуск. Конфигурация небольшого набора серверов может выполняться по требованию или периодически в течение дня. При включённом кэшировании фактов не нужно «обращаться» ко всем серверам для ссылки на переменные и информацию о них.

При включённом кэшировании фактов машина в одной группе может ссылаться на переменные машин в другой группе, несмотря на то, что с ними не было связи в текущем выполнении /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 — количество секунд для кэширования записанных фактов.

Регистрация переменных

Другое важное применение переменных — это выполнение команды и регистрация результата этой команды как переменной. Результаты будут различаться в зависимости от модуля. Использование -v при выполнении плейбуков покажет возможные значения результатов.

Значение выполняемой задачи в Ansible можно сохранить в переменной и использовать позже. Смотрите примеры этого в главе Условные операторы.

Хотя это упоминается и в другом месте этого документа, вот быстрый пример синтаксиса:

- hosts: web_servers

  tasks:

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

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

Зарегистрированные переменные действительны на хосте в течение остального времени выполнения плейбука, что равно времени существования «фактов» в Ansible. Эффективно, зарегистрированные переменные — это просто как факты.

При использовании register с циклом структура данных, помещённая в переменную во время цикла, будет содержать атрибут 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, которое содержит часть до первого периода, без остальной части домена.

Другие полезные магические переменные относятся к текущей игре или книге playbooks, включая:

Добавлено в версии 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 содержит базовый каталог playbook.

У нас также есть role_path, который вернёт текущий путь к роли (с 1.8). Это будет работать только внутри роли.

И наконец, ansible_check_mode (добавлено в версии 2.1), магическая переменная булевого типа, которая будет установлена в значение True, если вы запустите Ansible с --check.

Определение переменных в файлах

Отличная идея хранить playbooks под управлением системы контроля версий, но вы можете захотеть сделать исходный код playbook общедоступным, сохраняя при этом некоторые важные переменные конфиденциальными. Аналогично, иногда вы просто хотите хранить определенную информацию в разных файлах, отдельно от основного playbook.

Вы можете сделать это, используя внешний файл или файлы переменных, как это:

---

- hosts: all
  remote_user: root
  vars:
    favcolor: blue
  vars_files:
    - /vars/external_vars.yml

  tasks:

  - name: this is just a placeholder
    command: /bin/echo foo

Это устраняет риск совместного использования конфиденциальных данных с другими, когда вы делитесь исходным кодом playbook с ними.

Содержание каждого файла переменных представляет собой просто словарь YAML, например:

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

Примечание

Также можно хранить переменные на уровне хоста и на уровне группы в очень похожих файлах. Это подробно описано в Разделение данных, специфичных для хоста и группы.

Передача переменных через командную строку

Помимо 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, если вам нужно передать что-либо, что не должно быть строкой (булевы значения, целые числа, числа с плавающей точкой, списки и т. д.).

Добавлено в версии 1.2.

Формат 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"]}'

Добавлено в версии 1.3.

Формат YAML-строки:

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'

Добавлено в версии 1.3.

Переменные из файла JSON или YAML:

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

Это полезно для, среди прочего, задания группы хостов или пользователя для playbook.

Обработка экранирования кавычек и других специальных символов:

Добавлено в версии 1.2.

Убедитесь, что вы правильно экранируете кавычки как для разметки (например, 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"\!"\\\"\"}"

Добавлено в версии 1.3.

В этих случаях лучше использовать файл JSON или YAML, содержащий определения переменных.

Приоритет переменных: куда следует поместить переменную?

Многие задаются вопросом о том, как переменные переопределяют другие. В конечном итоге философия Ansible заключается в том, что лучше знать, куда поместить переменную, а затем об этом задумываться гораздо меньше.

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

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

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

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

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

  • значения командной строки (например, «-u user»)
  • значения по умолчанию роли [1]
  • переменные группы в файле или скрипте инвентаризации [2]
  • переменные группы инвентаризации/все [3]
  • переменные группы playbook/все [3]
  • переменные группы инвентаризации/* [3]
  • переменные группы playbook/* [3]
  • переменные хоста в файле или скрипте инвентаризации [2]
  • переменные хоста инвентаризации/«*» [3]
  • переменные хоста playbook/«*» [3]
  • факты хоста / кэшированные set_facts [4]
  • переменные игры
  • переменные игры vars_prompt
  • переменные игры vars_files
  • переменные роли (определены в role/vars/main.yml)
  • переменные блока (только для задач в блоке)
  • переменные задачи (только для задачи)
  • include_vars
  • set_facts / зарегистрированные переменные
  • параметры роли (и include_role)
  • параметры include
  • дополнительные переменные (всегда имеют приоритет)

В основном, все, что попадает в «значения по умолчанию роли» (папка defaults внутри роли), является наиболее изменчивым и легко переопределяется. Любая переменная в каталоге vars роли переопределяет предыдущие версии этой переменной в пространстве имён. Идея здесь заключается в том, чтобы вы становились всё более и более конкретными в области действия, тем самым повышая приоритет по сравнению с аргументами командной строки -e дополнительные переменные всегда имеют приоритет. Переменные хоста и/или инвентаризации могут иметь приоритет над значениями по умолчанию роли, но не явные include, например, каталог 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_ssh_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_ssh_user в инвентаризации.

Это сделано для того, чтобы настройки, специфичные для хоста, могли перезаписывать общие настройки. Эти переменные обычно определяются на хосте или группе в инвентаризации, но ведут себя как другие переменные.

Если вы хотите глобально перезаписать удаленного пользователя (даже над инвентаризацией), вы можете использовать дополнительные переменные. Например, если вы выполняете:

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

значение lola по-прежнему игнорируется, но ansible_user=maria имеет приоритет над всеми другими местами, где ansible_user (или ansible_ssh_user, или remote_user) может быть установлено.

Вы также можете перезаписать как обычную переменную в пьесе:

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

Ограничения переменных

Вы можете решить, где установить переменную, исходя из области, в которой это значение должно действовать. Ansible имеет три основные области:

  • Глобальная: устанавливается конфигурацией, переменными среды и командной строкой
  • Пьеса: каждая пьеса и содержащие структуры, записи переменных (vars; vars_files; vars_prompt), значения по умолчанию ролей и переменные.
  • Хост: переменные, напрямую связанные с хостом, такие как инвентаризация, include_vars, факты или результаты зарегистрированных задач

Примеры установки переменных

Давайте рассмотрим несколько примеров и то, где вы бы разместили то или иное значение в зависимости от желаемого контроля над значениями.

Прежде всего, переменные групп являются мощным инструментом.

Общие значения по умолчанию для всего сайта должны быть определены как group_vars/all настройка. Переменные групп обычно размещаются вместе с файлом инвентаризации. Они также могут возвращаться динамическим скриптом инвентаризации (см. Работа с динамической инвентаризацией) или определяться в таких средствах, как 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 оно будет установлено в 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.7/user_guide/playbooks_variables.html

Spec-Zone.ru

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