С чего начать Первые шаги трек · 15 ступеней
Linux Labs

ansible-playbook: запуск плейбука

13 мин Ubuntu 24.04 · ansible-core 2.16

Запускает плейбук Ansible на машинах инвентаря и по каждой задаче пишет ok, changed, skipping или failed.

Шпаргалка

8 команд Версия для печати
  • ansible-playbook -i inventory.ini site.yml прогон
  • ansible-playbook -i inventory.ini site.yml -C -D что изменится
  • ansible-playbook -i inventory.ini site.yml --syntax-check
  • ansible-playbook -i inventory.ini site.yml --list-tasks
  • ansible-playbook -i inventory.ini site.yml -l node2 одна машина
  • ansible-playbook -i inventory.ini site.yml -t page задачи с тегом
  • ansible-playbook -i inventory.ini site.yml -e site_title=Demo
  • ansible-playbook -i inventory.ini site.yml -v подробнее

Обзор

ansible-playbook берёт плейбук — YAML-файл с плеями (play) — и инвентарь, подключается к машинам и выполняет задачи по порядку. Что такое инвентарь, модули и разовые команды ansible, — в статье ansible. На учебной машине стоят ansible-core 2.16 и пакет ansible 9.2 со 103 коллекциями (ansible-galaxy collection list); сети наружу нет, ставить коллекции из Galaxy нельзя.

Плейбук для примеров — site.yml:

yaml
- name: Демо-сайт
  hosts: local
  become: true
  vars:
    site_title: Демо
  tasks:
    - name: Каталог сайта
      ansible.builtin.file:
        path: /srv/demo
        state: directory
        mode: "0755"

    - name: Страница из шаблона
      ansible.builtin.template:
        src: index.html.j2
        dest: /srv/demo/index.html
        mode: "0644"
      notify: Перечитать nginx
      tags: page

    - name: Отметка времени
      ansible.builtin.shell: date > /srv/demo/stamp

  handlers:
    - name: Перечитать nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

Шаблон templates/index.html.j2 лежит рядом с плейбуком — template ищет src в каталоге templates/:

text
<h1>{{ site_title }}</h1>
<p>{{ ansible_hostname }}</p>

Инвентарь inventory.ini — одна машина, эта же, без SSH:

ini
[local]
localhost ansible_connection=local

Вывод прогона

Первый прогон (звёздочки в заголовках укорочены, строка итога перенесена):

text
student@node1:~/demo$ ansible-playbook -i inventory.ini site.yml

PLAY [Демо-сайт] ***

TASK [Gathering Facts] ***
ok: [localhost]

TASK [Каталог сайта] ***
changed: [localhost]

TASK [Страница из шаблона] ***
changed: [localhost]

TASK [Отметка времени] ***
changed: [localhost]

RUNNING HANDLER [Перечитать nginx] ***
changed: [localhost]

PLAY RECAP ***
localhost  : ok=5    changed=4    unreachable=0    failed=0
             skipped=0    rescued=0    ignored=0

Gathering Facts — сбор фактов о машине (ansible_hostname в шаблоне берётся отсюда). Состояние задачи на каждой машине:

Состояние Что значит
ok на машине уже так, как описано, — ничего не менялось
changed задача изменила машину
skipping задача пропущена: условие when или --check у shell
fatal … FAILED! ошибка; дальше на этой машине задачи не идут
UNREACHABLE! к машине не подключиться

Итог: PLAY RECAP

PLAY RECAP — строка на каждую машину: сколько задач в каком состоянии.

Поле Что считает
ok задачи без изменений и с изменениями — все, что прошли
changed задачи, которые изменили машину (входят и в ok)
unreachable машина недоступна — до задач дело не дошло
failed упавшие задачи; после первой на этой машине дальше не идут
skipped пропущенные задачи
rescued, ignored ошибки, перехваченные rescue: или ignore_errors: true

Хороший повторный прогон — changed=0 failed=0. Код возврата: 0 — всё прошло, 2 — на какой-то машине задача упала, 4 — машина недоступна или плейбук не разобран (ошибка YAML, неизвестный модуль), 1 — другая ошибка, например не осталось машин.

Повторный прогон

text
TASK [Каталог сайта] ***
ok: [localhost]

TASK [Страница из шаблона] ***
ok: [localhost]

TASK [Отметка времени] ***
changed: [localhost]

PLAY RECAP ***
localhost  : ok=4    changed=1    unreachable=0    failed=0

Модули file и template сначала смотрят, что на машине, и меняют только разницу — это идемпотентность. shell не знает, что делает его команда, и всегда пишет changed. Хороший плейбук на втором прогоне даёт changed=0: для задачи — модуль вместо shell, или creates: (не запускать, если файл есть), или changed_when: (своё правило, когда считать изменением).

Обработчики

Обработчик (handler) — задача, которая выполняется, только если её позвала другая задача через notify и та задача дала changed. В первом прогоне шаблон изменился — nginx перечитал конфиг; во втором шаблон ok, и RUNNING HANDLER в выводе нет.

  • Обработчики идут в конце плея, после всех задач, и каждый — один раз, сколько бы задач его ни позвали.
  • notify ищет обработчик по имени name (или по listen) точно, с учётом регистра; опечатка — ошибка ansible-playbook.
  • Если задача упала, обработчики, позванные до неё, не выполнятся; --force-handlers выполняет их и тогда. Позвать раньше конца — задача ansible.builtin.meta: flush_handlers.

Шаблон и переменные

Модуль template собирает файл из шаблона .j2: каждое {{ имя }} заменяется значением переменной. Готовый файл он сравнивает с тем, что на машине, и пишет changed, только если они различаются, — поэтому шаблон и обработчик дают changed=0 на повторном прогоне.

Откуда берутся значения:

  • vars: плея — site_title: Демо;
  • факты машины из Gathering Facts — ansible_hostname (node1);
  • переменные из инвентаря — строка машины или [группа:vars];
  • -e site_title=Новый из командной строки — главнее всех остальных.

Переменная, которой нет, роняет задачу — ansible-playbook. Значение по умолчанию прямо в шаблоне — {{ site_title | default('Демо') }}. Что изменит шаблон, покажет --check --diff.

Проверка без изменений

--check (-C) прогоняет плейбук, ничего не меняя; --diff (-D) печатает, что изменилось бы в файлах:

text
student@node1:~/demo$ ansible-playbook -i inventory.ini site.yml \
> --check --diff -e site_title=Новый
...
TASK [Страница из шаблона] ***
--- before: /srv/demo/index.html
+++ after: /home/student/.ansible/tmp/…/index.html.j2
@@ -1,2 +1,2 @@
-<h1>Демо</h1>
+<h1>Новый</h1>
 <p>node1</p>

changed: [localhost]

TASK [Отметка времени] ***
skipping: [localhost]

RUNNING HANDLER [Перечитать nginx] ***
changed: [localhost]

PLAY RECAP ***
localhost  : ok=4    changed=2    unreachable=0    failed=0
             skipped=1    rescued=0    ignored=0

changed здесь значит «изменилось бы»: файл остался прежним, nginx никто не трогал. shell и command в режиме проверки пропускаются — по ним --check ничего не скажет. Плейбук, у которого после прогона --check --diff не показывает ни одного changed, описывает машину такой, какая она есть.

Ключи

16

Основные ключи — в таблице ниже; полный список — ansible-playbook --help. Без -i Ansible ищет инвентарь в /etc/ansible/hosts, а на учебной машине его нет — инвентарь передают явно.

Ключ Что делает Пример
-i --inventory FILE Инвентарь — файл со списком машин и групп. ansible-playbook -i inventory.ini site.yml
-C --check Прогон без изменений — что изменилось бы; shell и command пропускаются. ansible-playbook -i inventory.ini site.yml --check
-D --diff Показать разницу в файлах и шаблонах, обычно вместе с --check. ansible-playbook -i inventory.ini site.yml --check --diff
-l --limit PATTERN Только эти машины или группы из инвентаря. ansible-playbook -i inventory.ini site.yml -l node2
-e --extra-vars VARS Переменные с наибольшим приоритетом — key=value или @файл. ansible-playbook -i inventory.ini site.yml -e site_title=Demo
-b --become Выполнять задачи через sudo — как become true в плейбуке. ansible-playbook -i inventory.ini site.yml -b
-t --tags TAGS Только задачи с этими тегами. ansible-playbook -i inventory.ini site.yml -t page
--syntax-check Только разобрать плейбук; на исправном печатает его имя. ansible-playbook -i inventory.ini site.yml --syntax-check
--list-tasks Задачи по порядку с тегами, без прогона. ansible-playbook -i inventory.ini site.yml --list-tasks
--list-hosts На какие машины пойдёт плейбук, без прогона. ansible-playbook -i inventory.ini site.yml --list-hosts
-v --verbose Подробнее — результат каждой задачи; -vvv — с подключением по SSH. ansible-playbook -i inventory.ini site.yml -vvv
--skip-tags TAGS Все задачи, кроме помеченных этими тегами. ansible-playbook -i inventory.ini site.yml --skip-tags page
--start-at-task NAME Начать с задачи с этим именем, пропустив предыдущие. ansible-playbook -i inventory.ini site.yml --start-at-task 'Stamp'
--step Спрашивать перед каждой задачей — выполнить, пропустить или идти дальше без вопросов. ansible-playbook -i inventory.ini site.yml --step
--force-handlers Выполнить позванные обработчики, даже если задача на машине упала. ansible-playbook -i inventory.ini site.yml --force-handlers
-u --user USER Под каким пользователем входить на машины по SSH. ansible-playbook -i inventory.ini site.yml -u deploy

Ничего не найдено. Попробуй запрос короче.

Рецепты

4

Проверить плейбук до прогона

bash
ansible-playbook -i inventory.ini site.yml --syntax-check
ansible-playbook -i inventory.ini site.yml --list-hosts
ansible-playbook -i inventory.ini site.yml --list-tasks
ansible-playbook -i inventory.ini site.yml --check --diff

--syntax-check на исправном плейбуке печатает только playbook: site.yml. --list-tasks — задачи по порядку с тегами:

text
  play #1 (local): Демо-сайт	TAGS: []
    tasks:
      Каталог сайта	TAGS: []
      Страница из шаблона	TAGS: [page]
      Отметка времени	TAGS: []

На другую машину по SSH

Инвентарь node2.ini:

ini
[web]
node2
text
student@node1:~/demo$ ansible -i node2.ini web -m ping
node2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

Ansible входит по SSH под тем же именем, что и ты: от student — как student, из-под sudo — как root. У student на node1 есть ключ к node2 (~/.ssh/id_ed25519), а sudo там без пароля, поэтому become: true в плейбуке работает и на node2. Это машины лабы на двух ВМ; у машины урока node2 нет. Другое имя — ansible_user=<имя> в строке машины или -u <имя>.

Часть плейбука

bash
ansible-playbook -i inventory.ini site.yml -l node2     # одна машина
ansible-playbook -i inventory.ini site.yml -t page      # по тегу
ansible-playbook -i inventory.ini site.yml \
  --start-at-task 'Страница из шаблона'                 # с задачи
ansible-playbook -i inventory.ini site.yml --step       # спрашивать

С -t page выполнились сбор фактов, задача с тегом и её обработчик — ok=3 changed=2.

Переменная из командной строки

-e site_title=Новый перекрывает vars: плейбука: у переменных из -e наибольший приоритет. Несколько — -e 'a=1 b=2' или файл -e @vars.yml.

Неполадки

9

Could not match supplied host pattern

text
[WARNING]: Unable to parse /home/student/demo/nothere.ini as an
inventory source
[WARNING]: No inventory was parsed, only implicit localhost is
available
[WARNING]: Could not match supplied host pattern, ignoring: local
...
skipping: no hosts matched

Инвентарь не прочитан: файла нет, путь неверный или -i забыт. Код возврата при этом 0 — проверь путь. В инвентаре нет группы из hosts: — то же предупреждение с её именем.

leaves us with no hosts to target

text
[WARNING]: Could not match supplied host pattern, ignoring: web
ERROR! Specified inventory, host pattern and/or --limit leaves us
with no hosts to target.

-l называет машину или группу, которой нет в инвентаре. Сверь с --list-hosts.

UNREACHABLE!

Ansible не подключился по SSH: неверное имя или адрес, нет ключа, не тот пользователь. Текст ошибки SSH — в поле msg. Проверь вручную тем же пользователем: ssh node2 true; подробности — -vvv. Ключи — ssh-keys.

couldn't resolve module/action

text
ERROR! couldn't resolve module/action 'ansible.builtin.servce'.
This often indicates a misspelling, missing collection, or incorrect
module path.

Опечатка в имени модуля или коллекции нет. Плейбук не начинается — код 4. Ниже в сообщении — файл, строка и место ошибки. Документация модуля — ansible-doc ansible.builtin.service.

The requested handler … was not found

text
ERROR! The requested handler 'перечитать nginx' was not found in either
the main handlers list nor in the listening handlers list

notify называет обработчик, которого нет: имя должно совпасть с name обработчика буква в букву, с учётом регистра. Здесь в notify написано перечитать nginx, а обработчик называется Перечитать nginx.

AnsibleUndefinedVariable

text
fatal: [localhost]: FAILED! => {"changed": false, "msg":
"AnsibleUndefinedVariable: 'site_name' is undefined. ..."}

В шаблоне или задаче переменная, которой нет: опечатка или переменная не задана в vars:, инвентаре или -e. Значение по умолчанию — {{ site_name | default('Demo') }}.

Syntax Error while loading YAML

text
Syntax Error while loading YAML.
  did not find expected key

The error appears to be in '/home/student/demo/bad-yaml.yml':
line 7, column 8, but may be elsewhere in the file ...

Отступ не совпал: ключ задачи сдвинут на пробел. YAML строг к отступам, табуляция в них запрещена. Ниже в сообщении — строка с ^ под местом ошибки; виновата часто строка над ней.

Permission denied без become

text
fatal: [localhost]: FAILED! => {"changed": false, "msg": "There was
an issue creating /srv/demo2 as requested: [Errno 13] Permission
denied: b'/srv/demo2'", "path": "/srv/demo2"}

Задача меняет то, что принадлежит root, а плей идёт от пользователя. become: true в плее или задаче — выполнять через sudo; sudo.

Каждый прогон снова changed

shell или command без creates:/changed_when:, или шаблон с переменной, которая меняется каждый раз (дата, случайное число). Обработчик, который зовёт такая задача, тоже срабатывает каждый раз.

Опасно

2

Прогон без -l по большому инвентарю

Плейбук идёт на все машины из hosts:. Перед первым прогоном на рабочих серверах — --list-hosts, --check --diff и -l на одну машину.

--check не видит shell

shell и command в режиме проверки пропускаются: --check покажет меньше изменений, чем даст настоящий прогон, а задачи, которые зависят от их результата, могут упасть только в проверке.

Термины

3
плейбук (playbook)
YAML-файл с плеями (play) Ansible: на каких машинах (hosts) и какие задачи выполнить по порядку. Запускается командой ansible-playbook. Ещё встречается в: Ansible: инвентарь, модули и разовые команды, ssh-keyscan
обработчик (handler)
Задача плейбука, которая выполняется, только если её позвала через notify другая задача и та что-то изменила (changed). Идёт один раз в конце плея — например, перечитать конфиг nginx. Ещё встречается в: Ansible: инвентарь, модули и разовые команды, Docker
шаблон Jinja2 Jinja2 template
Файл .j2, в котором {{ имя }} заменяется значением переменной. Модуль template собирает из него готовый файл на машине и пишет changed, только если результат отличается.

См. также

  • ansible — инвентарь, модули, разовые команды.
  • ssh-keys — ключ для входа на машины инвентаря.
  • ssh-client — проверить подключение вручную.
  • sudo — что делает become: true.
  • systemctl — службы, которые перезапускают обработчики.
  • diff-patch — как читать вывод --diff.
Загрузка…