В этой статье мы рассмотрим плейбуки Ansible — схемы для действий по автоматизации. Плейбуки — это простой, целостный и воспроизводимый способ определить все действия, которые мы хотели бы автоматизировать. Что такое плейбук Ansible? Структура плейбука Запуск плейбука Использование переменных в плейбуках Чувствительные данные Запуск задач при изменении с помощью обработчиков Условные задачи Циклы Советы по плейбукам Ansible Плейбуки — это базовые компоненты Ansible, которые записывают и исполняют конфигурацию Ansible. Обычно это основной способ автоматизировать набор задач, которые мы хотели бы выполнять на удалённой машине. Они собирают все ресурсы, которые нужны, чтобы оркестрировать упорядоченные процессы и не выполнять одни и те же действия вручную. Плейбуки можно использовать повторно и распространять. Их можно легко написать в YAML и так же легко прочитать. Плейбук состоит из сценариев (play), которые выполняются в заданном порядке. Сценарий представляет собой список задач для определённой группы хостов. Каждая задача связана с модулем, отвечающим за действие, и параметрами конфигурации. Поскольку большинство задач идемпотентны, плейбук можно спокойно запускать несколько раз. Плейбуки пишутся в YAML со стандартным расширением .yml с минимальным синтаксисом. Мы делаем одинаковые отступы для элементов на одном уровне иерархии, используя пробелы. У дочернего элемента отступ должен быть больше, чем у родительского. Количество пробелов может быть любым, но обычно их два. Tab использовать нельзя. Ниже приводится пример простого плейбука с двумя сценариями, по две задачи в каждом: Для каждого сценария мы придумываем понятное имя, которое будет указывать на его назначение. Затем мы указываем группу хостов, для которых будет выполняться сценарий, из инвентаря. Наконец, все сценарии должны выполняться от имени root, а для become нужно указать yes. Для настройки поведения Ansible мы можем определить и другие ключевые слова на разных уровнях — задача, сценарий, плейбук. Более того, большинство ключевых слов можно задать в среде выполнения в виде флагов командной строки в файле конфигурации Ansible, ansible.cfg, или инвентаре. В правилах приоритета прописано, как Ansible ведёт себя в таких случаях. Затем с помощью параметра tasks мы определяем список задач для каждого сценария. У каждой задачи должно быть понятное имя. Задача выполняет операцию с помощью модуля. Например, первая задача в первом сценарии использует модуль ansible.builtin.copy. Для модуля мы обычно определяем аргументы. Во второй задаче первого сценария используется модуль ansible.builtin.user для управления учётными записями пользователей. В нашем примере мы настраиваем имя пользователя, состояние пользовательского аккаунта и UID. При запуске плейбука Ansible выполняет задачи по порядку, по одной за раз, для всех указанных хостов. Это поведение по умолчанию можно скорректировать по необходимости с помощью стратегий. Если при выполнении задачи происходит сбой, Ansible останавливает выполнение плейбука для этого хоста, но продолжает на других, где задача выполнена успешно. Во время выполнения Ansible отображает информацию о статусе подключения, именах задач, статусе выполнения и наличии изменений. В конце Ansible предоставляет сводку по выполнению плейбука с указанием выполненных и невыполненных задач. Давайте на примере посмотрим, как это работает. Выполним наш пример плейбука командой ansible-playbook. В выходных данных мы видим имена сценариев, задачу Gathering Facts (сбор фактов), другие задачи сценария, а в конце Play Recap — сводку по выполнению сценария. Мы не определили группу хостов databases, поэтому второй сценарий плейбука был пропущен. С помощью флага –limit мы можем ограничить выполнение плейбука несколькими хостами. Например: Переменные замещают значения, чтобы мы могли повторно использовать плейбук и другие объекты Ansible. Имя переменной может содержать только буквы, цифры и символы подчёркивания и должно начинаться с буквы. В Ansible переменные можно определить на нескольких уровнях — см. приоритет переменных. Например, можно задать глобальные переменные для всех хостов, переменные для отдельного хоста или переменные для конкретного сценария. Для переменных на уровне группы и хоста нужны каталоги group_vars и host_vars. Например, чтобы определить переменные для группы databases, создаём файл group_vars/databases. Дефолтные переменные задаём в файле group_vars/all. Чтобы определить переменные для определённого хоста, создаём файл с именем этого хоста в каталоге hosts_vars. Заменить любую переменную в среде выполнения можно с помощью флага -e. Самый простой метод определить переменные — использовать блок vars в начале сценария со стандартным синтаксисом YAML. Также мы можем определить переменные во внешних файлах YAML. Чтобы использовать эти переменные в задачах, мы должны сослаться на них, указав их имя в двойных фигурных скобках, как того требует синтаксис Jinja2: Если значение переменной начинается с фигурных скобок, нужно взять в кавычки всё выражение, чтобы YAML корректно интерпретировал синтаксис. Мы также можем определить переменные с несколькими значениями в виде списков. Мы можем ссылаться на отдельные значения из списка. Например, берём первое значение, foo1: Также переменные можно определить с помощью словарей YAML. Например: Здесь тоже можно взять первое поле: Чтобы ссылаться на вложенные переменные, используем квадратные скобки или точку. Например, нам требуется значение example_name_2: Мы можем создавать переменные с помощью инструкции register, которая получает выходные данные команды или задачи и использует их в других задачах. Иногда плейбукам нужны чувствительные данные (ключи API, пароли и т. д.). Для таких случаев у нас есть Ansible Vault. Хранить такие данные обычным текстом небезопасно, поэтому мы можем зашифровывать и расшифровывать их с помощью команды ansible-vault. Зашифровав секреты и защитив их паролем, мы можем спокойно размещать их в репозитории кода. Ansible Vault защищает данные только при хранении. После расшифровки секретов мы должны обращаться с ними бережно, чтобы случайно не раскрыть. Шифровать можно переменные или файлы. Зашифрованные переменные расшифровываются по требованию только при необходимости, а зашифрованные файлы расшифровываются всегда, потому что Ansible не знает заранее, понадобится ли их содержимое. В любом случае, нужно продумать стратегию управления паролями для Vault. Зашифрованное содержимое мы помечаем тегом !vault, который указывает Ansible, что содержимое нужно расшифровать, а перед многострочным зашифрованным фрагментом мы ставим символ |. Создаем новый зашифрованный файл: Открывается редактор, где можно добавить содержимое, которое должно быть зашифровано. Также можно зашифровать существующие файлы командой encrypt: Просматриваем зашифрованный файл: Чтобы внести изменения в зашифрованный файл, временно расшифровываем его командой  edit: Изменить пароль от зашифрованного файла можно командой rekey с указанием текущего пароля: Если мы хотим расшифровать файл, мы используем команду decrypt: С помощью команды encrypt_string можно зашифровать отдельные строки, которые потом можно будет использовать в переменных и включать в плейбуки или файлы переменных: Например, мы хотим зашифровать строку db_password ‘12345679’ с помощью Ansible Vault: Поскольку мы опустили <password_source>, мы вручную ввели пароль от Vault. Можно было с тем же успехом передать файл пароля: –vault-password-file. Чтобы просмотреть содержимое зашифрованной переменной, которую мы сохранили в файле vars.yml, мы используем тот же пароль с флагом –ask-vault-pass: Чтобы управлять несколькими паролями, можно задать метку с помощью –vault-id. Например, устанавливаем метку dev для файла и запрашиваем пароль: Атрибут no_log: true позволяет запретить вывод на консоль выходных данных задачи, которые могут содержать чувствительные значения: При выполнении задачи на консоль не будет выводиться сообщение: Наконец, давайте выполним плейбук с нашей зашифрованной переменной: Мы убедились, что можем расшифровать значение и использовать его в задачах. Обычно модули Ansible идемпотентны и их можно выполнять много раз, но иногда мы хотим выполнять задачу только при изменении на хосте. Например, мы хотим перезапускать сервис только при изменении его файлов конфигурации. Ansible использует обработчики, которые срабатывают при уведомлении от других задач. Задачи уведомляют обработчики с помощью параметра notify:, только если они действительно что-то меняют. У обработчиков должны быть глобально уникальные имена, и обычно мы пишем обработчики в нижней части плейбука. В примере выше задача Restart apache (перезапустить Apache) будет выполняться, только если в конфигурации что-то изменилось. Обработчики можно считать неактивными задачами, которые ждут уведомления. Нужно понимать, что по умолчанию обработчики выполняются после завершения всех остальных задач. При таком подходе они выполняются только один раз, даже если триггеров несколько. Это поведение можно изменить с помощью задачи meta: flush_handlers, которая будет запускать обработчики, уже получившие уведомления на этот момент. Одна задача может уведомлять несколько разработчиков одной инструкцией notify. Условные конструкции — это ещё один способ контролировать порядок выполнения в Ansible. С их помощью мы можем выполнять или пропускать задачи в зависимости от соблюдения условий. Эти условия могут быть связаны с переменными, фактами или результатами предыдущих задач, а также операторами. Например, мы можем обновлять переменную на основе значения другой переменной, пропускать задачу, если переменная имеет определённое значение, выполнять задачу, только если факт с хоста возвращает значение, превышающее заданный порог. Чтобы применить простую условную инструкцию, мы указываем параметр when в задаче. Если условие удовлетворяется, задача выполняется. В противном случае — пропускается. В этом примере задача выполняется, потому что условие удовлетворено. Кроме того, часто управлять задачами можно на основе атрибутов удалённого хоста, которые можно получить из фактов. Посмотрите список с часто используемыми фактами, чтобы получить представление обо всех фактах, которые можно использовать в условиях. Мы можем сочетать несколько условий с помощью логических операторов и группировать их с использованием скобок: Инструкция when поддерживает использование списка в случаях, когда требуется соблюдение нескольких условий: Также можно использовать условия на основе зарегистрированных переменных, которые мы определили в предыдущих задачах: Ansible позволяет повторять набор элементов в задаче, чтобы выполнять её несколько раз с разными параметрами, при этом не переписывая её. Например, чтобы создать несколько файлов, можно использовать задачу, которая проходит по списку имён каталога, вместо того чтобы писать пять задач с одним модулем. Для итерации по простому списку элементов указываем ключевое слово loop. Мы можем ссылаться на текущее значение с помощью переменной среды item. Выходные данные этой задачи, которая использует loop и item: Также возможна итерация по словарям: Также можно выполнять итерацию по группе хостов в инвентаре: Сочетая условные конструкции с циклами, мы можем выполнять задачу только для некоторых элементов в списке: Наконец, можно использовать ключевое слово until, чтобы выполнять задачу повторно, пока условие не будет удовлетворяться. В приведённом выше примере мы проверяем файл example_log 10 раз с задержкой в 15 секунд между проверками, пока не найдём слово success. Если мы добавим слово success в файл example_log, пока задача выполняется, через некоторое время мы увидим, что задача успешно останавливается. Более сложные варианты использования см. в официальном руководстве Ansible по циклам. Придерживайтесь этих рекомендаций при создании плейбуков, чтобы работать продуктивнее. 1. Чем проще, тем лучше. Не усложняйте задачу. В Ansible есть много вариантов и вложенных структур, и если мы будем бездумно сочетать функции друг с другом, мы получим слишком сложную структуру. Не пожалейте времени на то, чтобы упростить артефакты Ansible. В долгосрочной перспективе это обязательно окупится. 2. Размещайте артефакты Ansible в системе управления версиями. Плейбук рекомендуется хранить в git или другой системе управления версиями. 3. Создавайте понятные имена для задач, сценариев и плейбуков. Придумывайте имена, по которым будет сразу понятно, что делает артефакт. 4. Стремитесь к удобочитаемости. Следите за отступами и добавляйте пустые строки между задачами, чтобы код было проще читать. 5. Всегда явно упоминайте состояние задачи. У многих модулей есть дефолтное состояние, что позволяет нам пропускать параметр состояния, но лучше явно указывать это значение, чтобы избежать недоразумений. 6. Написание комментариев при необходимости. Иногда определения задания будет недостаточно, чтобы объяснить всю ситуацию, так что используйте комментарии для сложных частей в плейбуке. В этой статье мы рассмотрели основной компонент автоматизации в Ansible — плейбуки. Мы узнали, как создавать, структурировать и запускать плейбуки. Мы также рассмотрели использование переменных, защиту чувствительных данных, контроль выполнения задач с помощью обработчиков и условий, а также итерацию по задачам с помощью циклов. Кстати, если вы хотите использовать модель «инфраструктура как код», попробуйте Spacelift. Он поддерживает рабочие процессы Git, политику как код, программируемую конфигурацию, совместное использование контекста и многие другие удобные функции. Сейчас эта платформа работает с Terraform, Pulumi и CloudFormation с поддержкой Ansible. Здесь можно создать аккаунт, чтобы получить бесплатную пробную версию. Вы научитесь конфигурировать рутинные задачи и никакие правки конфигураций вас не остановят. Будете не просто конфигурировать, но и делать это с помощью удобного и простого инструмента. Сможете выполнять сложные задачи, настраивать под свои задачи и смело залезать под капот Ansible. Пойметё, когда и как писать свои модули. Курс состоит не только из теории, но и из опыта спикера, его набитых шишек, кейсов, а также 78 тестовых и 46 практических заданий на стендах в личном кабинете. Коротко о программе: — Узнаете как работать с переменными, как писать плейбуки и роли; — Развернете LEMP стек, PostgreSQL и Mongo кластеры, задеплоите Flask приложение; — Напишите свой модуль для Ansible; — Настроите IaC в Gitlab; — Разберетесь с работой с облаками и enterprise решениями. Посмотреть подробную программу и записаться:  https://slurm.io/ansible