Ansible playbook вместо чеклиста: провизионируем новый Linux-сервер за 20 минут
Перевели ручной чеклист развёртывания нового сервера в Ansible playbook. Первые 50 строк YAML уже заменили час рутины - и теперь это идемпотентно.
Ansible 1.8/1.9 набирает популярность как agentless-инструмент автоматизации Linux-инфраструктуры
Год назад писали про Ansible и идемпотентность в довольно общих словах. Сейчас есть что рассказать конкретнее: как именно мы перевели ручной чеклист развёртывания нового сервера в playbook, что из этого вышло и где ещё спотыкаемся.
Повод простой: в декабре подняли очередной сервер для клиента на сопровождении. Выбрали момент, чтобы сделать это уже не по чеклисту, а полностью через Ansible 1.8. Ниже - что реально произошло, без приукрашивания.
Откуда взялась задача
Чеклист существовал в вики уже года три. Выглядел примерно так: два десятка пунктов, часть из них с пометкой «зависит от клиента», несколько с примечаниями «на RHEL иначе». Когда сервер поднимает один и тот же инженер - всё в голове, работает. Когда задача падает на другого человека или ты сам возвращаешься к этому через три месяца - начинаются вопросы. Пропустил ли sudo-конфиг? Выставил ли правильный timezone? Добавил ли хост в мониторинг?
Принцип простой: если делаешь одно и то же руками больше трёх раз - пора автоматизировать. Мы немного затянули.
Структура playbook
Итоговый base-playbook получился на 180 строк YAML, но костяк - те самые первые 50 - делает основное:
- Базовые пакеты.
vim,htop,curl,wget,lsof,net-tools,chronyилиntpв зависимости от дистрибутива. Модульapt/yumпроверяет наличие перед установкой - запустишь второй раз, ничего лишнего не случится. - Временная зона. Один таск через
timezone:- и не нужно помнить что на одном клиентском сервере мы выставляли Europe/Moscow черезtimedatectl, а на другом правили/etc/localtimeвручную. - SSH-конфиг. Отключение root-логина, разрешённые пользователи, порт. Шаблон Jinja2 с переменными - один и тот же плейбук, разные параметры под разных клиентов.
- Пользователи и ключи. Список пользователей и их публичные ключи хранятся в переменных инвентаря. Модуль
authorized_keyидемпотентен - добавляет ключ только если его ещё нет. - Zabbix-агент. Ставится из репозитория, конфиг генерируется из шаблона с адресом Zabbix-сервера, сервис включается в автозагрузку. Три таска вместо шести ручных шагов.
Оставшиеся 130 строк - это handlers, проверки через assert, и специфика для CentOS 6 vs CentOS 7 (systemd в CentOS 7 меняет несколько вещей с управлением сервисами).
Что получилось на практике
Подняли сервер из шаблона VMware, прописали в инвентарь, запустили ansible-playbook base.yml -l новый_хост. Через 18 минут сервер был в нужном состоянии: пользователи созданы, SSH перенастроен, Zabbix видит хост, NTP синхронизирован.
Раньше этот же процесс занимал минут 50-60, если делать аккуратно и сверяться с чеклистом. Если делать по памяти - могло быть быстрее, но с риском что-то пропустить.
Самый приятный момент: через неделю запустили тот же playbook повторно, когда потребовалось добавить ещё одного пользователя. Ansible прошёл по всем тасками, ничего лишнего не тронул, только добавил нового пользователя и ключ. Это и есть идемпотентность в деле - не абстрактное свойство, а конкретное «можно запустить ещё раз и не бояться».
Где спотыкаемся
Несколько мест, где всё пошло не так гладко.
Условия под разные дистрибутивы быстро превращают playbook в нечитаемое when: ansible_os_family == "RedHat" через каждые три строки. Мы пока решаем это через group_vars: серверы по дистрибутивам в разных группах, специфичные переменные переопределяются там. Работает, но требует дисциплины при добавлении новых хостов в инвентарь.
Shell-таски без creates: - вечная проблема. Там где нет готового модуля и приходится использовать shell: или command:, идемпотентность надо обеспечивать вручную. Пару раз забыли, и при повторном запуске таск выполнялся заново. Не катастрофа, но неприятно.
Порядок handlers несколько раз удивлял. Ansible запускает handlers в конце play, а не сразу после notify. Если перезапуск сервиса нужен до следующего таска - это нужно явно обрабатывать через meta: flush_handlers. Вроде понятно, но в горячке забываешь.
Про версию 1.8/1.9
Ansible 1.8 вышел в октябре, 1.9 ожидается в начале года. В 1.8 заметно улучшился модуль template и появился нормальный Ansible Vault для хранения секретов - мы наконец перестали класть пароли в plaintext в переменные. Vault шифрует файл целиком: ansible-vault encrypt vars/secrets.yml - и файл уходит в репозиторий в зашифрованном виде.
vault в 1.8 ещё немного сырой: если зашифровал файл, расшифровать его для чтения из командной строки можно только через ansible-vault view, редактировать - через ansible-vault edit. Неудобно когда надо быстро сверить значение. Ждём что в 1.9 это поправят.
Что дальше
Базовый playbook работает. Следующий шаг - разбить его на роли, чтобы можно было переиспользовать отдельные куски: роль ntp, роль zabbix-agent, роль ssh-hardening. Ansible Galaxy как репозиторий готовых ролей уже существует, но большинство публичных ролей пишут под свои предположения о структуре системы - проще написать свою, чем адаптировать чужую.
Есть у нас и другой план: превратить этот playbook в основу для CI-пайплайна, где каждый новый сервер проходит проверку через ansible-playbook --check перед реальным применением. Пока это идея, не реализация.
В целом: 50 строк YAML за вечер - и ручной чеклист стал историей. Не панацея, есть нюансы, но направление очевидное.