← Ansible
15 мин · средне · Урок 8 из 12

Переменные, 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, шаблоны.

Что хранится в зарегистрированной переменной:

Пример:

- 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 меняет это для словарей, но она не рекомендуется и считается плохой практикой, не используй её в новых проектах.)

Упрощённая лестница от низшего к высшему:

приоритет role defaults/main.yml НИЗШИЙ inventory → group_vars/ → host_vars/ vars: / vars_files: в плее role vars/main.yml set_fact / register -e / --extra-vars ВЫСШИЙ приоритет, побеждает WINNER

Практическое правило: -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.

Дрилл · вопросы как на собесе

Проверь себя без подсказок

Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.

01

Разница между `host_vars`, `group_vars` и переменными внутри роли (`defaults` vs `vars`) — порядок precedence.

ANS-011

Прошли материал до конца? Отметьте урок пройденным.