Глава 7. Компоновка повторно используемого содержания и ролей Ansible

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

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

  • Задачи, обработчики, переменные и плейбуки, включая основные понятия

  • Роли (структуры, значения по умолчанию, зависимости)

  • Проектирование верхнего уровня плейбуков для применения ролей (тегов и прочих недостающих в ролях вещей)

  • Совместное применение ролей в проектах (зависимости через репозитории подобные Galaxy, Git)

Технические требования

Ознакомьтесь с видеоматериалами Code in Action.

Концепции вложения задач, обработчика, переменной и плейбука

Самым первым шагом в понимании того как действенно организовывать некую структуру проекта Ansible является овладение понятием вложения файлов. Такое действие вложения файлов позволяет содержимому определяться в особом для некоторой темы файле, который может быть включённым (вложенным) в другие файлы один раз или более в рамках какого- то проекта. Такая функциональность включения поддерживает имеющуюся концепцию Не повторять себя самого (DRY, Don't Repeat Yourself).

Вложения задач

Файлы задач являются файлами YAML, которые определяют одну или более задач. Эти задачи не связаны напрямую в какие бы то ни было воспроизведение или плейбук; они присутствуют просто как некий список задач. На эти файлы могут ссылаться плейбуки или другие файлы задач посредством имеющегося оператора include. Данный оператор принимает некий путь к какому- то файлу задач и, как мы усвоили в Главе 1, Архитектура системы и проектирование Ansible, такой путь может быть относительным для того файла, который применяет эту ссылку.

Для демонстрации того, как применять такой оператор include чтобы включать задачи, давайте создадим некое простое воспроизведение, которое включает некий файл задач с какими то задачами отладки внутри него. Вначале мы напишем свой файл плейбука, и мы озаглавим его includer.yaml:


---
- name: task inclusion
  hosts: localhost
  gather_facts: false

  tasks:
    - name: non-included task
      debug:
        msg: "I am not included"

    - include: more-tasks.yaml
 	   

Затем мы создадим в некотором каталоге, который содержит includer.yaml файл more-tasks.yaml:


---
- name: included task 1
  debug:
    msg: "I am the first included task"

  - name: included task 2
    debug:
      msg: "I am the second included task"
 	   

Теперь мы исполним свой плейбук чтобы посмотреть на его вывод:

 

Рисунок 7.1



Мы можем отчётливо видеть свои задачи из исполнения данного файла include. Поскольку этот оператор includeбыл применён внутри данного раздела воспроизведения задач, эти включённые задачи были исполнены внутри данного воспроизведения. На самом деле если мы должны были добавить некую задачу в своё воспроизведение после данного оператора include, мы бы увидели, что имеющийся порядок исполнения следует так, как если бы эти задачи из данного включённого файла имелись в том месте, где был применён сам оператор include:


tasks:
  - name: non-included task
    debug:
      msg: "I am not included"

    - include: more-tasks.yaml

    - name: after-included tasks
      debug:
        msg: "I run last"
 	   

Если мы исполним свой видоизменённый плейбук, мы увидим тот порядок задач, который мы ожидаем:

 

Рисунок 7.2



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

Передача значений переменных во вложенные задачи

Порой мы желаем разделить некий набор задач, но иметь эти задачи действующими слегка отличным образом в зависимости от переменных данных. Наш оператор include позволяет нам определять и переписывать переменные данные во время их включения. Такая сфера данного определения имеется только внутри определяемых файлов включения задач (и всех прочих файлов, которые могут включаться в них).

Чтобы проиллюстрировать такую возможность, давайте создадим некий новый сценарий при котором нам нужно выполнить touch {Прим. пер.: изменить время последнего изменения или создать файл} для пары файлов, причём каждый из них в своём собственном пути каталога. Вместо написания двух задач файлов для каждого файла (один для создания такого каталога и другой для контакта с самим файлом), мы создадим некий файл задачи с обеими задачами который будет применять имена переменных в теле задач. Затем мы включим этот файл задачи дважды, причём каждый раз передадим в него разные данные. Вначале мы проделаем это с имеющимся файлом задач files.yaml:


---
- name: create leading path
  file:
    path: "{{ path }}"
    state: directory

- name: touch the file
  file:
    path: "{{ path + '/' + file }}"
    state: touch
 	   

Далее мы изменим свой плейбук includer.yaml чтобы включить тот файл задач, который мы только что создали, совместно с этим передав переменные данные для переменных определённого path и file:


---
- name: touch files
  hosts: localhost
  gather_facts: false

  tasks:
    - include: files.yaml         vars:
        path: /tmp/foo
        file: herp

    - include: files.yaml         vars:
        path: /tmp/foo
        file: derp
 	   
[Замечание]Замечание

Предоставляемое при включении файлов определения переменных может быть либо в виде встроенного формата key=value, либо в проиллюстрированном выше формате key:value внутри некоторого хэша vars.

Когда мы выполним этот плейбук, мы увидим выполненными четыре задачи, по две задачи из files.yaml дважды. При этом наш второй набор должен иметь результатом только одно изменение, так как его путь один и тот же в обоих наборах:

 

Рисунок 7.3



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

Передача сложных данных во вложенные задачи

Когда желательна передача сложных данных во включаемые задачи, например, некий список или хэш, может применяться альтернативный синтаксис при включении такого файла. Давайте повторим свой последний вариант, только на этот раз вместо включения своего файла задачи дважды мы включим его один раз и передадим некий хэш необходимых путей и файлов. Для начала мы заново создадим свой файл files.yaml:


---
- name: create leading path
  file:
    path: "{{ item.value.path }}"
    state: directory
  with_dict: "{{ files }}"

- name: touch the file
  file:
    path: "{{ item.value.path + '/' + item.key }}"
    state: touch
  with_dict: "{{ files }}"
 	   

Теперь мы видоизменим свой плейбук includer.yaml чтобы предоставлять необходимый хэш files в некотором отдельном выражении include:


---
- name: touch files
  hosts: localhost
  gather_facts: false

  tasks:
    - include: files.yaml
      vars:
        files:
          herp:
            path: /tmp/foo
          derp:
            path: /tmp/foo
 	   

Если мы исполним эти новый плейбук и файл задач, мы должны видеть аналогичный, но слегка отличающийся вывод, причём его конечный результат состоит в определённом каталоге /tmp/foo, уже имеющемся на своём месте и необходимыми двумя пустыми файлами herp и derp созданных командой touch:

 

Рисунок 7.4



Такое применение передачи в виде некоторого хэша данных позволяет для вашего растущего набора сущностей создание выражений в вашем главном плейбуке без роста общего числа выражений include.

Условные вложения задач

Аналогично передаче данных во включаемые файлы, в них также могут передаваться и условные зависимости. Это осуществляется присоединением некоторого выражения when в ваш оператор включения. Такое условие не должно вызывать Ansible выполнять конкретную проверку для определения того следует или нет включать данный файл; место этого оно указывает Ansible добавить такую условную зависимость в каждую без исключения задачу внутри данного включаемого файла (а также во все прочие файлы, которые может включать обсуждаемый файл).

[Замечание]Замечание

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

Давайте продемонстрируем это изменив свой первый пример, который включает простые выражения отладки. Мы добавим некоторую условную зависимость и передадим её совместно с некоторыми данными для применения такой зависимости. Для начала давайте изменим свой плейбук includer.yaml:


---
- name: task inclusion
  hosts: localhost
  gather_facts: false

  tasks:
    - include: more-tasks.yaml
      when: item | bool       vars:
        a_list:
          - true
          - false
 	   

Затем модифицируем more-tasks.yaml для цикла по имеющейся переменной a_list внутри каждой задачи:


---
- name: included task 1
  debug:
    msg: "I am the first included task"
  with_items: "{{ a_list }}"

- name: include task 2
  debug:
    msg: "I am the second included task"
  with_items: "{{ a_list }}"
 	   

Теперь давайте выполним этот плейбук и рассмотрим свой новый вывод:

 

Рисунок 7.5



Мы можем видеть некую пропускаемую итераций в каждой из задач, а именно, ту итерацию, в которой данный элемент вычисляет Булево значение false. Важно помнить, что все хосты будут находить значение всех включённых задач. Не имеется никакого способа повлиять на то, чтобы Ansible не включил некий файл для какого- то подмножества хостов. Самое большое, для каждой из задач внутри некоторой иерархии включения может быть применена некая условная зависимость с тем, чтобы эти включённые задачи могли бы быть пропущены. Единственный метод для включения задач на основе фактов хостов состоит в применении подключаемого метода действия group_by для создания динамических групп на основании фактов хостов. Затем вы можете предоставлять таким группам их собственные воспроизведения для включения особых задач. Данное упражнение оставляем самому читателю.

Помеченные вложенные задачи

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

[Замечание]Замечание

Теги могут быть определены в определённом выражении include или в самом воспроизведении для охвата всех включений (и прочих задач) в некотором заданном воспроизведении.

Давайте создадим некую простую демонстрацию чтобы проиллюстрировать как могут применяться теги. Мы начнём с изменения плейбука includer.yaml, который включает некий файл задач дважды, причём всякий раз с неким отличным именем тега и другими переменными данными:


---
- name: task inclusion
  hosts: localhost
  gather_facts: false

  tasks:
    - include: more-tasks.yaml
	  vars:
        data: first
      tags: first

    - include: more-tasks.yaml
	  vars:
        data: second
      tags: second
 	   

Теперь мы обновим more-tasks.yaml для выполнения чего- нибудь с предоставляемыми данными:


---
- name: included task
  debug:
    msg: "My data is {{ data }}"
 	   

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

 

Рисунок 7.6



Теперь, если мы выберем какой тег исполнять, допустим, второй через видоизменение наших аргументов ansible-playbook, мы должны увидеть что только будет исполнена только это вхождение нашей включённой задачи:

 

Рисунок 7.7



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

Вложения задач с циклами

Включения задач могут объединяться также с циклами. При добавлении некоторого цикла with_ в какое- то включение задач, все задачи внутри этого файла будут исполнены с той переменной item, которая придерживает место для текущего значения данного цикла. Весь включаемый файл целиком будет повторно исполняться пока такой цикл не обойдёт все элементы. Давайте обновим свой пример воспроизведения чтобы продемонстрировать это:


- name: task inclusion
  hosts: localhost
  gather_facts: false

  tasks:
    - include: more-tasks.yaml
      with_items:
        - one
        - two
 	   

Нам также нужно обновить свой файл more-tasks.yaml чтобы применить свой item цикла:


---
- name: included task 1
  debug:
    msg: "I am the first included task with {{ item }}"
- name: included task 2
  debug:
    msg: "I am the second included task with {{ item }}"
 	   

После выполнения мы можем сказать, что задачи 1 и 2 исполнены по одному разу для каждого item в данном цикле:

 

Рисунок 7.8



Цикличность при включении является неким мощным понятием, однако оно привносит одну сложность. Что если внутри данного включаемого файла имелись задачи, которые имеют свои собственные циклы? Будет иметься некое противоречие определяемой переменной item, приводящее к неожиданным результатам. По этой причине в версии Ansible 2.1 было добавлено свойство loop_control. Помимо прочих вещей, эта функциональность предоставляет некий метод именования той переменной, которая применяется в конкретном цикле вместо определённой по умолчанию переменной item. Применяя такую функциональность, мы можем различать те item, которые поступают извне данного включения от всех item, используемых внутри данного включения. Чтобы продемонстрировать это мы добавим некое управление loop_var в своё внешнее включение:


- include: more-tasks.yaml
  with_items:
    - one
    - two
  loop_control:
    loop_var: include_item
 	   

Внутри more-tasks.yaml мы будем иметь некую задачу со своим собственным циклом, применяющим имеющийся include_item и свою локальную переменную item:


---
- name: included task 1
  debug:
    msg: "I combine {{ item }} and {{ include_item }}"
  with_items:
    - a
    - b
 	   

После исполнения мы видим, что задача 1 исполнилась дважды для каждого включённого цикла и что применены две обе имеющиеся переменный цикла:

 

Рисунок 7.9



Кроме того также имеется другое управление циклом, такое как label, которое определит что показывается на экране в выводе данной задачи для данного значения item (что полезно для предотвращения приведения в беспорядок вашего экрана большими структурами данных) и pause, предоставляя возможность pause (приостановки) на некоторое определённое число секунд между каждым циклом.

Вложения обработчиков

Обработчики в сущности являются задачами. Они являются неким набором потенциальных задач, включаемых посредством уведомлений от прочих задач. Раз они являются таковыми, задачи обработчика могут включаться в точности как это можно делать с обычными задачами. Оператор include допустим внутри определённого блока handlers.

В отличии от включений задач переменные данные не могут передаваться при включении задач обработчика. Однако, имеется возможность подключать некую условную зависимость в какому- то включению обработчика чтобы применять такое условие для всех обработчиков внутри данного файла.

Давайте создадим некий пример для демонстрации. Вначале мы создадим некий плейбук, имеющий какую- то задачу, которая всегда будет изменяться и которая включает некий файл задач обработчика и присоединяет отдельную условную зависимость к этому включению:


---
- name: touch files
  hosts: localhost
  gather_facts: false

tasks:
  - name: a task
    debug:
      msg: "I am a changing task"
    changed_when: true
    notify: a handler

handlers:
  - include: handlers.yaml
    when: foo | default('true') | bool
 	   
[Замечание]Замечание

При оценивании некоторой переменной, которая может быть определена за пределами какого- то плейбука, лучше применять имеющийся фильтр bool чтобы гарантировать что строки будут преобразованы надлежащим образом в свои Булевы значения.

Затем мы создадим handlers.yaml для определения своих задач обработчика:


---
- name: a handler
  debug:
    msg: "handling a thing"
 	   

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

 

Рисунок 7.10



Теперь давайте выполним этот плейбук вновь; на это раз мы определим foo как некую extra-var и установим её в false для своих аргументов исполнения ansible-playbook:

 

Рисунок 7.11



На этот раз, поскольку foo оценивается как false, наш включаемый обработчик пропускается.

Вложения переменных

Переменные также могут разделяться на загружаемые файлы. Это позволяет совместно применять переменные во множестве воспроизведений или плейбуков, либо включать переменные данные, которые располагаются за пределами каталога данного проекта (например, секретные данные). Файлы переменных являются простыми файлами в формате YAML, предоставляющими ключи и значения. В отличии от файлов включения задач, включения переменных не могут включать и далее дополнительные файлы.

Переменные могут включаться тремя различными способами; через vars_files, через include_vars или посредством --extra-vars (-e).

vars_files

Ключ vars_files является директивой воспроизведения. Он определяет некий перечень файлов для считывания с целью загрузки переменных данных. Эти файлы считываются и синтаксически разбираются в тот момент, когда выполняется синтаксический анализ самого рассматриваемого плейбука. В точности как и для включаемых задач и обработчиков, имеющийся путь является относительным в рассматриваемому файлу для искомого файла ссылки.

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


---
- name: vars
  hosts: localhost
  gather_facts: false

  vars_files:
    - variables.yaml

  tasks:
    - name: a task
      debug:
        msg: "I am a {{ name }}"
 	   

Теперь нам нужно создать variables.yaml в том же самом каталоге что и наш плейбук:


name: derp
 	   

Исполнение нашего плейбука отобразит, что значение этой именованной переменной надлежащим образом получено из исходного кода в нашем файле variables.yaml:

 

Рисунок 7.12



Динамические вложения vars_files

При определённых обстоятельствах может быть желательным параметризировать имеющиеся файлы переменных, подлежащие загрузке. Это возможно сделать благодаря применению переменной в качестве части самого названия файла; однако эта переменная должна иметь некоторое значение, определённое на момент синтаксического анализа рассматриваемого плейбука, в точности как и при применении переменных в названиях задач. Давайте обновим свой пример воспроизведения для загрузки некоего переменного файла на основании тех данных, которые предоставляются на момент исполнения:


---
- name: vars
  hosts: localhost
  gather_facts: false

  vars_files:
    - "{{ varfile }}"

  tasks:
    - name: a task
      debug:
        msg: "I am a {{ name }}"
 	   

Теперь, когда мы выполним этот плейбук, мы предоставим определённое значение для varfile с помощью аргумента -e:

 

Рисунок 7.13



Дополнительно к необходимости определения самого значения переменной на момент исполнения, сам подлежащий загрузке файл также должен иметься во время исполнения. Даже если некая ссылка на какой- то файл является четвёртым воспроизведением вниз по отдельному плейбуку и такой файл сам по себе создаётся в имеющемся первом воспроизведении, если такой файл не существует на момент исполнения, ansible-playbook выдаст сообщение об ошибке.

include_vars

Нашим вторым методом для включения переменных данных из файлов является имеющийся модуль include_vars. Этот модуль загрузит переменные в виде некого действия задачи и будет выполнен на каждом хосте. В отличии от большинства модулей, этот модуль исполняется локально на самом хосте Ansible; вследствие этого все пути всё ещё являются относительными к самому файлу исполняемого воспроизведения. Так как все загрузки переменных выполняются как некая задача, оценка переменных в данном имени файла происходит при исполнении такой задачи. Переменные данные в этом file name могут быть особенными для хоста и определяться к некоторой предыдущей задаче. Кроме того, этот файл сам по себе не должен существовать на момент исполнения; он может быть также создан какой- то предыдущей задачей. Это очень мощная и гибкая концепция, которая может влечь за собой очень динамичные плейбуки при надлежащем применении.

Прежде чем мы двинемся далее, давайте продемонстрируем некое простое применение include_vars изменив наше имеющееся воспроизведение чтобы загрузить определённый файл переменных в качестве задачи:


---
- name: vars
  hosts: localhost
  gather_facts: false

  tasks:
    - name: load variables
      include_vars: "{{ varfile }}"

    - name: a task
      debug:
        msg: "I am a {{ varname }}"
 	   

Исполнение этого плейбука оста1тся тем же самым и наш вывод только слегка отличается от предыдущих итераций:

 

Рисунок 7.14



В точности как и для остальных задач, для загрузки более одного файла в отдельной задаче могут применяться цикличность. Это в особенности действенно при использовании специального цикла with_first_found для итераций по некоторому списку всё более общих имён файлов пока не будет загружен искомый файл. Давайте продемонстрируем это изменив своё воспроизведение для применения собранных фактов хоста чтобы предпринять попытку и загрузить некий файл переменных, специфичный для определённого дистрибутива, особенного для конкретного семейства дистрибутива или, в конце концов, некоторого файла по умолчанию:


---
- name: vars
  hosts: localhost
  gather_facts: true

  tasks:
    - name: load variables
      include_vars: "{{ item }}"
      with_first_found:
        - "{{ ansible_distribution }}.yaml"
        - "{{ ansible_os_family }}.yaml"
        - variables.yaml

  - name: a task
    debug:
      msg: "I am a {{ varname }}"
 	   

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

 

Рисунок 7.15



Из данного вывода мы также можем видеть какой файл был определён для загрузки. В данном случае был загружен variables.yaml, поскольку два прочих файла не существуют. Такая практика общеупотребима для загрузки переменных, которые являются особенными для операционной системы данного хоста в запросе. Переменные для различных операционных систем могут быть записаны в соответствующим образом именованных файлах. Применяя определённую переменную ansible-distribution, которая заполняется полученными фактическими данными, файлы переменных, которые используют значения ansible-distribution как часть своего имени могут загружаться при помощи аргумента with_first_found. В некотором файле может быть предоставлен какой- то набор переменных по умолчанию, который не применяет никаких переменных данных в качестве защиты от неисправностей.

extra-vars

Самый последний метод загрузки переменных данных из некоторого файла состоит в ссылке на некий путь файла при помощи аргумента --extra-vars (или -e) для ansible-playbook. Обычно этот аргумент ожидается в виде некоторого набора данных key=value; однако, если представляется некий путь файла и он имеет префикс с символом @, Ansible считает весь файл целиком для загрузки переменных данных. Давайте видоизменим один из своих более ранних примеров в котором мы применяли -e и вместо определения некой переменной прямо в командной строке мы включим определённый файл переменных который уже написан нами:


---
- name: vars
  hosts: localhost
  gather_facts: false

  tasks:
    - name: a task
      debug:
        msg: "I am a {{ varname }}"
 	   

Когда мы предоставим некий путь после имеющегося символа @, такой путь является относительным для нашего текущего рабочего каталога, причём вне зависимости от того где располагается сам этот плейбук. Давайте исполним наш плейбук и предоставим некий путь к variables.yaml:

 

Рисунок 7.16



Здесь мы вновь можем наблюдать, что наш файл variables.yaml был успешно включён, только, как вы это можете видеть из предыдущего кода, он даже вовсе не упоминается в самом плейбуке - мы имеем возможность загружать его целиком при помощи значения флага -e.

[Замечание]Замечание

При включении некоторого файла переменных с определённым аргументом --extra-vars, этот файл должен существовать на момент исполнения ansible-playbook.

Вложения переменных чрезвычайно мощны в Ansible - однако что относительно самих плейбуков? В этом случае вещи слегка отличаются и по мере развития данной главы мы будем рассматривать как действенно повторно вторично применять задачи и код пдейбуков, тем самым поощряя в Ansible достойную практику.

Вложения плейбуков

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

Вплоть до Ansible 2.4 вложение плебука достигалось применением ключевого слова include - однако это является устаревшим и будет удалено в Ansible 2.8, а потому его не следует применять. Вместо этого вам теперь надлежит применять import_playbook. Это директива уровня воспроизведения (play) - она не может применяться как некая задача. Тем не менее, её очень просто применять. Давайте зададим некий простой образец чтобы продемонстрировать это. Прежде всего давайте создадим некий плейбук, который будет вложен и назовём его includeme.yaml:


---
- name: include playbook
  hosts: localhost
  gather_facts: false

  tasks:
  - name: an included playbook task
    debug:
      msg: "I am in the included playbook"
 	   

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

 

Рисунок 7.17



Тем не менее, вы также можете импортировать его в другом плейбуке. Изменим свой первоначальный плейбук includeme.yaml чтобы он выглядел так:


---
- name: include playbook
  hosts: localhost
  gather_facts: false

  tasks:
  - name: a task
    debug:
      msg: "I am in the main playbook"

- name: include a playbook
  import_playbook: includeme.yaml
 	   

Когда мы его исполним, мы сможем обнаружить отображёнными оба сообщения, а сам импортированный плейбук запускается после задачи инициализации, что является именно той последовательностью, которую мы задали в своём первоначальном плейбуке:

 

Рисунок 7.18



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

Роли

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

Структура ролей

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

Все роли располагаются в некотором подкаталоге архива какого- то плейбука, в своём каталоге roles/. Конечно, это настраивается посредством имеющегося общего ключа настройки roles_path, однако давайте останемся с этим определением по умолчанию. Каждая роль сама по себе некое дерево каталога. Именем роли является само название каталога внутри roles/. Каждая роль имеет некое число подкаталогов со специальным значением, которое работает когда некая роль применяется к какому- то набору хостов.

Некая роль может содержать все эти элементы или всего лишь один из них. Опущенные элементы просто игнорируются. Некоторые роли существуют просто чтобы предоставить общие обработчики по некоторому проекту. Прочие роли существуют как некая отдельная точка зависимости, которая в свою очередь зависит от ряда прочих ролей.

Задачи

Сам файл задачи является основной плотью некоторой роли. Если существует roles/<role_name>/tasks/main.yaml, все имеющиеся там задачи и все прочие файлы, которые они включают будут встроены в данное воспроизведение и исполнятся.

Обработчики

Аналогично задачам, обработчики автоматически загружаются из roles/<role_name>/handlers/main.yaml, если этот файл существует. На эти обработчики могут ссылаться любые задачи внутри данной роли, или любые задачи внутри любой другой роли, которая перечисляется этой ролью в качестве зависимости.

Переменные

Существует два вида переменных, которые могут быть определены в некоторой роли. Имеются переменные роли, загружаемые из roles/<role_name>/vars/main.yaml, а также существуют значения по умолчанию роли, которые загружаются из roles/<role_name>/defaults/main.yaml. Основная разница между vars и defaults имеет отношение к порядку приоритета. Дл подробного описания имеющегося порядка отсылаем вас к Главе 1, Архитектура системы и проектирование Ansible. Определения по умолчанию роли являются самым нижним порядком переменных. Буквально, любое прочее определение некоторой переменной будет более предпочтительно в сравнении с с некоторым значением по умолчанию роли. Значения по умолчанию роли можно представлять как держатель места для реальных данных, некоторой справкой какие переменные могут заинтересовать какого- то разработчика при определении особых для площадки значений. Переменные роли, с другой стороны, имеют некий высший порядок приоритета. Переменные роли могут переназначаться, однако обычно они применяются когда на один и тот же набор данных ссылаются более одного раза внутри какой- то роли. Если этот набор данных должен быть переопределён некими значениями локальной площадки, тогда такая переменная должна быть перечислена в имеющихся значениях по умолчанию этой роли вместо того чтобы быть представленной в переменных этой роли.

Простые и встраиваемые модули

Некая роль может включать персональные модули также как и встраиваемые модули. Хотя сам проект Ansible достаточно хорош в отношении просмотра и принятия представленных модулей, имеются определённые случаи при которых может быть нежелательным или даже незаконным представление некоторого персонального модуля вверх проекта. В таких случаях предоставление определённого модуля с конкретной ролью может быть наилучшим вариантом. Модули могут загружаться из roles/<role_name>/library/ и могут применяться любой задачей в данной роли или любой последующей роли. Представленные в таком пути модули перепишут все прочие копии того же самого названия модуля в любом прочем месте в данной файловой системе, что может быть неким способом распространять дополнительную функциональность в некий модуль ядра прежде чем эта функциональность будет принята в восходящем потоке проекта и выпущена с некоторой новой версией Ansible.

Аналогично, встраиваемые модули часто применяются для настройки поведения Ansible неким способом, который имеет смысл для какой- то особенной среды и не подходит для сотоварищей в восходящем потоке проекта. Встраиваемые модули могут распространяться как часть некоторой роли, что может быть проще чем установка в явном виде встраиваемых модулей на каждом хосте, который будет действовать как некий управляющий хост Ansible.

Вcтраиваемые модули автоматически загружаются если они обнаружены внутри какой- то роли в одном из следующих подкаталогов:

  • action_plugins

  • lookup_plugins

  • callback_plugins

  • connection_plugins

  • filter_plugins

  • strategy_plugins

  • cache_plugins

  • test_plugins

  • shell_plugins

Зависимости

Роли могут выражать некую зависимость от другой роли. Общей практикой является установка всех ролей зависящими от некоторой общей роли, такой как задачи, обработчики, модули и тому подобноею Эти роли могут зависеть только от того, что должно быть определено только один раз. Когда Ansible обрабатывает некую роль для некоего набора хостов, он вначале просматривает все зависимости, которые определены в roles/<role_name>/meta/main.yaml. Если какие- либо из них определены, эта роль будет обрабатываться и все задачи внутри будут исполнены (после проверки также и всех перечисленных внутри зависимостей) пока, прежде чем запустятся задачи данной начальной роли, не будут завершены все зависимости. Более глубоко мы опишем зависимости роли позже в данной главе.

Файлы и шаблоны

Модули задачи и обработчиков могут иметь относительные ссылки внутри roles/<role_name>/files/. Такое имя файла может предоставляться без какого бы то нибыло префикса и будет иметь источником roles/<role_name>/files/<file_name>. Относительные префиксы также допускаются при доступе к файлам внутри подкаталогов roles/<role_name>/files/. Такие модули как template, copy и script могут также получать преимущества этого.

Аналогично, применяемые модулем template шаблоны могут иметь ссылки относительно roles/<role_name>/templates/. Данный код примера использует некий относительный путь для загрузки своего шаблона derp.j2 из полного пути roles/<role_name>/templates/herp/derp.j2:


- name: configure herp
  template:
    src: herp/derp.j2
    dest: /etc/herp/derp.j2
 	   

Собираем всё вместе

Для иллюстрации того как может выглядеть полная структура роли, вот некоторый пример с названием demo:


roles/demo
├── defaults
│   └── main.yaml
├── files
│   └── foo
├── handlers
│   └── main.yaml
├── library
│   └── samplemod.py
├── meta
│   └── main.yaml
├── tasks
│   └── main.yaml
├── templates
│   └── bar.j2
└── vars
    └── main.yaml
 	   

При создании некоторой роли требуются не все каталоги или файлы. Обрабатываться будут только те файлы, которые присутствуют. Таким образом, наш пример вашей роли не требует или не применяет обработчики; вся часть handlers из этого дерева может быть целиком опущена.

Зависимости ролей

Как уже устанавливалось, роли могут зависеть от других ролей. Эти взаимосвязи имеют название dependencies и они описываются в файле meta/main.yaml некоторой роли. Этот файл ожидает какого- то хэша данных верхнего уровня с неким ключом dependencies; все данные внутри являются неким списком ролей:


---
dependencies:
  - role: common
  - role: apache
 	   

В данном примере Ansible полностью обработает вначале роль common (и все зависимости, которые она может выражать), прежде чем продолжит со второй ролью apache и затем в конце концов запустит задачи своей роли.

Зависимости могут иметь ссылки по имени без какого бы то ни было префикса если присутствуют внутри той же самой структуры каталога или располагаются внутри имеющихся настроенными roles_path. В противном случае для указания местоположения роли могут применяться полные пути:


role: /opt/ansible/site-roles/apache
 	   

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

Переменные зависимости ролей

Передаваемые совместно с перечнем зависимостей переменные будут переписывать значения для соответствующих переменных, определяемых в defaults/main.yaml или vars/main.yaml. Это может быть полезным для использования некоей общей роли подобной роли apache в качестве зависимости при предоставлении особенных данных, таких как какой порт открывать в имеющемся межсетевом экране или какой модуль apache включать. Переменные выражаются как дополнительные ключи для данного перечисления роли. Давайте обновим свой пример чтобы добавить простые и сложные переменные в наши две зависимости:


---
dependencies:
  - role: common
    simple_var_a: True
    simple_var_b: False
  - role: apache
    complex_var:
      key1: value1
      key2: value2
    short_list:
      - 8080
      - 8081
 	   

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

Тэги

Теги могут применяться для всех имеющихся задач, обнаруженных внутри некоторой зависимости роли. Эти функции во многом аналогичны тому способу, при котором теги могут применяться для включения файлов задач, как это описывалось ранее в данной главе. Их синтаксис простой; ключ tags может быть неким отдельным элементом или каким- то списком. Для демонстрации давайте добавим некие теги в свой пример списка зависимости:


---
dependencies:
  - role: common
    simple_var_a: True
    simple_var_b: False
    tags: common_demo
  - role: apache
    complex_var:
      key1: value1
      key2: value2
    short_list:
      - 8080
      - 8081
    tags:
      - apache_demo
      - 8080
      - 8181
 	   

Как и в случае с добавлением тегов во включаемые файлы задач, все те задачи, которые обнаружены внутри некоторой зависимости (и любой зависимости внутри этой иерархии) получат предоставляемые марекры (теги).

Условные зависимости ролей

Хотя и невозможно предотвратить текущую обработку некоторой зависимости роли с помощью некоторого условия, имеется возможность пропуска всех имеющихся задач внутри какой- то иерархии зависимости роли применив некое условие к какой- то зависимости. Это также отражает всю функциональность включения задач с условиями. Для выражения этого условия применяется ключ when. И вновь мы увеличим свой пример добавив некую зависимость для демонстрации имеющегося синтаксиса:


---
dependencies:
  - role: common
    simple_var_a: True
    simple_var_b: False
    tags: common_demo
  - role: apache
    complex_var:
      key1: value1
      key2: value2
    short_list:
      - 8080
      - 8081
    tags:
      - apache_demo
      - 8080
      - 8181
    when: backend_server == 'apache'
 	   

Приложения ролей

Роли не являются воспроизведениями. У них нет какого- либо представления о том на каких хостах задачи этой роли должны исполняться, какие методы соединения применять, работать или нет последовательно или любое иное поведение воспроизедения, описанное в Главе 1, Архитектура системы и проектирование Ansible. Роли должны применяться внутри некоторого воспроизведения внутри какого- то плейбука, в котором могут быть выражены все эти заключения.

Чтобы применить некую роль внутри какого- то воспроизведения применяется оператор when. Этот оператор ожидает некий список ролей для применения к имеющимся хостам в этом воспроизведении. Во многом как и с описанием зависимостей роли, при определении применяемых ролей, вместе с ними могут передаваться данные, такие как переменные, теги и условия. Его синтаксис в точности такой же.

Для демонстрации применения ролей внутри некоторого воспроизведения давайте создадим какую- то простую роль и применим её в простом плейбуке. Вначале давайте построим такую род с названием simple, которая будет иметь некую отдельную задачу debug в roles/simple/tasks/main.yaml, печатающую то значение некоторой переменной по умолчанию роли, которая определена в roles/simple/defaults/main.yaml. Во- первых давайте поработаем с самим файлом задачи:


---
- name: print a variable
  debug:
    var: derp
 	   

Далее мы напишем свой файл по умолчанию в какой- то единственной переменной, derp:


---
derp: herp
 	   

Для исполнения такой роли мы запишем некий плейбук с единственным воспроизведением для применения этой роли. Мы вызовем свой плейбук roleplay.yaml, причём он расположен в том же самом уровне каталога, что и сам каталог roles/:


---
- hosts: localhost
  gather_facts: false

  roles:
    - role: simple
 	   
[Замечание]Замечание

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

Мы повторно применим свой список ресурсов mastery-hosts из предыдущих глав и выполним свой плейбук:

 

Рисунок 7.19



Благодаря магии ролей имеющееся значение переменной derp было автоматически загружено из значений роли по умолчанию. Конечно, мы можем переопределить такое значение по умолчанию при применении данной роли. Давайте изменим свой плейбук и применим некое новое значение для derp:


---
- hosts: localhost
  gather_facts: false

  roles:
    - role: simple
      derp: newval
 	   

На этот раз, при нашем исполнении мы обнаружим в качестве значения для derp newval:

 

Рисунок 7.20



Внутри некоторого отдельного воспроизведения может применяться множество ролей. Ключ roles: ожидает некий перечень значений. Для применения большего числа ролей просто добавьте дополнительные:


---
- hosts: localhost
  gather_facts: false

  roles:
    - role: simple
      derp: newval
    - role: second_role
      othervar: value
    - role: third_role
    - role: another_role
 	   

Этот плейбук загрузит в общей сложности четыре роли - simple, second_role, third_role и another_role - причём каждая из них будет выполняться в той последовательности, в которой они перечислены.

Смешанные роли и задачи

Воспроизведения, которые применяют роли не ограничиваются только ролями. Тактие воспроизведения могут иметь свои собственные tasks, а также иные блоки задач: pre_tasks и post_tasks. Тот порядок, в котором они исполняются, не зависит от того в каокм порядке перечисляются эти разделы в самом таком воспроизведении; вместо этого существует строгий порядок исполнения блоков внутри некого воспроизведения (play). Для ознакомления с порядком операций в конкретном плейбуке обратитесь к Главе 1, Архитектура системы и проектирование Ansible.

Обработчики для некоторого воспроизведения мелькают во множестве мест. Если имеется некий блок pre_tasks, обработчики вспыхивают после исполнения всех pre_tasks. Затем исполняются блоки roles и tasks (первыми роли, затем задачи в зависимости от того порядка, в котором они записаны в данном плейбуке), после чего вновь вспыхнут обработчики. Наконец, если имеется блок post_tasks, обработчики мелькнут снова после того как исполнится post_tasks. Конечно, обработчики могут мигнут в любой момент времени с вызовом meta: flush_handlers. Давайте расширим свой roleplay.yaml чтобы продемонстрировать все различные разы, когда могут включаться обработчики:


---
- hosts: localhost
  gather_facts: false

  pre_tasks:
    - name: pretask
      debug:
        msg="a pre task"
      changed_when: true
      notify: say hi

roles:
    - role: simple
      derp: newval

tasks:
    - name: task
      debug:
        msg: "a task"
      changed_when: true
      notify: say hi

post_tasks:
    - name: posttask
      debug:
        msg: "a post task"
      changed_when: true
      notify: say hi

handlers:
- name: say hi
  debug:
    msg="hi
 	   

Мы также заменим свой пример задачи роли чтобы также отметить обработчик say hi:


---
- name: print a variable
  debug:
    var: derp
  changed_when: true
  notify: say hi
 	   
[Замечание]Замечание

Это работает только потому, что наш обработчик say hi был определён в том воспроизведении, который вызывается имеющейся ролью simple. Если такой обработчик не определён, возникнет некая ошибка. Будет лучше только уведомлять обработчики, которые имеются внутри той же самой роли или любой роли, отмеченной как некая зависимость.

Исполнение нашего плейбука должно иметь результатом вызов имеющегося обработчика say hi суммарно три раза: один раз для pre_tasks, один раз для roles и tasks, а также один раз для post_tasks:

 

Рисунок 7.21



Хотя тот порядок, в котором pre_tasks, roles, tasks и post_tasks записаны в воспроизведении не влияет на тот порядок, в котором эти разделы исполняются, лучше записывать их именно в том порядке, в котором они выполняются. Именно этот визуальный сигнал чтобы помочь запомнить данный порядок и избежать путаницы при последующем прочтении данного плейбука.

Вложения ролей и импорты

Для Ansible версии 2.2 в качестве некоторого предварительного технического предложения сделан доступным новый подключаемый модуль действия, include_role. Затем в Ansible версии 2.4 эта концепция получила дальнейшее развитие добавлением подключаемого модуля import_role.

Этот подключаемые модули применяется в некоторой задаче для включения и исполнения какой- то роли целиком напрямую из задачи. Основное отличие между ними двумя является едва уловимой, но существенной - подключаемый модуль include_role рассматривается как динамический, что означает что сам код обрабатывается в процессе времени исполнения, когда встречается ссылка на данную задачу. Подключаемый модуль import_role, в свою очередь, рассматривается как статический, что означает, что весь импорт и предварительная обработка подвергаются предварительному синтаксическому разбору на момент данного плейбука. Это имеет различное воздействие на их применение в плейбуках - например, import_role не может использоваться в циклах, в то время как include_role может.

[Замечание]Замечание

Все подробности и компромиссы между импортировванием и вложением можно обнаружить в официальной документации Ansible.

Поскольку на момент написания книги в данный момент оба этих подключаемых модуля рассматриваются как некое предварительное техническое представление, нет никакой гарантии того, что они присутствуют в их нынешнем виде в последующих выпусках Ansible. Следовательно, следует избегать применения этих функций, если только они не требуются вам для конкретной цели.

Совместное использование ролей

Одним из преимуществ применения ролей является имеющаяся возможность совместно применять такие роли по воспроизведениям, плейбукам, целым пространствам проекта и даже по всем организациям. Роли разрабатываются чтобы они были самостоятельными (или ясно отражать зависимость ролей), поэтому они могут существовать вне некоторого пространства проекта в котором располагается тот плейбук, который применяет данную роль. Роли могут быть установленными в совместно применяемых путях в некотором хосте Ansible или они могут распространяться через управление исходными кодами.

Ansible Galaxy

Ansible Galaxy является общеупотребимым хабом для поиска и совместного применения ролей Ansible. Этот вебсайт может посетить любой чтобы пройтись по имеющимся ролям и обзорам; плюс пользователи, которые создают некую регистрацию могут предоставлять обзоры тех ролей, которые они проверили. Роли из Galaxy могут выгружаться при помощи утилиты ansible-galaxy, предоставляемой в Ansible.

Эта утилита ansible-galaxy может соединяться с вебсайтом Ansible Galaxy и устанавливать роли из него. Данная утилита по умолчанию устанавливает роли в /etc/ansible/roles. Если настроен roles_path или если предоставляется с помощью --roles-path (или -p) некий путь времени исполнения, все роли будут установлены там вместо этого. Если какая- то роль была установлена в roles_path или предоставленный путь, ansible-galaxy может перечислить её и также показывать информацию о ней. Для демонстрации применения ansible-galaxy давайте воспользуемся им чтобы установить некую роль чтобы управлять known_hosts для ssh из Ansible Galaxy, требующего username.rolename, поскольку множество пользователей могут выгружать роли с одним и тем же именем. В данном случае нам нужна роль docker_ubuntu от определённого пользователя angstwad:

 

Рисунок 7.22



Теперь мы можем воспользоваться этой ролью по ссылке angstwad.docker_ubuntu в некотором воспроизведении или другом блоке зависимостей ролей. Мы также можем перечислить её и получить информацию о ней воспользовавшись своей утилитой ansible-galaxy:

 

Рисунок 7.23



Наш вывод ограничен сверху 25 строками во избежание отображения всего содержимого README.md. некоторые из данных, подлежащих отображению этой командой информации располагаются в самой роли, в файле meta/main.yaml. Изначально мы увидели только информацию о зависимостях в данном файле и возможно не было смысла называть данный каталог meta, однако теперь мы видим что в этом файле также располагаются прочие метаданные:

 

Рисунок 7.24



Данная утилита ansible-galaxy также может помочь с созданием новых ролей. имеющийся метод init создаст некий скелет дерева каталога для такой роли помимо того, что заполнит имеющийся файл meta/main.yaml заполнителем места для связанных с Galaxy данных. Этот метод init получает некое разнообразие параметров, что отражено в его справочной информации:

 

Рисунок 7.25



Давайте продемонстрируем данную возможность создав некую новую роль в своём рабочем каталоге с названием autogen:

 

Рисунок 7.26



Для ролей, которые не пригодны для Ansible Galaxy, например, те роли, которые связаны с внутренними системами, ansible-galaxy может выполнять установку напрямую с некоторого URL git. Вместо простого предоставления названия роли для установки таким методом, вместо этого может быть предоставлен некий полный URL git с не обяательным параметром версии. Например, если мы желаем установить свою роль foowhiz с нашего сервера git, мы можем просто сделать следующее:

 

Рисунок 7.27



Без указания информации о версии , будет применена ветвь master. Без данных названия, такое имя будет определено из самого URL. Чтобы предоставить некую версию, добавьте в конец запятую и строку с номером версии, которую поймёт этот git, например, как некий тег или название ветви, например, v1:

 

Рисунок 7.28



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

 

Рисунок 7.29



Роли также могут устанавливаться напрямую из tarball, а также путём предоставления URL к такому tarball в месте полного URL Git или имени некоторой роли для доставки с Ansible Galaxy.

Когда нам нужно устанавливать много ролей для какого- то проекта, имеется возможность определять множество ролей для выгрузки и установки в некотором файле с форматом YAML, который заканчивается .yaml (или .yml). Имеющийся формат данного файла позволяет вам определять множество ролей из большого числа источников и сохраняет вашу возможность определять версии и названия роли. Кроме того, может перечисляться метод управления источником кода (в настоящее время поддерживаются только git и hg):


---
- src: <name or url
  version: <optional version>
  name: <optional name override>
  scm: <optional defined source control mechanism>
 	   

Чтобы установить все роли из некоторого файла воспользуйтесь параметром --roles-file (-r) со своим методом установки:

 

Рисунок 7.30



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

Выводы

Ansible предоставляет определённую возможность делить содержимое логически на раздельные файлы. Эта возможность помогает разработчикам проекта не повторять один и тот же код снова и снова. Роли внутри Ansible проводят эту возможность на шаг далее и обёртывают некоторой магией сами пути имеющегося содержимого. Роли имеют возможность настройки, повторного использования, переносимости и совместного использования блоков функциональности. Ansible Galaxy существует как некий хаб сообщества для того чтобы разработчики могли найти, отранжировать и совместно применять роли. Инструмент командной строки ansible-galaxy предоставляет некий метод взаимодействия с Ansible Galaxy или прочими механизмами совместных ресурсов. Такие возможности и инструменты помогают с организацией и использованием общего кода.

В данной главе мы изучили понятия вложения, относящиеся к задачам, обработчикам, переменным и даже плейбукам в целом. Затем расширили эти познания изучением ролей - их структуры, установки значений переменных по умолчанию и обработке зависимостей ролей. После этого вы продолжили изучение проектирования плейбуков для действенного применения ролей, а также применения таких вариантов как тэги, которых в противном случае у вас бы не было. Наконец, вы ознакомились с совместным применением ролей между проектами с применением таких репозиториев как Git и Ansible Galaxy.

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