Блог "Дети ОИТа"
/поиск

Ansible Pull: автономные узлы, cron и отказоустойчивость

Когда push-модель не подходит: разбираем Ansible Pull, его архитектуру, типовые грабли и готовые рецепты для автономных или изолированных окружений.

Ansible Pull: автономные узлы, cron и отказоустойчивость

Ansible Pull — это режим, при котором узел сам инициирует получение конфигурации, а не ждёт команды от центрального контроллера. Это переворачивает привычную push-модель с ног на голову и решает задачи, где классический Ansible бессилен.

Когда выбирать Pull вместо Push

Push (классический Ansible) требует:

  • Доступности SSH от контроллера к хостам.
  • Открытого пути через NAT/файрволы.
  • Централизованного учёта инвентаря.

Pull нужен, когда:

  • Хосты за NAT или в изолированных сегментах (DMZ, производственные сети).
  • Машины часто появляются и исчезают (эпемерные инстансы, edge-устройства).
  • Требуется автономность: узел должен выжить без связи с контроллером.
  • Нужно масштабировать на тысячи хостов без узкого горлышка в виде одного бастиона.

Архитектура и компоненты

1. ansible-pull (CLI)

Утилита, которая:

  1. Клонирует репозиторий с плейбуками (git clone или аналоги).
  2. Запускает указанный плейбук локально (ansible-playbook).
  3. Записывает логи (обычно в /var/log/ansible-pull.log).

Типовая команда:

ansible-pull \
  -U https://git.example.com/infra/ansible.git \
  -d /opt/ansible-pull \
  -i inventory/hosts.ini \
  playbooks/site.yml \
  --extra-vars "env=prod" \
  -v

2. Планировщик (cron / systemd timer)

Pull бесполезен без автозапуска. Варианты:

Cron (просто и надёжно):

# /etc/cron.d/ansible-pull
*/15 * * * * root /usr/bin/ansible-pull -U https://git.example.com/infra/ansible.git -d /opt/ansible-pull >> /var/log/ansible-pull.log 2>&1

Systemd timer (лучше для логирования и зависимостей):

# /etc/systemd/system/ansible-pull.service
[Unit]
Description=Ansible Pull
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/ansible-pull -U https://git.example.com/infra/ansible.git -d /opt/ansible-pull
StandardOutput=journal
StandardError=journal
# /etc/systemd/system/ansible-pull.timer
[Unit]
Description=Run Ansible Pull every 15 minutes

[Timer]
OnBootSec=5min
OnUnitActiveSec=15min
Persistent=true

[Install]
WantedBy=timers.target

3. Репозиторий как источник правды

Всё хранится в Git. Структура:

infra/ansible/
├── playbooks/
│   ├── site.yml          # Входная точка
│   └── roles.yml
├── roles/
├── inventory/
│   └── hosts.ini         # Статический или минимальный
├── requirements.yml      # Зависимости ролей
└── ansible.cfg           # Локальные настройки

Self-registration и идентификация

Главная боль Pull — как хост понимает, кто он?
Решения (от простого к сложному):

  1. По hostname (просто):
    В ansible.cfg или через --extra-vars:

    ansible-pull --extra-vars "hostname={{ ansible_hostname }}"
    

    Внутри плейбука:

    - name: Apply role by hostname
      import_role:
        name: "role_{{ hostname | regex_replace('[^a-z0-9]', '_') }}"
    
  2. По метаданным облака (AWS/GCP/Azure):
    Скрипт на старте получает IMDS/metadata и пишет временный inventory.

  3. CMDB lookup (продвинуто):
    Хост при первом запуске стучится в API CMDB, получает свою роль и сохраняет в /etc/ansible/facts.d/.

Отказоустойчивость и масштабирование

Кэширование репозитория

Каждый ansible-pull делает git fetch. Это создаёт нагрузку на Git-сервер.
Решение:

  • Включить --checkout только при смене тега/коммита.
  • Использовать локальный кэш: --directory /opt/ansible-pull (он уже есть).
  • Для масштабирования: поставить локальный Git-зеркало (Gitea, GitLab) внутри сегмента.

Обработка сбоев

  • Транзакционность: Ansible сам по себе идемпотентен, но убедитесь, что роли не оставляют систему в полуразвёрнутом состоянии.
  • Логи: Ротация /var/log/ansible-pull.log. Используйте journald через systemd.
  • Dead man’s switch: Если хост не применял конфиг N часов — алёрт в мониторинг.

Сравнение: Pull vs Push vs Satellite

Критерий Push (классика) Pull (автономность) AWX/AAP (Satellite)
Связность SSH от контроллера HTTPS/Git от хоста API/WebSocket
Масштаб Ограничен бастионом Почти линейный Зависит от инстансов
NAT/DMZ Проблема Решение Решение (через релеи)
Сложность Низкая Средняя (нужен Git) Высокая

Чек-лист внедрения Ansible Pull

  • Выбран режим запуска: cron или systemd timer.
  • Настроен доступ к Git-репозиторию (deploy key или token).
  • Реализована логика self-registration (по hostname или metadata).
  • Включено кэширование фактов (fact_caching).
  • Настроена ротация логов и мониторинг последнего запуска.
  • Проверена идемпотентность ролей (многократный запуск не ломает систему).
  • Для масштабирования поднят локальный Git-зеркало.

Мини-пример: Полный цикл

1. Unit-файл (/etc/systemd/system/ansible-pull.service):

[Unit]
Description=Ansible Pull configuration
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/ansible-pull \
  -U https://git.internal/ansible.git \
  -d /var/lib/ansible-pull \
  playbooks/site.yml \
  --extra-vars "env=prod"
StandardOutput=journal
StandardError=journal

2. Плейбук (playbooks/site.yml):

- hosts: localhost
  connection: local
  gather_facts: yes
  roles:
    - role: base
    - role: "role_{{ ansible_hostname | lower }}"

3. Запуск и проверка:

systemctl enable --now ansible-pull.timer
journalctl -u ansible-pull.service -f
ansible-pull --check --diff  # Сухой прогон

Ansible Pull — это не «костыль», а архитектурный выбор для распределённых систем. Он жертвует централизованным контролем ради выживаемости и масштабируемости узлов.

См. также: