Модули - кирпичики автоматизации

Урок 8 - знакомимся с модулями: готовыми кирпичиками для управления системой.

«Под каждую типовую задачу - свой модуль. Не пиши shell-команду там, где есть готовый идемпотентный модуль».

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

Ключевые модули

МодульНазначение
ansible.builtin.apt / yum / dnfУправление пакетами
ansible.builtin.copyСкопировать файл на хост
ansible.builtin.templateСгенерировать файл из Jinja2-шаблона
ansible.builtin.fileСоздать каталог, задать права, симлинк
ansible.builtin.serviceУправление сервисами (start/stop/restart)
ansible.builtin.userСоздание и удаление пользователей
ansible.builtin.lineinfileТочечная правка строки в файле

FQCN - полные имена

Современная практика - использовать FQCN (Fully Qualified Collection Name): не apt, а ansible.builtin.apt. Это убирает двусмысленность, когда несколько коллекций содержат модули с одинаковыми короткими именами. Формат: пространство.коллекция.модуль.

- name: Создать каталог приложения
  ansible.builtin.file:
    path: /opt/myapp
    state: directory
    owner: deploy
    mode: "0755"

- name: Завести пользователя deploy
  ansible.builtin.user:
    name: deploy
    groups: sudo
    shell: /bin/bash

Как работает под капотом

Каждый модуль - это маленькая программа на Python. Она принимает параметры (как name=nginx state=present), смотрит текущее состояние системы, сравнивает с желаемым и применяет минимально необходимое изменение. Возвращает JSON со статусом changed или нет. Именно поэтому модули идемпотентны «из коробки» - сравнение состояний встроено в них.

Смоделируем логику модуля file для каталога: создаём, только если его нет.

# Имитация модуля file (state=directory): идемпотентное создание
existing_dirs = {"/opt", "/etc/myapp"}

def ensure_directory(path, dirs):
    if path in dirs:
        return {"changed": False, "path": path}
    dirs.add(path)
    return {"changed": True, "path": path}

print(ensure_directory("/opt/myapp", existing_dirs))  # changed: True
print(ensure_directory("/opt/myapp", existing_dirs))  # changed: False
print(ensure_directory("/etc/myapp", existing_dirs))  # changed: False (уже есть)

Попробуй сам ▶ Модуль не «создаёт каталог», он «обеспечивает, чтобы каталог существовал». Разница тонкая, но именно она делает повторные запуски безопасными.

Частые ошибки

  • Mode как число без кавычек. mode: 0755 YAML может прочитать как восьмеричное/десятичное число неожиданно. Пиши строкой: mode: "0755".
  • Сырой shell вместо модуля. shell: "useradd deploy" не идемпотентен и упадёт при повторе. Есть модуль user.
  • Игнорировать документацию. У каждого модуля свои параметры; ansible-doc ansible.builtin.file покажет их все.

Best practices

  • Используй FQCN везде - это снимает конфликты имён и читается однозначно.
  • Предпочитай специализированный модуль сырому command/shell - почти всегда он есть.
  • Изучай ansible-doc <модуль> - там примеры и все параметры.

В реальной работе

Перед тем как тянуться к shell, опытный инженер сначала ищет специализированный модуль - почти всегда он есть. Нужно поправить одну строку в конфиге? Есть lineinfile. Добавить блок? blockinfile. Управлять записью в crontab? cron. Смонтировать диск? mount. Каждый такой модуль идемпотентен и понимает контекст задачи, тогда как shell - это слепое выполнение без проверки состояния. Команда ansible-doc с именем модуля показывает все параметры и готовые примеры прямо в терминале - это быстрее, чем гуглить, и всегда соответствует установленной версии. Знание десятка ключевых модулей покрывает 90% повседневных задач настройки серверов.

Итоги

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

Проверьте себя
1. Что такое FQCN и зачем он нужен?
AФормат логов Ansible
BПолное имя модуля вида пространство.коллекция.модуль - убирает двусмысленность при одинаковых коротких именах
CТип переменной
DКоманда установки
2. Почему mode лучше писать как mode: "0755", а не mode: 0755?
AТак быстрее
BЧтобы YAML не интерпретировал значение как число и права применились корректно
CКавычки обязательны для всех параметров
DБез кавычек модуль не запустится вообще