Базированый Ansible

от автора

Введение

Настраивать оборудование можно разными способами. Можно ходить от одной железки к другой и делать всё руками, но это медленно и непродуктивно, сгодится максимум для дома. Можно использовать скрипты, но это может не всегда работать и вызывать проблемы. А можно использовать подход, который сейчас называют Infrastructure as Code, то есть описать в каком состоянии Вы хотите видеть свою инфраструктуру, а программа сделает всё сама. Вот про одну из таких программ, которая активно завоёвывает рынок, мы и поговорим, а именно об Ansible.

Часть 1. Что это такое

Ansible описывают как инструмент управления конфигурациями. В этом он практически идентичен таким утилитам как Puppet, Chef и Salt. Но чем выделяется Ansible на фоне конкурентов?

  1. Простота синтаксиса — всё в Ansible можно написать, используя YAML файлы;

  2. Простота аудита — вытекает из первого пункта;

  3. Отсутсвие демонов — на удалённых хостах нужны только SSH-доступ и Python версии 3.8+;

  4. Легко делиться — созданные роли, плагины и бибилотеки можно добавить в коллекцию и выложить в общий доступ;

  5. Простые абстракции — модули позволяют выполнять действия по одному шаблону для разных хостов, вместо использования ряда команд в консоли;

  6. Принудительная настройка — после внесения изменений можно выполнить запуск сценария для немедленного применения, а не ждать пока агент с хоста придёт за новыми настройками;

  7. Отсутствие управляющего узла — не обязательно иметь доступ до сервера, откуда катятся изменения, можно распространять их с любой машины, которая имеет доступ до целевых хостов;

  8. Многоуровневая оркестрация — управляемое оборудование можно делить по типам, применению и свойствам и к каждой группе применять свои настройки независимо от других;

  9. Самодокументирующийся код — правильно оформленные плейбуки и сценарии не нуждаются в дополнительной документации, а говорят сами за себя;

  10. Воспроизводимость — если настроить всю систему с помощью Ansible, то можно автоматически настроить любую аналогичную в любых количествах;

  11. Встроенное шифрование — с помощью ansible-vault можно шифровать секреты, хнарить их в Git и передавать напрямую на хосты;

  12. Идемподентность — сценарии можно применять сколько угодно раз без ущерба для целевого хоста.

Часть 2. Составные части

Сценарии

Основой Ansible являются сценарии. Сценарием или плейбуком называется файл, который описывает порядок управления конфигурациями. Рассмотрим пример сценария установки Angie (форк Nginx):

angie_install.yml - name: Different need soft install  hosts: webservers  become: true  tasks:    - name: Install Angie      ansible.builtin.apt:        name: angie        state: present    - name: Change angie.conf      ansible.builtin.copy:        src: files/angie.conf        dest: /etc/angie/angie.conf        mode: '0644'        owner: root        group: root

Здесь мы можем увидеть сразу несколько важных моментов, а именно:

  • name — названия тасков (Рекоммендуется писать с заглавной буквы);

  • hosts — к каким узлам применять данные таски

  • become — нужно ли повышение привилегий (пользователь указывается в конфигурационном файле в параметре bacome_user);

  • ansible.builtin.apt и ansible.builtin.copy — названия модулей;

  • name, state, src, dest, mode, owner, group — параметры модулей. Первый модуль принимает имя нужного пакета и его состояние. Если Angie не установлен, то установит его, а если он уже есть на хосте, то просто пропустит данный таск, написав OK в лог.

Второй модуль скопирует конфигурационный файл с хоста, откуда запущен и поместит его по пути указанному в dest с указанными правами.

Запускается сценарий так:

ansible-playbook ansible_install.yml

YAML

Ещё раз напомню, что всё в Ansible пишется в YAML. Конечно, можно писать и в JSON, так как YAML интерпиритруется в него, но это сложно и для написания, и для дальнейшего прочтения.

Есть несколько правил, которые рекомендуется применять при оформлении YAML:

  • В начале файла указываются ---;

  • В конце файл указываются ... и пусатя строка;

  • Комментарии начинаются с #

  • Отсутпы состоят из двух пробелов;

  • Строки не заключаются в кавычки в YAML, но Ansible требует это делать. В двойные кавычки принято заключать имена переменных в выражениях интерполяции, а в одинарные литеральные значения, где подстановка переменных не требуется. Логические значения никогда не заключаются в кавычки;

  • Булевы выражения можно писать как true, True, TRUE, yes, Yes, YES, on, On, ON, false, False, FALSE, no, No, NO, off, Off, OFF. Но так как YAML интерпритируется в JSON, а в нём есть только false и true, то удобнее использовать только их. Плюс это позволит избежать предупреждений при проверке кода через yamllint;

  • Списки (или последовательности) оформляются с помощью отступов и дефиса:

need_support_soft:  - xrdp  - tree  - mc  - wget  - gpg  - python3  - ansible  - git  - apt-transport-https  - ca-certificates  - curl  - apt-utils  - gnupg2
  • Словари (или отображения) выглядят так:

address:  street: Lenina  city: Moscow

(также есть поддержка строенных словарей address: { street: Lenina, city: Moscow});

  • Многострочные значения поддерживаются через распознавание оператторных скобок (| и >), символы начала многострочного текста (+ и -) и отступы (от 1 до 9)

---visiting_address: |+  Department of Computer Science    A.V. Williams Building  University of Maryland

Форматирование сохраняется.

Задачи

Как видно из примера сценария, он состоит из более мелких частей, а именно задач. В нашем случае это, например:

- name: Install Angie  ansible.builtin.apt:    name: angie    state: present

Задача делает ровно одно действие — вызывает модуль apt и передаёт ему параметры angie и present.

Модули

Модуль — это сценарий, который поставляется вместе с Angie. Модули могут быть как встроенные, так и подключаемые дополнительно. Документацию по модулям можно посмотреть с помощью утилиты ansible-doc. Например, документацию по модулю apt можно посмотреть с помощью ansible-doc apt или ansible-doc -l | grep apt.

Переменные

Могут задаваться прямо в сценарии с помощью vars, например:

vars:  key_file: nginx.key  conf_file: /etc/nginx/sites-available/default

Далее значение из vars можно подставить в задачу:

- name: Nginx config install    ansible.builtin.copy:      src: files/nxinx.conf      dest: "{{ conf_file }}"

Но удобнее определять их в отдельных файлах. Для этого все переменные выносятся в отдельный файл, а в сценарии просто укахывается его имя:

vars_files:  nginx.yml

Но можно сделать ещё проще и разместить все переменные относящиеся к роли в файле defaults/main.yml.

Если при отладке роли нужно посмотреть какая переменная используется по факту, то можно использовать модуль debug:

- debug: var=varname

Также значения переменнных можно объединять в двойных фигурных скобках с помощью ~:

debug:  msg: "The URL is {{ server_name ~'.'~ domain.name }}"

Если возникает необходимость опрелелить переменную во время работы сценария, то сделать это можно с помощью регистрации:

- name: Whoami found  command: whoami  register: login  - debug: var=login- debug: msg="You are {{ login.stdout }}"

В данном случае - debug: var=login понадобился, чтобы понять в каком именно ключе хранятся нужные нам данные.

И ещё один момент. Если по какой либо причине Ansible не сможет выполнить задачу, то он свалится в ошибку и прервёт дальнейшее выполнение сценария. Чтобы этого избежать на рисковых задачах безопаснее будет указать на пропуск ошибок:

- name: Whoami found  command: whoami  register: login  ignore_errors: true  # или если нужно только в режиме проверки  ignore_errors: {{ ansible_check_mode }}  # ansible_check_mode вернёт true, если включен режим проверки через опцию --check

Переменные могут переопределяться. Вот как выглядит приоритет в порядке возрастания:

  1. В роли(в defaults/main.yml);

  2. В реестре или сценарии для группы хостов;

  3. Секция group_vars/all в реестре;

  4. Секция group_vars/all в сценарии;

  5. Секция group_vars/* в реестре;

  6. Секция group_vars/* в сценарии;

  7. В реестре или сценарии для хоста;

  8. Секция host_vars/* в реестре;

  9. Секция host_vars/* в сценарии;

  10. Кэшированные факты из `set_facts;

  11. Переменные операции;

  12. Секция vars_prompt в операции;

  13. Секция vars_files в операции;

  14. Переменные роли(в `vars/main.yml);

  15. Переменные блока задач;

  16. Переменные задач;

  17. Секция include_vars;

  18. Факты возвращаемые из set_facts;

  19. Параметры ролей;

  20. Подключаемые параметры;

  21. Переданные из командной строки. То есть переменная объявленная для конкретного хоста всегда будет иметь приоритет над переменной для группы, а переменная из include_vars перекроет переменную для хоста. Наивысший уровень работает лишь тогда, когда Вы запускаете Ansible из командной строки с параметрами -e var=value. Также можно передать целый файл с переменными -e @filename.yml.

Встроенные переменные

Ansible такде имеет список втроенных переменных, которые можно использовать в своих задачах:

  • hostvars — словарь, где ключи — имена хостов Ansible, а значения словари, содержащие данные об этих хостах;

  • inventory_hostname — имя текущего хоста, как оно задано в Ansible;

  • inventory_hostname_short — имя текущего хоста, без имени домена;

  • group_name — список групп куда входит хост;

  • groups — словарь, где ключи — имена групп в Ansible, а значения — список имён хостов, входящих в группы;

  • ansible_check_mjde — логическая переменная, принимающая true, при включении режима проверки;

  • ansible_play_batch — логическая переменная, принимающая true, при выполнении сценария в тестовом режиме;

  • ansible_olay_hosts — список хостов из реестра, участвующих в текущей операции;

  • ansible_version — словарь с информацией об Ansible.

Факты

При выполнении сценария, до начала запуска первой задачи Ansible подключается к хосту и запрашивает всю информацию: аппаратную архитектуру, название ОС, IP-адреса, объём памяти и прочее. Доступ ко всем этим данным можно получить через переменную ansible_facts и обращаться к ним можно, используя префикс ansible_. Также можно устанавливать факты во рвемя выполнения задача, по сути, создавать новые переменные:

- name: Set nginx state  when: ansible_facts.service.nginx.state is defined  set_fact:    nginx_state: "{{ ansible_facts.service.nginx.state }}"

Шаблоны

Шаблон — это простой текстовый файл, где использованием специального синтаксиса определяются переменные, которые должны заменяться файктическим значением. В случае Ansible эти шаблоны пишутся с использованием механизма jinja2. Эти файлы обычно аналогичны тем, что должны получиться на целевой машине, хранятся в формате .j2 и на месте значений имеют указатели на подстановку переменных в формате {{ }}. Пример строки с использованием шаблона:

deb https://download.angie.software/angie/ubuntu/{{ ubuntu_version }} {{ ubuntu_codename }} main

Циклы

Если необходимо запустить задачу для каждого элемента из списка, то можно использовать цикл loop. Задача будет выполнена несколько раз, заменяя элемент item значениями из списка.

- name: Copy TLS  ansible.builtin.copy:    src: "{{ item }}"    dest: "{{ tls_dir }}"  loop:    - "{{ key_file }}"    - "{{ cert_file }}"  notify: Restart nginx

Обработчики

В прошлом примере присутствует строка notify: Restart nginx, это вызов обработчика. Они конфигурируются в блоке handlers

handlers:  - name: Restart nginx    service:      name: nginx      state: restarted

Обработчики — это одна из форм условного выполнения. Обработчик похож на задачу, но выполняется только после получения уведомления (notify). Обработчики выполняются после завершения всех задач и только один раз, даже если получено несколько уведомлений. Чтобы была возможность запустить обработчик раньше, нужно заменить notify на meta. Если сценарий содержит несколько обработчиков, то они выполняются в порядке следования в разделе handlers, а не в порядке вызова.

Реестр

Реестр в простейшем виде — это список имён хостов, перечисленных через запятую. Это может быть тесктовый файл, причём в разных форматах, каталог или выполняемый файл. В данном реестре (или inventory) хранится список хостов, которыми нужно управдять. Я предпочитаю держать данный файл в виде YAML-файла:

inventory/hosts.ymlall:  children:      prod:      children:        webservers:          hosts:            test01:              ansible_host: 10.10.10.4            test02:              ansible_host: 10.10.10.5

Здесь видно, что присутствуют группы all, prod и webservers. При этом prod и webservers являются дочерними от all, а webservers является дочерней от prod. То есть если указать для сценария вышестоящий уровень, то он будет применён и к нижестоящим, но не наоборот. Помимо ansible_host можно указать следующее:

  • ansible_port — SSH порт. По умолчанию 22;

  • ansible_user — SSH пользователь. По умолчанию $USER;

  • ansible_password — пароль пользователя. По умолчанию его нет;

  • ansible_connection — как будет происходить подключение. По умолчанию smart. Этот механизм проверяет поддержку локальным SSH-клиентом функции ControlPersist и если её нет, то использует библиотеку Paramiko;

  • ansible_ssh_private_key_file — закрытый ключ. По умолчанию нет;

  • ansible_shell_type — командная оболочка для выполнения команд. По умолчанию sh. Для Linux можно указать любую, которая есть на хосте. Для Windows — powershell;

  • ansible_python_interpreter — путь к интерпретатору Python на хосте. По умолчанию /usr/bin/python; Чтобы не прописывать переопределённое значение для каждого хоста, можно изменить их глобально через ansible.cfg. В этом файле аналогии следующие:

ansible_port = remote_portansible_host: 10.10.10.5ansible_user = remote_useransible_ssh_private_key_file = ssh_private_key_fileansible_shell_type = executable (но здесь необходимо указать полный путь /usr/local/bin/fish)

Также совсем необязательно указывать ansible_host: 10.10.10.5, если настроено разрешение имён. Ansible может резолвить имена из реестра, конфигурационного файла SSH, файла /etc/hosts и DNS. Запись имён тоже можно сильно упростить. Если имеется 50 серверов с именами webserver01, webserver02 и так, далее, то можно записать это просто webserver[01:50]. Перебор букв также поддерживается — webserver-[a-z].

Часть 3. Собираем всё вместе

Роли

Роли — это основной механизм деления сценария на отдельные файлы.

Роль помогает собрать в одном месте несколько задач или других ролей для выполнения определённого действия без привязки к определённому хосту и без сохранения каких-либо данных. Базовая структрура роли может содержать в себе:

defaults/main.yml - переменные по умолчаниюfiles/main.yml - файлы для загрузки на хостыhandlers/main.yml - обработчикиmeta/main.yml - информация о ролиtasks/main.yml - точка входаtemplates/angie.conf.j2 - шаблоны для загрузки на хостыvars/main.yml - переменные, которые переопределять нежелательно

Но при это ни один из этих файлов, кроме tasks/main.yml, не является обязательным и может отсутствовать. Ansible ищет роли в подкаталоге roles, но системные роли можно разместить по пути /etc/ansible/roles. Переопределить данный путь можно с помощью ansible.cfg

[defaults]roles_path = /usr/bin/roles

Есть задачи, например обновление кэша apt, которые можно поместить прямо в роль, но можно вынести их в отдельную секцию, которая всегда выполняется до ролей, а некоторые задачи в секцию, которая будет выполняться после успешного применения ролей:

pre_tasks:  - name: Update apt cache    apt:      update_cache: true      roles:  - role...    post_tasks:  - name: Notify to Telegram    ...

Каталог для роли можно сгенерировать автоматически. Для этого можно использовать немного не по назначению ansible-galaxy.

ansible-galaxy init --init-path playbooks/roles ansible_install

Ansible Galaxy

Galaxy — это хранилище ролей с открытым исходным кодом, пополняемое членами сообщества. Роли хранятся на GitHub, устанавливаются через ansible-galaxy, а подробнее почитать об этом можно на сайте. Но если кратко, то роль можно установить или удалить вот так:

ansible-galaxy install ...ansible-galaxy revove ...

Список установленных ролей можно посмотреть с помощью list.

Роли сохраняются в директорию, которая указана первой в roles_path в galaxy_roles/. Путь можно изменить при установке с помощью ключа -p.

Если ваша роль требует какой-либо зависимости, то будет правильно указать её в файле roles/requirements.yml и требуемая роль будет автоматически подключена или, при необходимости, скачана из Galaxy.

Часть 4. Что может пригодиться в реальной эксплуатации

Фильтры

Фильтры пришли в Ansible из Jinja2, чтобы упростить подстановку нужных значений, а также избежать некоторых ошибок

default

Если в задаче нам нужно указать хост, отличный от стандартного, то можно использовать фильтр default:

host: "{{ database_host | default('localhost') }}"

В этом случае, если переменная database_host не определена, то будет подставлен localhost.

Фильтры зарегистрированных переменных

Если нам нужно запустить задачу и вывести её результат, если она выполнилась с определённым результатом и завершить сценарий:

- name: Test task  command: /opt/muscript  register: result  ignore_errors: true  - debug: var=result  - debug:    msg: "Stop tasks"    failed_when: result|failed

Фильтры для статусов могут быть следующие:

failed - задача завершилась неудачейchanged - задача внесла измененияsuccess - задача завершилась успешноskipped - задача пропущена

Фильтры для путей к файлам

Если у нас задана переменная, в которой есть длинный путь к файлу, то его можно записать один раз

vars:  homepage: /usr/share/angie/html/index.html

а потом использовать фильтр basename, который позволяет получить index.html

- name: Copy home page  copy:    src: "files/{{ homepage | basename }}"    dest: "{{ homepage }}"

Подключение задач и ролей

Бывает так, что одинаковые задачи могут использоваться в разных местах. Поэтому чтобы не плодить одинаковый код можно описать такие задачи в одной и потом их использовать через подключение. Например установку и включение nginx можно добавить в файл nginx_install.yml

- name: Nginx install  package:    name: nginx    - name: Nginx start  service:    name: nginx    state: started    enabled: true

и при необходимости добавить его

- include_tasks: nginx_install.yml

Также есть возможность использовать динамическое подключение. Например, использовать задачу в зависимости от типа ОС:

- name: Platform specific actions  include_tasks: "{{ ansible_os_family }}.yml"

Подключение ролей работает по такому же принципу:

- name: Install php  include_role    name: php

Но также есть и возможность указать с какого таска начинать выполнение:

- name: Install php  include_role    name: php    tasks_from: install

Блоки

При выполнении задач есть возможность группировать их по блокам, это позволяет определять условия выполнения и аргументы для всех задач сразу:

- block:  - name: Nginx install    ...      - name: Nginx start    ...when: ansible_os_family == 'RedHat'

Также блоки помогают обрабатывать ошибки, возникающие при выполнении задач:

- block:    ...    - debug: msg="You never see this message"  rescue:    - debug: msg="You see this message in case of failure in the block"  always:    - debug: msg="This will be always executed"

Тут логика работы такая, в rescue будут задачи, которые необходимо выполнить, если на каком-то из хостов произойдёт ошибка, а в блоке always будут задачи, которые выполняются всегда.

Шифрование секретов

Очень часто возникает необходимость хранить чувствительную информацию прямо в Ansible, например пароли, ключи или сертификаты в переменных. Ansible даёт такую возможность, шифруя всё, что ему скажут встроенным механизмом через ansible_vault. Чтобы зашифровать целый файл, нужно ввести

ansible-vault encrypt srt.key

и после этого дважды указать пароль для шифрования. Этот пароль нужно сохранить и использовать при запуске Ansible

ansible-playbook --ask-vault-pass cert_update.yml

Также можно шифровать отдельные переменные:

ansible-vault encrypt_string 'oun394ossa' --name="telegram_cannel_id" 

и полученный результат занести в файл.

Использование разных паролей

Начиная с версии 2.4, Ansible позволяет использование разных паролей с помощью идентификатора шифрования. Для этого в файле ansible.cfg, в разделе [defaults], создаются ссылки на идентификаторы паролей и соответствующие им файлы:

[defaults]vault_identity_list = dev@~/.vault_dev, prod@~/.vault_prod

Файлы же при этом шифруются так

ansible-vault encrypt --encrypt-vault-id=prod group_vars/prod/vault

Выбор хостов для применения

На какие группы хостов применять сценарий можно выбрать в самом сценарии. Выше был показан пример — hosts: all. Но есть способы выбрать несколько групп, для этого применяются шаблоны:

all или * - все хостыdev:stage - объединение группdev:&stage - выбор только общих хостов для двух группdev:!stage - только уникальные хосты из первой группы*.test.com - шаблонtest[1:5].com - шаблон с диапазоном~web\d\/test\.(com|ru) - регулярное выражение

Также выбор хостов для запуска можно определить и через ansible-playbook с использованием ключа -l или --limit.

Выполнение задач на машине отличной от целевой

Может возникнуть ситуация, что во время выполнения задачи необходимо выполнить определённый действия на машине откуда запускается Ansible или вообще на какой-то сторонней. Для этого можно воспользоваться выражением delegate_to Как пример. Нам необходимо скачать файл, но на удалённом хосте нет доступа в интернет. Поэтому мы можем скачать его на свой управляющий ПК и потом передать куда нужно.

- name: Download test binary  delegate_to: localhost  connection: local  become: false  get_url:    url: "https://test.com"    dest: "~/Downloads/test"    mode: '0755'  ignore_errors: true

Также подобный трюк можно использовать, например, для активации стороннего мониторинга, после установки служб на целевом хосте.

Управление количеством одновременного запуска

По умолчанию Ansible выполняет каждую задачу на всех хостах пралаллельно, но если нужно ограничить число хостов на которых запускается сценарий, то это можно сделать выражением serial, например:

- name: Upgrade package  hosts: web  serial: 1

В этом случае будет обрабатываться один хост за раз.

Также может пригодиться выражение max_fail_percentage. В нём будет учитываться количество неудачных выполнений задачи и в связке с serial можно остановить выпонение при заданном пороге ошибок.

Если мы точно не знаем сколько у нас хостов, то можно сделать пакетную обработку, указав serial: 50%. Значения можно комбинировать и сначала выполнить установку на одном хосте, потом на половине, а потом и на всех:

serial:  - 1  - 50%  - 100%

Если задачу нужно выполнить разово, то поможет выражение run_once: true.

Флаг --step при запуске ansible-playbook заставляет Ansible запрашивать подверждение на запуск каждой задачи.

Теги

Каждую задачу можно пометить тегом

- name: Download test binary  get_url:    url: "https://test.com"    dest: "~/Downloads/test"    mode: '0755'  tags:    - first

После этого действия появляется возможность либо запускать только таски с нужным тегом --tags first или пропускать их --skip-tags.

Стратегии выполнения

По умолчанию Ansible использует стратегию линейного выполнения (linear), это значит, что задача запускается на всех хостах сразу, ждёт результата и двигается дальше. Но данное поведение можно изменить. Если указать стратегию free, то Ansible не будет ждать окончания опреаций на других хостах.

- name: Strategies  hosts: web  strategy: free

Плагины обратного вызова

Ansible поддерживает так называемые плагины обратного вызова, которые выполняют некоторые действия в ответ на такие события, как запуск опреации или завершение какой-нибудь задачи. Есть три вида плагинов:

  • плагины стандартного вывода;

  • плагины уведомлений;

  • плагины агрегирования.

Плагины стандартного вывода

Единовременно может быть активен один плагин стандартного вывода. Назначается он установкой параметра stdout_callback в секции defaults файла ansible.cfg. Например, можно сделать вывод Ansible в более читаемом формате:

[defaults]stdout_callback = yaml

Список доступных плагинов можно посмотреть командой ansible-doc -t callback -l.

Плагины уведомлений и агрегирования

Данные плагины позволяют выполнять разнообразные действия, от записи времени выполнения, до отправки уведомлений в Jabber. Подобных плагинов может быть активировано несколько одновременно. Активируются они также через ansible.cfg

[defaults]callback_whitelist = mail, slack# или callback_enabled = mail, slack

Многие из этих плагинов имеют дополнительные параметры, посмотреть которые можно через ansible-doc -t callback plugin_name

Режим проверки

Ну и одна из самых важных возможностей — пробный прогон. Когда написана новая роль или изменён какой-либо таск, то можно выполнить сценарий в безопасном режиме, добавив к ansible-playbook ключ --check или -c. В этом режиме Ansible не производит на хосте никаких изменений, а просто показывает что будет сделано и успешно ли. Но есть пара но. Во-первых, модуль должен поддерживать режим проверки. А во-вторых, если задача требует чего-то, что должно быть добавлено на хост одной из прошлых задач, например должен быть скачан файл или установлен пакет, но так как в режиме проверки изменения не вносятся, то следующая задача, где будет использование файла или пакета упадёт с ошибкой. Чтобы этого не произошло, нужно в уязвимых для этого тасках указывать ignore_errors: {{ ansible_check_mode }}.

Вместо заключения

Ansible позволяет работать с собой практически как хочется. Есть мало правил, которые соблюдать строго необходимо, но всё же лучше организовывать работу с ним так, чтобы и самому было понятно что же там наверчено, и другим было несложно разобраться. Также в данной статье не раскрыты многие дополнительные возможности Ansible, такие как подстановка данных из сторонних файлов, создание динамического файла реестра, написание собственных модулей или ускорение работы, но приведённой выше информации будет достаточно для удобной и продуктоивной работы с Ansible в большинстве случаев.

ссылка на оригинал статьи https://habr.com/ru/articles/1033358/