Модули Ansible — как использовать их эффективно (примеры)

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

Если вас интересуют другие концепции Ansible, вам могут быть полезны эти учебники по Ansible, опубликованные в блоге Spacelift.

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

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

Вот пример задачи с использованием модуля apt package manager для установки определенной версии Nginx.

- name: "Install Nginx to version {{ nginx_version }} with apt module"
   ansible.builtin.apt:
     name: "nginx={{ nginx_version }}"
     state: present
Вход в полноэкранный режим Выход из полноэкранного режима

Модули также можно запускать непосредственно из командной строки. Вот пример запуска модуля ping против всех хостов базы данных из командной строки.

ansible databases -m ping
Вход в полноэкранный режим Выход из полноэкранного режима

Работа с модулями Ansible

Хорошо спроектированный модуль предоставляет предсказуемый и четко определенный интерфейс, принимающий аргументы, которые имеют смысл и согласуются с другими модулями. Модули принимают некоторые аргументы в качестве входных данных и возвращают значения в формате JSON после выполнения.

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

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

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

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

Вот пример вывода результатов выполнения команды ansible-doc.

ansible-doc apt
Вход в полноэкранный режим Выход из полноэкранного режима

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

12 полезных и распространенных модулей Ansible

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

Модули менеджера пакетов yum & apt

Модуль apt является частью ansible-core и управляет пакетами apt для дистрибутивов Debian/Ubuntu Linux. Вот пример, который обновляет кэш репозитория и обновляет пакет Nginx до последней версии:

- name: Update the repository cache and update package "nginx" to latest version
  ansible.builtin.apt:
    name: nginx
    state: latest
    update_cache: yes
Вход в полноэкранный режим Выход из полноэкранного режима

Модуль yum также является частью ansible-core и управляет пакетами с помощью yum для дистрибутивов RHEL/Centos/Fedora Linux. Вот тот же пример, что и выше, с модулем yum:

- name: Update the repository cache and update package "nginx" to latest version
   ansible.builtin.yum:
     name: nginx
     state: latest
     update_cache: yes
Вход в полноэкранный режим Выход из полноэкранного режима

Модуль Service

Сервисный модуль управляет службами на удаленных хостах и может использовать различные системы init в зависимости от их наличия в системе. Этот модуль обеспечивает хороший уровень абстракции для базовых модулей менеджера сервисов. Вот пример перезапуска службы docker:

- name: Restart docker service
   ansible.builtin.service:
     name: docker
     state: restarted
Вход в полноэкранный режим Выход из полноэкранного режима

Файловый модуль

Модуль file обрабатывает операции с файлами, симлинками и каталогами. Вот пример использования этого модуля для создания каталога с определенными разрешениями:

- name: Create the directory "/etc/test" if it doesnt exist and set permissions
  ansible.builtin.file:
    path: /etc/test
    state: directory
    mode: '0750'
Войти в полноэкранный режим Выйти из полноэкранного режима

Модуль копирования

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

- name: Copy file with owner and permissions
  ansible.builtin.copy:
    src: /example_directory/test
    dest: /target_directory/test
    owner: joe
    group: admins
    mode: '0755'
Вход в полноэкранный режим Выход из полноэкранного режима

Модуль шаблонов

Модуль шаблонов помогает нам шаблонировать файлы для целевых хостов, используя язык шаблонов Jinja2. Вот пример использования файла шаблона и некоторых переменных Ansible для создания конфигурационного файла Nginx:

- name: Copy and template the Nginx configuration file to the host
  ansible.builtin.template:
    src: templates/nginx.conf.j2
    dest: /etc/nginx/sites-available/default
Вход в полноэкранный режим Выход из полноэкранного режима

Модули Lineinfile & Blockinfile

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

- name: Add a line to a file if it doesnt exist
  ansible.builtin.lineinfile:
    path: /tmp/example_file
    line: "This line must exist in the file"
    state: present
Вход в полноэкранный режим Выход из полноэкранного режима

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

- name: Add a block of config options at the end of the file if it doesn’t exist
  ansible.builtin.blockinfile:
    path: /etc/example_dictory/example.conf
    block: |
      feature1_enabled: true
      feature2_enabled: false
      feature2_enabled: true
    insertafter: EOF
Вход в полноэкранный режим Выход из полноэкранного режима

Модуль Cron

Модуль cron управляет записями crontab и переменными окружения на удаленных хостах.

- name: Run daily DB backup script at 00:00
  ansible.builtin.cron:
    name: "Run daily DB backup script at 00:00"
    minute: "0"
    hour: "0"
    job: "/usr/local/bin/db_backup_script.sh > /var/log/db_backup_script.sh.log 2>&1"
Войти в полноэкранный режим Выйти из полноэкранного режима

Модуль wait_for

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

- name: Wait until a string is in the file before continuing
  ansible.builtin.wait_for:
    path: /tmp/example_file
    search_regex: "String exists, continue"
Вход в полноэкранный режим Выход из полноэкранного режима

Модули команд и оболочки

Модули command и shell выполняют команды на удаленных узлах. Их основное отличие в том, что модуль command обходит локальную оболочку, и, следовательно, переменные типа $HOSTNAME или $HOME недоступны, а операции типа «<«, «&» не работают. Если вам нужны эти возможности, вы должны использовать модуль shell.

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

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

- name: Execute a script in remote shell and capture the output to file
  ansible.builtin.shell: script.sh >> script_output.log
Вход в полноэкранный режим Выход из полноэкранного режима

Создание модулей Ansible

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

Прежде чем приступать к разработке модуля, убедитесь, что аналогичного модуля не существует, чтобы избежать ненужной работы. Αdditionally, you may be able to combine different modules to achieve the functionality you need. В этом случае вы сможете воспроизвести нужное вам поведение, создав роль, использующую другие модули. Другой вариант — использовать плагины для расширения базовой функциональности Ansible с помощью логики и новых возможностей, доступных всем модулям.

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

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

Мы создаем наш пользовательский Python-модуль epoch_converter.py внутри каталога library. Этот простой модуль принимает на вход аргумент epoch_timestamp и конвертирует его в тип datetime. Мы используем другой аргумент, state_changed, для имитации изменения целевой системы этим модулем.

library/epoch_converter.py

#!/usr/bin/python

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import datetime

DOCUMENTATION = r'''
---
module: epoch_converter

short_description: This module converts an epoch timestamp to human-readable date.

# If this is part of a collection, you need to use semantic versioning,
# i.e. the version is of the form "2.5.0" and not "2.4".
version_added: "1.0.0"

description: This module takes a string that represents a Unix epoch timestamp and displays its human-readable date equivalent.

options:
   epoch_timestamp:
       description: This is the string that represents a Unix epoch timestamp.
       required: true
       type: str
   state_changed:
       description: This string simulates a modification of the target's state.
       required: false
       type: bool

author:
   - Ioannis Moustakis (@Imoustak)
'''

EXAMPLES = r'''
# Convert an epoch timestamp
- name: Convert an epoch timestamp
 epoch_converter:
   epoch_timestamp: 1657382362
'''

RETURN = r'''
# These are examples of possible return values, and in general should use other names for return values.
human_readable_date:
   description: The human-readable equivalent of the epoch timestamp input.
   type: str
   returned: always
   sample: '2022-07-09T17:59:22'
original_timestamp:
   description: The original epoch timestamp input.
   type: str
   returned: always
   sample: '16573823622'

'''

from ansible.module_utils.basic import AnsibleModule


def run_module():
   # define available arguments/parameters a user can pass to the module
   module_args = dict(
       epoch_timestamp=dict(type='str', required=True),
       state_changed=dict(type='bool', required=False)
   )

   # seed the result dict in the object
   # we primarily care about changed and state
   # changed is if this module effectively modified the target
   # state will include any data that you want your module to pass back
   # for consumption, for example, in a subsequent task
   result = dict(
       changed=False,
       human_readable_date='',
       original_timestamp=''
   )

   # the AnsibleModule object will be our abstraction working with Ansible
   # this includes instantiation, a couple of common attr would be the
   # args/params passed to the execution, as well as if the module
   # supports check mode
   module = AnsibleModule(
       argument_spec=module_args,
       supports_check_mode=True
   )

   # if the user is working with this module in only check mode we do not
   # want to make any changes to the environment, just return the current
   # state with no modifications
   if module.check_mode:
       module.exit_json(**result)

   # manipulate or modify the state as needed (this is going to be the
   # part where your module will do what it needs to do)
   result['original_timestamp'] = module.params['epoch_timestamp']
   result['human_readable_date'] = datetime.datetime.fromtimestamp(int(module.params['epoch_timestamp']))

   # use whatever logic you need to determine whether or not this module
   # made any modifications to your target
   if module.params['state_changed']:
       result['changed'] = True

   # during the execution of the module, if there is an exception or a
   # conditional state that effectively causes a failure, run
   # AnsibleModule.fail_json() to pass in the message and the result
   if module.params['epoch_timestamp'] == 'fail':
       module.fail_json(msg='You requested this to fail', **result)

   # in the event of a successful module execution, you will want to
   # simple AnsibleModule.exit_json(), passing the key/value results
   module.exit_json(**result)


def main():
   run_module()


if __name__ == '__main__':
   main()

Вход в полноэкранный режим Выйти из полноэкранного режима

Чтобы протестировать наш модуль, давайте создадим test_custom_module.ymlplaybook в той же директории, что и наша library директория.

test_custom_module.yml

- name: Test my new module
  hosts: localhost
  tasks:
  - name: Run the new module
    epoch_converter:
      epoch_timestamp: '1657382362'
      state_changed: yes
    register: show_output
  - name: Show Output
    debug:
      msg: '{{ show_output }}'
Вход в полноэкранный режим Выйти из полноэкранного режима

И последнее, давайте выполним плейбук для тестирования нашего пользовательского модуля. Поскольку мы решили установить аргумент state_changed, мы ожидаем, что состояние задачи появится как changed и будет отображаться желтым цветом.

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

Лучшие практики использования модулей Ansible
Используйте специализированные модули вместо shell или command: Хотя может возникнуть соблазн часто использовать shell или командный модуль, считается лучшей практикой использовать более специализированные модули для каждой работы. Специализированные модули обычно рекомендуются, поскольку они реализуют концепцию желаемого состояния и идемпотентности, были протестированы и соответствуют основным стандартам, таким как обработка ошибок.

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

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

- name: Install Docker dependencies
  ansible.builtin.apt:
     name:
       - curl
       - ca-certificates
       - gnupg2
       - lsb-release
     state: latest
Вход в полноэкранный режим Выйти из полноэкранного режима

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

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

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

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

Ключевые моменты

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

Спасибо за прочтение, и я надеюсь, что эта статья понравилась вам так же, как и мне.

Оцените статью
devanswers.ru
Добавить комментарий