Оформление файлов Ansible#

Единые правила оформления помогают читать и проверять наборы сценариев, роли, описания инвентаря и конфигурацию Ansible. Примеры используют Ansible Core из состава Astra Automation. Правила оформления дополняют требования форматов файлов, но не меняют их синтаксис. Например, Ansible принимает yes как логическое значение YAML, однако в новых примерах рекомендуется использовать true.

Файлы YAML#

Для наборов сценариев, задач роли и файлов переменных используйте расширение .yml, а для шаблонов Jinja – .j2. Начинайте документ YAML с --- и завершайте файл переводом строки. Это правила оформления; многие документы YAML корректны и без начального маркера.

Не рекомендуется

Рекомендуется

Причина

playbook.yaml, config.tpl

playbook.yml, config.j2

Расширение выбирают по назначению файла; само расширение не меняет синтаксис содержимого.

Файл начинается с service_port: 8080.

Первая строка содержит ---, следующая – service_port: 8080.

Начальный маркер явно обозначает документ YAML.

Последняя строка service_port: 8080 не имеет завершающего перевода строки.

После последнего символа строки service_port: 8080 есть перевод строки.

Завершающий перевод строки упрощает обработку файла текстовыми инструментами.

Отступы и списки#

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

Следующий фрагмент YAML использует допустимые, но не рекомендуемые отступы и пробелы:

server:
    port:  8080

Рекомендуемое оформление того же словаря:

server:
  port: 8080

Замена пробелов перед port символом табуляции сделает этот пример синтаксически некорректным. Запрет табуляции относится к отступам, а не к содержимому строковых значений.

Следующий фрагмент файла переменных допустим по синтаксису YAML, но не соответствует выбранному оформлению списка:

packages:
- nginx
- curl

Рекомендуемое оформление того же фрагмента:

packages:
  - nginx
  - curl

Разделяйте соседние задачи пустой строкой. Отступы ключевых слов name, when, register и changed_when должны соответствовать уровню задачи, а не уровню параметров модуля.

Следующий фрагмент списка задач соответствует синтаксису YAML, но Ansible воспримет when и register как параметры модуля ansible.builtin.debug:

- ansible.builtin.debug:
    msg: Проверка начата
    when: true
    register: _message
  name: Задача
- ansible.builtin.debug:
    msg: Проверка завершена
  name: "{{ service_name }}: Сообщение"

Рекомендуемый фрагмент того же списка задач; переменную service_name необходимо определить в сценарии или инвентаре:

- name: Вывод сообщения о начале проверки
  ansible.builtin.debug:
    msg: Проверка начата
  when: true
  register: _message

- name: "Вывод сообщения службы {{ service_name }}"
  ansible.builtin.debug:
    msg: Проверка завершена

Исправленный пример также показывает пустую строку между задачами, содержательное название первым ключом и переменную в конце названия. Порядок ключей и пустая строка относятся к оформлению, а расположение when и register – к структуре задачи Ansible. Ошибка параметров модуля может проявиться только при выполнении задачи, поэтому проверки синтаксиса недостаточно.

Типы значений и кавычки#

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

Не рекомендуется

Рекомендуется

Причина

service_enabled: yes

service_enabled: true

Оба значения логические; true соответствует принятому оформлению.

service_port: 010 для числа десять

service_port: 10

Парсер YAML Ansible воспринимает 010 как восьмеричное число восемь.

answer_text: 'false'

answer_text: "false"

Обе записи задают строку; двойные кавычки рекомендуются для единообразия.

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

account_code: 00123
answer_text: false
file_mode: 0644

Исправленный фрагмент с явными строковыми значениями:

account_code: "00123"
answer_text: "false"
file_mode: "0644"

Заключайте в кавычки также строки с выражением Jinja в начале или с двоеточием перед пробелом:

service_name: nginx
service_enabled: true
service_port: 8080
service_message: "Служба: запущена"
service_config_path: "{{ config_dir }}/nginx.conf"

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

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

windows_path: 'C:\Temp\example.txt'
file_pattern: '^example\.txt$'

Ошибочный фрагмент YAML

Корректный фрагмент YAML

Причина

service_config_path: {{ config_dir }}/nginx.conf

service_config_path: "{{ config_dir }}/nginx.conf"

Без кавычек этот фрагмент не соответствует синтаксису YAML.

service_message: Служба: запущена

service_message: "Служба: запущена"

Двоеточие перед пробелом требует кавычек в таком строковом значении.

windows_path: "C:\Temp\example.txt"

windows_path: 'C:\Temp\example.txt'

В двойных кавычках YAML обрабатывает обратную косую черту как начало управляющей последовательности. Допустимый альтернативный вариант – экранировать каждый разделитель двойной обратной косой чертой.

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

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

message: >
  первая
  вторая

Он задает строку первая вторая с завершающим переводом строки. Для сохранения границы строк используйте следующий вариант:

message: |
  первая
  вторая

В результате между словами и после слова вторая есть переводы строки. Если текст должен занимать одну строку, первый вариант с > соответствует этой цели, а вариант с | – нет. Если завершающий перевод строки не требуется, замените | на |- при том же содержимом. Оба последних варианта сохраняют перевод строки между словами, но только |- удаляет завершающий перевод строки.

Сценарии, задачи и переменные#

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

Для модулей и расширений используйте полностью определенное название контента (FQCN), включая префикс ansible.builtin для встроенных компонентов. Исключение – локальная роль, у которой нет пространства имен коллекции, например роль example в каталоге roles/example/. Параметры модулей оформляйте отдельными парами ключей и значений. Указывайте state явно, если модуль поддерживает этот параметр и он определяет ожидаемое состояние.

Не рекомендуется оформлять задачу следующим образом:

- apt: name=nginx

Рекомендуемый фрагмент для списка tasks сценария с необходимыми привилегиями на целевом узле:

- name: Установка Nginx
  ansible.builtin.apt:
    name: nginx
    state: present

В этих двух примерах итоговое состояние пакета одинаково, поскольку модуль ansible.builtin.apt использует present по умолчанию. Явный параметр state делает ожидаемое состояние видимым при чтении задачи.

Для названий переменных используйте строчные латинские буквы, цифры и символ подчеркивания; название не должно начинаться с цифры. Разделяйте слова символом подчеркивания, например service_port. Переменные роли рекомендуется начинать с ее названия, например example_message. Префикс _ можно использовать для вспомогательных переменных, но он не ограничивает область их видимости.

Следующий фрагмент файла переменных роли example допустим по синтаксису, но не соответствует правилам оформления названий и префикса роли:

ServicePort: 8080
serviceName: nginx
message: Проверка завершена

Рекомендуемое оформление тех же переменных роли:

example_service_port: 8080
example_service_name: nginx
example_message: Проверка завершена

При переименовании переменных необходимо изменить все обращения к ним. Названия 1service_port и service-port нарушают требования Ansible к названиям переменных, хотя YAML допускает такие ключи словаря. Используйте, например, service_port_1 и service_port соответственно. Строчные буквы, разделение слов и префикс роли относятся к оформлению; запрет цифры в начале и дефиса в названии – к требованиям Ansible. Для переменных, которые не принадлежат роли, префикс роли не требуется.

В выражениях Jinja отделяйте содержимое от {{ и }} пробелами, а фильтры – пробелами от |. Для доступа к значениям словаря рекомендуется использовать квадратные скобки, например {{ server['address'] }}. Такая запись устраняет неоднозначность между ключом словаря и атрибутом объекта.

Следующие фрагменты задачи предполагают, что словарь server содержит ключ address, а переменная service_name содержит название службы. Синтаксически корректный фрагмент, который не соответствует рекомендуемому оформлению выражений:

- name: Вывод параметров службы
  ansible.builtin.debug:
    msg: "{{server.address}} {{service_name|upper}}"

Рекомендуемое оформление той же задачи:

- name: Вывод параметров службы
  ansible.builtin.debug:
    msg: "{{ server['address'] }} {{ service_name | upper }}"

Пробелы в этих выражениях не меняют результат вычисления. Доступ через точку может изменить результат, если ключ совпадает с названием атрибута словаря. Например, server.items обозначает метод словаря, а server['items'] – значение ключа items.

Комментарии должны объяснять назначение или причину действия. После # ставьте пробел; комментарии в русскоязычных примерах пишите по-русски без обращения к читателю. Для задач с ansible.builtin.command, ansible.builtin.shell и регулярными выражениями поясняйте причину их использования или существенные ограничения.

Не рекомендуется

Рекомендуется

Причина

#Получите сведения

# Получение сведений об учетной записи

После # необходим пробел; комментарий описывает действие без обращения к читателю.

# Read account data

# Получение сведений об учетной записи

Комментарии в русскоязычных примерах пишут по-русски.

# Запуск id

# Команда только читает сведения и не изменяет систему

Комментарий поясняет причину настройки changed_when, а не повторяет команду.

Предпочитайте профильный модуль запуску команды. Если необходима команда, используйте ansible.builtin.command; к ansible.builtin.shell переходите, когда необходимы возможности оболочки. Для команды чтения данных можно указать changed_when: false. Для команды, изменяющей систему, необходимо определить корректный признак изменения или условие запуска, например creates. Директива changed_when меняет отчет о результате, но сама по себе не предотвращает повторное действие.

Фрагмент задачи для получения идентификаторов текущей учетной записи на целевом узле Linux:

- name: Получение идентификаторов учетной записи
  # Команда только читает сведения и не изменяет систему
  ansible.builtin.command:
    cmd: /usr/bin/id
  changed_when: false
  register: _account_info

Ожидаемый результат: задача сохраняет вывод команды в _account_info['stdout'] и сообщает changed: false. Пароли и токены передавайте через защищенные источники переменных, например Ansible Vault. Для задач, вывод которых может раскрыть секреты, используйте no_log: true и не выводите секретные переменные отдельной задачей debug.

Временные заменители в примерах#

Если пример требует значения из окружения читателя, обозначайте его временным заменителем из строчных латинских букв в угловых скобках. Слова разделяйте символом подчеркивания, например <host_pattern>. После примера объясняйте назначение каждого заменителя. Перед запуском необходимо заменить его целиком, включая угловые скобки. Ansible не подставляет значения вместо таких обозначений автоматически.

Не используйте неоднозначные обозначения в команде запуска:

ansible-playbook -i ИНВЕНТАРЬ СЦЕНАРИЙ

Рекомендуемое оформление той же команды:

ansible-playbook -i <inventory_file> <playbook_file>

Здесь:

  • <inventory_file> – путь к файлу описания инвентаря;

  • <playbook_file> – путь к файлу набора сценариев.

Исключение – полностью воспроизводимый пример с заданными именами файлов, как в инструкции по запуску минимальной роли. В таком примере используйте эти имена без заменителей.

Описание инвентаря#

Для нового описания инвентаря рекомендуется YAML, поскольку он позволяет явно задавать структуру и типы переменных. Пример полного файла inventory.yml для локального выполнения:

---
all:
  hosts:
    localhost:
      ansible_connection: local
      example_enabled: false
      example_port: 8080
      example_code: "00123"

В формате INI правила зависят от расположения значения. В строке узла Ansible сначала разбирает запись с учетом правил оболочки, затем преобразует значения, которые имеют синтаксис литералов Python, в соответствующие типы. Например, example_enabled=False задает логическое значение, а example_port=8080 – число. Кавычки оболочки могут исчезнуть до определения типа, поэтому example_port="8080" не гарантирует строку.

Расширение ansible.builtin.ini разбирает литералы Python также в секции [<group_name>:vars]. Предварительного разбора строки по правилам оболочки здесь нет, поэтому example_port="8080" задает строку, а example_port=8080 – число. Запись example_enabled=False задает логическое значение, а example_enabled=false – строку. Для комментариев используйте отдельные строки, чтобы их текст не влиял на разбор значения. Пример полного файла inventory.ini:

[examples]
localhost ansible_connection=local example_enabled=False example_port=8080

[examples:vars]
example_code="00123"
example_message=Тестовое сообщение
# Комментарий на отдельной строке
example_label=ready

Здесь example_enabled – логическое значение, example_port – число, а остальные переменные этого примера – строки. Запись example_label="ready" в секции :vars передает строку ready без кавычек. Запись example_label=ready # комментарий включит комментарий в значение. Для сложных типов и единообразного разбора рекомендуется использовать файлы YAML в каталогах group_vars/ или host_vars/.

Для проверки полного описания инвентаря выполните команду:

ansible-inventory -i <inventory_file> --list

Здесь:

  • <inventory_file> – путь к проверяемому файлу YAML или INI.

Ожидаемый результат: Ansible выводит узлы и итоговые переменные в JSON без предупреждений о разборе файла. Проверьте не только наличие узлов, но и значения и типы переменных в объекте _meta.hostvars. Правила объединения переменных приведены в описании статической инвентаризации.

Файл ansible.cfg#

Файл ansible.cfg тоже использует формат INI, но не является описанием инвентаря. Типы значений определяет конкретная настройка Ansible. Правила строк узлов и секций :vars к нему не относятся. Указывайте настройки в соответствующих секциях и отделяйте = пробелами. Комментарии рекомендуется помещать на отдельные строки.

Не подходит для указанной цели

Корректный вариант

Причина

forks=7

forks = 7

Обе записи задают одно число; пробелы вокруг = относятся к оформлению.

forks = 7 в секции [ssh_connection]

forks = 7 в секции [defaults]

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

forks = 7 # Параллельные процессы

Строка # Параллельные процессы, затем отдельная строка forks = 7.

Символ # внутри значения мешает преобразованию в целое число. Концевые комментарии с ; допустимы, но отдельная строка исключает неоднозначность.

ansible_user = ansible в секции [defaults]

remote_user = ansible в секции [defaults]

Название переменной инвентаря не является названием настройки конфигурации.

inventory = {{ inventory_file }}

inventory = ./inventory.yml

Ansible не вычисляет выражения Jinja в файле конфигурации.

Пример полного файла конфигурации для проекта, в котором файл inventory.yml расположен рядом с ansible.cfg:

[defaults]
inventory = ./inventory.yml
remote_user = ansible
forks = 5
retry_files_enabled = false

Учетную запись подключения в секции [defaults] задает настройка remote_user, а не переменная инвентаря ansible_user. Не переносите в ansible.cfg выражения Jinja из набора сценариев, поскольку Ansible не обрабатывает их как шаблоны конфигурации. Путь ./inventory.yml относится к каталогу файла конфигурации.

Для проверки выбранного файла и распознанных настроек выполните команды из каталога проекта:

ansible --version
ansible-config dump --only-changed

Ожидаемый результат: первая команда показывает путь к ansible.cfg, а вторая – значения измененных настроек и их источники. Если настройка отсутствует в выводе, проверьте ее название, секцию и фактически загруженный файл.

Минимальная роль и набор сценариев#

Роль объединяет связанные задачи, значения переменных и вспомогательные файлы. Значения, которые необходимо переопределять через инвентарь, рекомендуется задавать в defaults/main.yml. Переменные из vars/main.yml имеют более высокий приоритет, чем переменные инвентаря. Подробности см. в описании ролей и правилах приоритета переменных.

Следующий пример запускает локальную роль и выводит сообщение без изменения файлов и служб. Для его выполнения необходим пакет Ansible Core на управляющем узле. Пример использует только встроенную коллекцию ansible.builtin и локальное выполнение.

Для подготовки и запуска примера выполните следующие действия:

  1. Создайте каталог проекта и вложенный каталог roles/example/ со следующей структурой:

    example-project/
    ├── inventory.yml
    ├── playbook.yml
    └── roles/
        └── example/
            ├── defaults/
            │   └── main.yml
            └── tasks/
                └── main.yml
    
  2. В inventory.yml сохраните полное описание инвентаря YAML из примера локального выполнения.

  3. В roles/example/defaults/main.yml задайте значение переменной:

    ---
    example_message: Проверка роли завершена
    
  4. В roles/example/tasks/main.yml добавьте список задач роли:

    ---
    - name: Вывод сообщения роли
      ansible.builtin.debug:
        msg: "{{ example_message }}"
    
  5. В playbook.yml сохраните полный набор сценариев:

    ---
    - name: Проверка локальной роли
      hosts: localhost
      gather_facts: false
      roles:
        - example
    
  6. Из каталога проекта проверьте синтаксис и запустите набор сценариев:

    ansible-playbook -i inventory.yml playbook.yml --syntax-check
    ansible-playbook -i inventory.yml playbook.yml
    

    Ожидаемый результат: проверка синтаксиса не сообщает об ошибках, задача выводит Проверка роли завершена, а итоговая сводка содержит changed=0 и failed=0.

В учебном примере вывод через debug является результатом работы роли. В прикладной роли временные отладочные задачи рекомендуется удалять или ограничивать параметром verbosity. Каталоги files/, templates/ и другие части роли добавляйте по мере необходимости; наличие каталога само по себе не запускает никаких операций.