ADG Оставить заявку
Блог DevOps 4 мин чтения

Ansible и идемпотентность: провизионируем серверы без ручных чеклистов

Как Ansible playbooks заменили нам чеклисты при развёртывании серверов и почему принцип идемпотентности меняет логику работы с инфраструктурой.

Контекст момента

Ansible набирает популярность как agentless-инструмент автоматизации конфигураций в 2013-2014 году

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

Главное изменение - мы перестали держать в голове или на бумаге список «что нужно сделать на новом сервере».

Откуда берутся чеклисты

Когда разворачиваешь новый сервер вручную, неизбежно появляется список действий. Сначала он в голове, потом - в вики, потом в текстовом файле с инструкцией. У нас был такой файл на два экрана: поставить пакеты, настроить syslog, выставить timezone, добавить пользователей, отключить root-логин по SSH, положить authorized_keys, настроить NTP, прописать в Zabbix, сделать ещё пятнадцать вещей.

Проблема не в том, что список длинный. Проблема в том, что его выполняют руками. Шаг пропускается, пункт интерпретируется по-разному, коллега делает чуть иначе - и через месяц два «одинаково настроенных» сервера на самом деле разные. Ловишь это, когда что-то ломается в продакшне и оказывается, что на одном сервере NTP настроен, а на другом - нет.

С Ansible чеклист стал playbook-ом, и теперь он не просто напоминание, а исполняемый код.

Что такое идемпотентность на практике

Слово громкое, но за ним стоит простая идея: запусти playbook хоть раз, хоть десять - результат будет один и тот же. Если NTP уже установлен - Ansible не будет его переустанавливать. Если строка в конфиге уже есть - не добавит дубль. Если пользователь существует - не попытается создать его заново.

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

На практике это означает: можно запустить playbook на сервере, который уже работает в продакшне, и он ничего не сломает - просто проверит что всё на месте и пройдёт дальше. Это принципиально меняет отношение к инструменту: он перестаёт быть «скриптом, который запускают один раз при установке» и становится описанием желаемого состояния.

Как выглядит провизионинг сейчас

Структура у нас сложилась примерно такая:

  • base.yml - всё что должно быть на любом Linux-сервере: пользователи, SSH-конфиг, базовые пакеты, NTP, rsyslog, Zabbix-агент.
  • web.yml - nginx, PHP-FPM, нужные расширения, конфиги с шаблонами под конкретный сервер.
  • db.yml - PostgreSQL или MySQL, настройки производительности, резервное копирование.

Поднимается новый сервер для клиента на сопровождении - берём нужные playbooks, прописываем хост в инвентарь, запускаем. За двадцать-тридцать минут сервер в нужном состоянии. Не «примерно как должно быть», а точно как описано в YAML.

Важный момент с инвентарём: переменные под конкретного клиента или конкретный сервер живут отдельно от самих playbooks. Одни и те же роли применяются к разным серверам с разными параметрами - hostname, IP Zabbix-сервера, список пользователей, которым нужен доступ.

Где пока не всё гладко

Сложности есть с идемпотентностью в shell-модуле. Когда нужно выполнить команду, для которой нет готового Ansible-модуля, используешь shell: - и тут идемпотентность надо обеспечивать самому через creates: или when:. Если забыть - команда выполнится при каждом запуске, что иногда нежелательно.

Ещё непростая тема - порядок выполнения задач при зависимостях. Если сервис должен стартовать только после того как применился конфиг, это нужно явно прописывать через notify и handlers. Первые разы путаешься, потом привыкаешь.

И самое болезненное - тестирование. Playbook прогоняешь на тестовой машине, всё работает. Но тест - это разовое выполнение на чистой машине. Идемпотентность нужно проверять отдельно: запустить второй раз и убедиться что изменений нет. Не всегда делаем, потом ловим баги. Работаем над этим.

Что изменилось в голове

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

Puppet делает то же самое, но Ansible дешевле в старте - никакого master-сервера, никаких агентов. Для парков в десятки серверов это ощутимо.

Чеклист в вики всё ещё существует - но теперь это не инструкция «что делать руками», а документация к playbook-ам: почему именно эти пакеты, зачем этот параметр в конфиге. Разница небольшая, но принципиальная.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.