Переменные, facts и приоритет
Где объявлять переменные, что такое facts (ansible_facts), register, set_fact и как Ansible разрешает конфликты по приоритету.
Зачем переменные
Один playbook, несколько окружений: dev слушает на порту 8080, prod — на 443.
Пути к логам разные, версии пакетов разные. Дублировать плейбук под каждый стенд — катастрофа.
Переменные решают это: пишешь логику один раз, а конкретные значения подставляешь снаружи в зависимости от окружения.
Где объявлять переменные
Ansible читает переменные из многих мест. Вот основные.
vars: прямо в плее — самый простой способ для локальных значений:
- name: Deploy app
hosts: webservers
vars:
app_port: 8080
app_user: deploy
tasks:
- name: start service on port {{ app_port }}
...
vars_files: — внешний YAML-файл — удобно, когда переменных много
или их хочется переиспользовать в нескольких плеях:
vars_files:
- vars/common.yml
- vars/prod.yml
group_vars/ и host_vars/ — директории рядом с инвентарём
(или с playbook). Ansible загружает их автоматически по имени группы или хоста.
Структура:
inventory/
hosts.ini
group_vars/
all.yml # для всех хостов
webservers.yml # только для группы webservers
host_vars/
web01.yml # только для хоста web01
Инвентарь напрямую — можно указывать переменные прямо в hosts.ini
или hosts.yml, но читается хуже и годится только для простых случаев:
[webservers]
web01 ansible_user=deploy app_port=8080
web02 ansible_user=deploy app_port=8080
[webservers:vars]
nginx_worker_processes=4
defaults/main.yml и vars/main.yml в роли — подробно разобрали
в уроке про роли. Коротко: defaults — самый низкий приоритет
(легко переопределить снаружи), vars — высокий (внутренние константы роли).
-e / --extra-vars в командной строке — наивысший приоритет,
всегда побеждает. Удобно для разовых переопределений и CI-скриптов:
ansible-playbook deploy.yml -e "app_version=2.1.0 env=prod"
# или из файла
ansible-playbook deploy.yml -e "@overrides.yml"
set_fact — установить переменную прямо во время выполнения плея.
Подробнее ниже.
register — сохранить результат задачи в переменную.
Подробнее ниже.
Синтаксис: {{ var }} и YAML-кавычки
Ansible использует Jinja2 для подстановки переменных:
- name: create dir
file:
path: /opt/{{ app_name }}/data
state: directory
Важный момент: если значение начинается с {{, YAML воспринимает
фигурные скобки как начало словаря и падает с ошибкой. Нужно обернуть
всё значение в кавычки:
# ПЛОХО - YAML parsing error
dest: {{ app_dir }}/config.yml
# ХОРОШО
dest: "{{ app_dir }}/config.yml"
Правило простое: если строка начинается с {{ — всегда ставь кавычки.
Facts: что Ansible знает о хосте
Facts — это переменные, которые Ansible собирает о хосте перед запуском
задач. За это отвечает модуль setup, он запускается автоматически в начале
каждого плея (фаза "Gathering Facts").
Собранные данные лежат в словаре ansible_facts. Часто используемые:
| Переменная | Что содержит |
|---|---|
ansible_facts['os_family'] | Семейство ОС: RedHat, Debian, ... |
ansible_facts['distribution'] | Конкретный дистрибутив: Ubuntu, Rocky |
ansible_facts['default_ipv4']['address'] | IP основного интерфейса |
ansible_facts['memtotal_mb'] | Общая RAM в мегабайтах |
ansible_facts['hostname'] | Hostname хоста |
Для многих facts есть короткие синонимы без префикса ansible_facts[...]:
ansible_os_family, ansible_distribution, ansible_default_ipv4.address и т.д.
Посмотреть все facts хоста одной командой:
ansible web01 -m setup
# или только нужные:
ansible web01 -m setup -a "filter=ansible_mem*"
Если facts не нужны (экономим время, хост без Python), их можно отключить:
- name: Quick ping
hosts: all
gather_facts: no
tasks:
- ping:
Это тот же случай, что разбирали в уроке про хосты без Python.
Пример использования facts в задаче:
- name: install Apache on Debian-based systems
apt:
name: apache2
state: present
when: ansible_facts['os_family'] == "Debian"
register: захватить результат задачи
register сохраняет полный результат задачи в переменную. Потом можно
использовать его в следующих задачах через when, debug, шаблоны.
Что хранится в зарегистрированной переменной:
.stdout— вывод stdout (строка).stderr— вывод stderr.rc— код возврата.changed— изменился ли хост.stdout_lines— stdout разбитый на список строк
Пример:
- name: check if app is running
command: systemctl is-active myapp
register: app_status
ignore_errors: yes
- name: show status
debug:
msg: "App status: {{ app_status.stdout }}"
- name: start app if stopped
systemd:
name: myapp
state: started
when: app_status.rc != 0
ignore_errors: yes здесь важен: systemctl is-active возвращает rc=3
если сервис не активен, и без этой опции задача завершится с ошибкой
раньше, чем мы успеем проверить статус.
set_fact: динамически задать переменную
set_fact позволяет вычислить и задать переменную прямо во время выполнения плея.
Переменная затем доступна во всех последующих задачах.
- name: set deploy path based on environment
set_fact:
deploy_path: "/opt/{{ app_name }}/{{ env }}"
config_path: "/etc/{{ app_name }}/{{ env }}.conf"
- name: create deploy dir
file:
path: "{{ deploy_path }}"
state: directory
По приоритету обычный set_fact стоит высоко: выше group_vars, play vars:
и role vars/main.yml, но ниже -e.
Тонкость для собеседования: обычный set_fact имеет высокий приоритет (выше
play vars и role vars). Но set_fact с cacheable: true ведёт себя как
собранный факт — такие значения имеют низкий приоритет (ниже play vars).
То есть один и тот же модуль попадает на разные уровни лестницы в зависимости
от cacheable.
Приоритет переменных
Когда одна и та же переменная определена в нескольких местах, Ansible берёт значение из источника с наивысшим приоритетом и игнорирует остальные. Слияния по умолчанию нет: побеждает один источник.
(Опция hash_behaviour=merge в ansible.cfg меняет это для словарей,
но она не рекомендуется и считается плохой практикой, не используй её
в новых проектах.)
Упрощённая лестница от низшего к высшему:
Практическое правило: -e всегда выигрывает; role defaults всегда проигрывает.
Схема выше упрощена. Внутри второго уровня источники тоже идут по приоритету:
host_vars побеждает group_vars, а group_vars рядом с playbook побеждает
group_vars из inventory. Полная иерархия Ansible — 21 уровень (от role
defaults до extra vars); официальный список в документации, ссылка ниже.
Официальный список: docs.ansible.com → «Using Variables» → «Variable precedence: Where should I put a variable?».
Типичная ошибка: задал переменную в group_vars, но не понимаешь почему
она не работает. Скорее всего в роли есть vars/main.yml с тем же именем
и более высоким приоритетом. Или наоборот: переопределяешь в defaults,
а оно не берётся, потому что переменная уже задана в inventory.
Что важно запомнить
-e / --extra-vars — наивысший приоритет, всегда побеждает. Используй
в CI для version/env-параметров.
role defaults — самый низкий приоритет; то, что пользователь роли
должен легко переопределить.
Когда одна переменная задана в нескольких местах — побеждает один источник,
слияния нет (если не включать нерекомендуемый hash_behaviour=merge).
register сохраняет результат задачи: .stdout, .rc, .changed, .stdout_lines.
Facts собираются автоматически; gather_facts: no их отключает, когда
скорость важнее.
В следующем уроке — безопасный запуск в проде: --check, --diff, теги и --limit.
Дрилл · вопросы как на собесе
Проверь себя без подсказок
Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.
Разница между `host_vars`, `group_vars` и переменными внутри роли (`defaults` vs `vars`) — порядок precedence.
ANS-011Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс