В этом уроке разберём всё про ansible переменные: какие бывают типы, где их объявлять и, самое важное, кто кого перебивает. Без этой картины ты будешь воевать с собственными плейбуками.
Типы значений: строки, списки, словари
Сами по себе ansible vars - это обычные значения YAML. Три формы, которые ты встретишь повсюду.
Простое (скаляр) - строка, число, булево:
Код: Выделить всё
app_name: nginx
http_port: 8080
enable_ssl: true
Код: Выделить всё
packages:
- httpd
- mariadb-server
- php-fpm
Код: Выделить всё
db:
host: 127.0.0.1
port: 3306
name: shop
Код: Выделить всё
- name: Показать порт БД
ansible.builtin.debug:
msg: "БД слушает {{ db.host }}:{{ db['port'] }}"
Код: Выделить всё
greeting: "{{ user_name }}, привет"

Где объявлять ansible variables
Мест на удивление много, и каждое со своим смыслом.
- В плейбуке через vars - локально для конкретной игры.
- В отдельных файлах через vars_files - выносим длинные наборы.
- В инвентаре - рядом с хостами и группами.
- group_vars/ и host_vars/ - файлы для групп и отдельных хостов, самый чистый способ.
- В ролях - defaults/main.yml (мягкие дефолты) и vars/main.yml (жёсткие).
- register - результат выполнения задачи.
- set_fact - вычисленная переменная на лету.
- extra vars через -e - из командной строки, перебивает всё.
Код: Выделить всё
- hosts: web
vars:
http_port: 8080
tasks:
- name: Проверить, открыт ли порт
ansible.builtin.command: ss -ltn sport = :{{ http_port }}
register: port_check
changed_when: false
- name: Запомнить факт на основе вывода
ansible.builtin.set_fact:
port_is_open: "{{ port_check.rc == 0 }}"
В роли defaults/main.yml - это значения "по умолчанию, легко переопределяемые". Туда кладут всё, что пользователь роли захочет настроить. А vars/main.yml - жёсткие константы роли, которые перебить намного сложнее. Разница между ними принципиальна, и она прямо вытекает из таблицы приоритета.
Приоритет переменных ansible - таблица, на которой срезаются
Вот сердце урока. Когда одно и то же имя объявлено в нескольких местах, Ansible выбирает значение по строгому порядку. Приоритет переменных ansible идёт от самого слабого к самому сильному:
- 1. значения командной строки (-u, -c и т.п., НЕ -e)
- 2. role defaults (defaults/main.yml)
- 3. inventory file / script group vars
- 4. inventory group_vars/all
- 5. playbook group_vars/all
- 6. inventory group_vars/*
- 7. playbook group_vars/*
- 8. inventory file / script host vars
- 9. inventory host_vars/*
- 10. playbook host_vars/*
- 11. host facts и кешированные set_facts
- 12. play vars
- 13. play vars_prompt
- 14. play vars_files
- 15. role vars (vars/main.yml)
- 16. block vars
- 17. task vars
- 18. include_vars
- 19. set_fact / registered vars
- 20. role (и include_role) params
- 21. include params
- 22. ansible extra vars (-e) - всегда побеждает
Отсюда практические выводы. Дефолты роли (defaults) лежат почти на дне - их легко перекрыть из group_vars или play vars, и это правильно: роль задаёт разумное значение, а пользователь его подстраивает. А вот role vars (vars/main.yml) сидят высоко - их перебьёт только set_fact, include_vars, параметры роли и extra vars. Поэтому в defaults кладём настраиваемое, в vars - то, что менять не должны.role defaults - самый слабый. set_fact и register - очень сильные. ansible extra vars (-e) бьёт абсолютно всё.
Проверим на живом примере. Объявим http_port разом в defaults роли и через -e:
Код: Выделить всё
# roles/web/defaults/main.yml
http_port: 80
Код: Выделить всё
ansible-playbook site.yml -e "http_port=9090"
Типичные грабли
- Переопределяю в group_vars, а работает старое значение. Скорее всего, то же имя сидит в play vars или vars_files - они выше по таблице. Или висит set_fact из прошлой задачи.
- Два файла group_vars/all - в инвентаре и рядом с плейбуком. Playbook group_vars сильнее inventory group_vars (строки 4 и 5). Люди про второй файл забывают.
- YAML падает на значении с {{. Оборачивай в кавычки: key: "{{ value }}".
- when: var ведёт себя странно. Проверь, строка у тебя "false" или булево false. Строка "false" в условии истинна, потому что непустая.
- Точечный доступ db.name возвращает не то. Если ключ называется как метод Python, бери db['name'].
- Дефис в имени: my-var. Так нельзя. В именах переменных только буквы, цифры и подчёркивание, и начинаться с буквы. Naming - снейк_кейс: app_user, db_port.
Мини-лаба: пощупать приоритет руками
Десять минут практики дадут больше, чем чтение таблицы.
- Создай роль: ansible-galaxy init roles/demo. В roles/demo/defaults/main.yml впиши greeting: "из defaults".
- Сделай group_vars/all.yml с тем же greeting: "из group_vars". Запусти плейбук с ansible.builtin.debug: msg="{{ greeting }}". Какое значение? Должно быть из group_vars.
- Добавь vars: { greeting: "из play vars" } в саму игру. Перезапусти. Теперь play vars победили.
- Запусти с -e 'greeting="из extra"'. Угадай результат до запуска, потом проверь. extra vars забирают всё.
- Добавь задачу set_fact: greeting="из факта" перед debug и убери -e. Посмотри, как факт перебивает play vars.
- Прогони ansible-inventory --list -y и ansible -m debug -a "var=greeting" all, чтобы увидеть, откуда хост берёт значение.
Контрольные вопросы
- Что перебьёт значение из roles/web/defaults/main.yml: group_vars, play vars или extra vars? А что НЕ перебьёт его?
- Чем role defaults отличается от role vars и почему одно кладут в defaults, а другое в vars?
- Как обратиться к ключу словаря, если он называется count, и почему точечная форма тут опасна?
- Где в таблице приоритета стоят set_fact и register относительно play vars?
Переменные в Ansible - это просто строки, списки и словари, но вся боль не в них, а в том, где они объявлены. Запомни направление: defaults роли внизу, extra vars наверху, set_fact и register - высоко. Когда значение "не то", не лезь сразу в код - открой таблицу приоритета и пройди её сверху вниз, ища, кто перебил. На RHCE это сэкономит тебе и нервы, и баллы.