Плейбук решает ровно эту боль. Это файл, в котором ты один раз описываешь желаемое состояние системы, а дальше Ansible сам приводит сервер к нему - хоть один раз, хоть сто раз подряд с тем же результатом. В этом уроке разберём, что такое playbook, из чего он состоит, и напишем первый рабочий ansible playbook yml с нуля.
Что такое playbook и чем он лучше ad hoc
Плейбук ansible - это обычный текстовый YAML-файл, в котором лежит список сценариев (play). Каждый play связывает группу хостов из инвентаря с набором задач (tasks), а каждая задача дёргает один модуль с конкретными параметрами. Запустил файл - Ansible по очереди прошёлся по задачам на всех указанных хостах.
Чем это лучше отдельных команд:
- Повторяемость. Один и тот же ansible playbook ты гоняешь на dev, stage и prod. Никакой "а что я там вводил в консоли неделю назад".
- Версионирование. Файл лежит в git. Видно, кто и что менял в инфраструктуре, можно откатиться.
- Идемпотентность по умолчанию. Прогнал второй раз - модули видят, что всё уже сделано, и ничего не трогают. Статус ok вместо changed.
- Порядок и логика. Задачи идут сверху вниз, можно навесить условия, циклы, обработчики (handlers).

Синтаксис YAML: отступы решают всё
Прежде чем писать, договоримся про YAML, потому что 90 процентов первых ошибок новичка - это не Ansible, а кривой отступ. YAML - это формат для данных, и в нём важны три вещи.
Отступы только пробелами. Никаких табов. Вообще. Один таб - и парсер падает с невнятной ошибкой. Настрой редактор так, чтобы Tab вставлял пробелы (обычно 2 пробела на уровень). Вложенность задаётся именно количеством пробелов слева.
Списки. Элемент списка начинается с дефиса и пробела. Вот список из трёх строк:
Код: Выделить всё
packages:
- httpd
- mod_ssl
- firewalld
Код: Выделить всё
service:
name: httpd
state: started
enabled: true
Анатомия плейбука и первый ansible playbook example
Теперь сама структура. Файл начинается с трёх дефисов (--- маркер начала YAML-документа, не обязателен, но это хороший тон). Дальше идёт список play. У play есть набор ключей верхнего уровня:
- name - человекочитаемое описание play. Не обязателен, но всегда пиши, иначе в выводе будет безымянный шум.
- hosts - на каких хостах или группах из инвентаря работаем. Обязательный ключ.
- become - повышение привилегий (true значит выполнять через sudo от root). Нужно почти всегда, когда ставишь пакеты или правишь систему.
- vars - переменные этого play.
- tasks - список задач, каждая со своим name и вызовом модуля.
Код: Выделить всё
---
- name: Развернуть базовый веб-сервер
hosts: web
become: true
vars:
web_package: httpd
web_service: httpd
tasks:
- name: Установить пакет веб-сервера
ansible.builtin.dnf:
name: "{{ web_package }}"
state: present
- name: Положить стартовую страницу
ansible.builtin.copy:
content: "Привет от Ansible\n"
dest: /var/www/html/index.html
owner: root
group: root
mode: "0644"
- name: Открыть HTTP в firewalld
ansible.posix.firewalld:
service: http
permanent: true
immediate: true
state: enabled
- name: Запустить и включить сервис
ansible.builtin.service:
name: "{{ web_service }}"
state: started
enabled: true
Запускаем:
Код: Выделить всё
ansible-playbook -i inventory site.yml
Код: Выделить всё
ansible-playbook --syntax-check site.yml
ansible-playbook -i inventory site.yml --check
Маленькая пометка про другие дистрибутивы. Здесь всё под RHEL/Fedora: dnf, systemd, firewalld. На Ubuntu/Debian пакет ставился бы через ansible.builtin.apt, а пакет веб-сервера называется apache2, не httpd. В RED OS и Astra Linux менеджер пакетов apt-совместимый или dnf, смотри по дистрибутиву. Универсальный приём - вынести имя пакета в переменную (как у нас web_package) и переопределять её под платформу.
Типичные грабли
- Таб вместо пробелов. Самая частая. Ошибка вида "found character that cannot start any token" - почти всегда таб или лишний пробел. Включи отображение невидимых символов в редакторе.
- Нет пробела после двоеточия. state:present не парсится. Всегда state: present.
- Сломанный отступ у задачи. Если ключ модуля выровнен не так, как другие, YAML либо ругается, либо молча трактует параметр как новую задачу.
- Забыл become. dnf и правка системных файлов требуют root. Без become: true получишь "Permission denied".
- Двоеточие внутри значения. Если в строке есть двоеточие (например в тексте сообщения), бери всё значение в кавычки, иначе YAML посчитает это словарём.
- Короткие имена модулей. copy вместо ansible.builtin.copy работает, но на экзамене и в команде лучше писать FQCN - меньше конфликтов и понятнее, откуда модуль.
Руками, без копипасты целиком - так синтаксис ложится в память:
- Создай инвентарь inventory с группой web и одним-двумя хостами.
- Напиши site.yml из этого урока заново, своими руками, следя за отступами.
- Прогони ansible-playbook --syntax-check site.yml. Намеренно убери один пробел после двоеточия и посмотри, как именно ругается парсер - полезно узнать ошибку в лицо.
- Запусти плейбук, затем сразу повтори. Убедись, что во второй раз все задачи ok (changed=0 в PLAY RECAP).
- Поменяй текст стартовой страницы в content и прогони ещё раз - теперь changed будет только у задачи с copy.
- Из каких обязательных ключей состоит минимальный play и за что отвечает hosts?
- Почему в YAML нельзя использовать табы и чем грозит отсутствие пробела после двоеточия?
- Что покажет PLAY RECAP при повторном запуске идемпотентного плейбука и почему?
- Зачем нужен become: true и что произойдёт без него в задаче с dnf?
Плейбук - это YAML-файл с одним или несколькими play, где play связывает хосты с задачами, а задача вызывает модуль. Ты описываешь желаемое состояние, а не последовательность команд, и получаешь повторяемость, версионирование и идемпотентность даром. Ключевые навыки на старте - чистый YAML с правильными отступами и понимание анатомии play (name, hosts, become, vars, tasks). Дальше будут переменные, циклы, условия и обработчики, но фундамент именно здесь. И помни: на EX294 плейбуки пишут руками под таймером, так что доводи синтаксис до автоматизма уже сейчас.