Переменные и их приоритет
Урок 11 - выносим значения в переменные и разбираемся с их приоритетом.
«Один и тот же playbook, но порт nginx разный на проде и стейдже - решается переменными, а не копипастой».
Захардкоженные значения в playbook - путь к дублированию. Переменные позволяют вынести то, что меняется: версии, порты, пути, имена пользователей. Тогда один playbook обслуживает разные хосты и окружения.
Где объявлять переменные
| Место | Для чего |
|---|---|
В плее, секция vars | Локально для этого плея |
group_vars/<группа>.yml | Для всех хостов группы |
host_vars/<хост>.yml | Для конкретного хоста |
defaults/main.yml роли | Значения по умолчанию роли (низкий приоритет) |
Командная строка --extra-vars | Самый высокий приоритет |
# group_vars/web.yml
nginx_port: 80
nginx_worker_processes: 4
# host_vars/web2.example.com.yml - переопределение для одного хоста
nginx_port: 8080
Приоритет переменных
Одна и та же переменная может быть задана в нескольких местах. Ansible выбирает значение по приоритету: грубо от низкого к высокому - defaults роли, затем group_vars, затем host_vars, затем vars плея, и на самом верху - --extra-vars из командной строки. Чем «ближе к хосту» и чем «явнее» задано - тем выше приоритет.
НИЗКИЙ приоритет
role defaults
|
group_vars (all -> конкретная группа)
|
host_vars (конкретный хост)
|
play vars
|
--extra-vars (командная строка)
ВЫСОКИЙ приоритет (перебивает всё)
Как работает под капотом
Перед выполнением Ansible собирает все источники переменных для каждого хоста и «накладывает» их слоями: каждый следующий слой по приоритету перекрывает предыдущий, если ключ совпадает. В итоге у каждого хоста формируется свой финальный словарь переменных.
# Модель приоритета: слои перекрывают друг друга по возрастанию
layers = [
("role_defaults", {"nginx_port": 80, "workers": 2}),
("group_vars", {"workers": 4}),
("host_vars", {"nginx_port": 8080}),
("extra_vars", {}), # пусто в этот раз
]
final = {}
for name, layer in layers: # от низкого приоритета к высокому
final.update(layer) # верхний слой перекрывает совпадения
print("Итоговые переменные хоста:", final)
# nginx_port из host_vars (8080), workers из group_vars (4)
Попробуй сам ▶ Итог: nginx_port=8080 (host_vars перебил defaults), workers=4 (group_vars перебил defaults). Так Ansible и разрешает конфликты значений.
Частые ошибки
- Не понимать, почему «не та» переменная. Скорее всего, её переопределили на более высоком уровне. Проверь все слои.
- Класть всё в vars плея. Окруженческие значения - в group_vars/host_vars, дефолты - в defaults роли.
- Путать defaults и vars в роли.
defaults/main.yml- низкий приоритет (легко переопределить),vars/main.yml- высокий.
Best practices
- Дефолты роли - в
defaults/main.yml: их должно быть легко переопределить. - Различия окружений - в group_vars (например,
group_vars/production.yml). - Точечные исключения - в host_vars, но не злоупотребляй: много host_vars - запах архитектурной проблемы.
В реальной работе
Понимание приоритета переменных экономит часы отладки. Классический сценарий: «я задал порт 8080, а сервис слушает 80 - почему?». Ответ почти всегда в приоритете: где-то выше по цепочке (в host_vars или extra-vars) значение переопределено. Команда ansible-inventory --host имя_хоста показывает финальный набор переменных конкретного хоста со всеми наложениями - это первый инструмент диагностики. Зрелые проекты держат жёсткую дисциплину: дефолты только в defaults роли, различия окружений только в group_vars, а точечные исключения в host_vars - и никогда не размазывают одну и ту же переменную по пяти местам, иначе разобраться, какое значение победит, становится почти невозможно.
Итоги
Переменные выносят изменяемые значения из playbook и задаются на разных уровнях с чётким приоритетом: от defaults роли до --extra-vars. Ansible накладывает слои, и побеждает самый приоритетный. Дальше - факты, которые Ansible собирает о хостах сам.