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

IaC-стандарт: Terraform держит ресурсы, Ansible держит состояние - граница больше не обсуждается

Формализуем внутренний стандарт разделения ответственности: Terraform - VM, сеть, хранилище; Ansible - ОС и приложения. Ревью стало тише.

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

IaC-практики зрелеют: разделение ответственности Terraform (инфраструктура) и Ansible (конфигурация) становится де-факто стандартом на managed-проектах

Полгода назад мы описывали как выглядит наш IaC-процесс в целом - структура репо, GitLab CI, review-процесс. С тех пор накопилось достаточно опыта чтобы зафиксировать одну вещь отдельно: граница между Terraform и Ansible. Не как рекомендацию, а как стандарт который мы прописали и теперь проверяем на ревью механически.

Откуда взялась проблема

Когда у тебя один проект и два инженера, граница между инструментами держится в голове. Когда проектов становится больше и к ним подключаются разные люди, начинается разброс. Один инженер ставит пакеты через provisioner "remote-exec" в Terraform-конфиге - потому что «так удобно при создании VM». Другой пишет Ansible-таски которые создают cloud-диски через модуль os_volume - потому что «проще держать в одном плейбуке». Третий кладёт cloud-init скрипты в Terraform, а потом та же логика дублируется в Ansible-роли.

В итоге конфигурация ОС живёт в трёх местах, и отвечая на вопрос «почему на этом сервере вот такой /etc/resolv.conf» нужно смотреть и туда и туда и ещё сюда.

Где проходит граница

Стандарт, который мы зафиксировали, простой:

Terraform владеет всем что существует на уровне API провайдера. VM, сетевые интерфейсы, подсети, security groups, floating IP, блочные хранилища, балансировщики, DNS-записи - всё это Terraform. Если ресурс появляется через вызов API облака или гипервизора, он живёт в .tf-файле и нигде больше.

Ansible владеет всем что происходит внутри ОС. Пакеты, конфиги, пользователи, systemd-юниты, файлы, cron-задачи, состояние сервисов - это Ansible. Если нужно что-то сделать через SSH, это роль или task в плейбуке.

Граница проходит по первому SSH-соединению. Всё до него - Terraform. Всё после - Ansible.

Из этого следует несколько частных правил которые мы добавили в CONTRIBUTING.md репо:

  • provisioner "remote-exec" и provisioner "file" запрещены. Это Terraform лезет за свою границу. Всё что они делают, должно быть в Ansible.
  • Ansible-модули для облачных ресурсов (os_server, os_volume, ec2) запрещены в ролях. Это Ansible лезет за свою границу. Облачные ресурсы создаёт Terraform.
  • cloud-init используется только для минимальной подготовки к первому Ansible-запуску - добавить SSH-ключ, убедиться что Python есть. Не для конфигурации приложений.

Как это помогло на практике

До фиксации стандарта каждый второй MR в infra-репо содержал дискуссию в духе «это лучше вынести в Ансибл» или наоборот. Дискуссия сама по себе не вредная, но когда одно и то же обсуждается в десятом MR подряд - это уже трата времени.

После того как правило появилось в CONTRIBUTING.md и мы добавили его в чеклист ревью, эти дискуссии практически исчезли. Теперь вместо «мне кажется это лучше...» можно просто сослаться на правило и попросить переделать. Нет предмета для спора.

Второй эффект - стало проще онбордить новых людей на проект. Раньше приходилось объяснять «у нас сложилась традиция делать вот так». Теперь есть документ.

Что всё ещё скрипит

Граница чёткая, но есть зоны где она не такая однозначная.

Terraform outputs -> Ansible inventory. Terraform создал VM и знает её IP. Ansible должен знать куда подключаться. Пока эта передача у нас частично руками - Terraform outputs копируются в Ansible-инвентарь. Мы экспериментировали с dynamic inventory через terraform-inventory но в мультиокружении с несколькими state-файлами оно работает неудобно. Вопрос открытый.

Теги и метаданные VM. Terraform ставит теги на VM - это его зона. Но Ansible иногда хочет читать эти теги чтобы принять решение что устанавливать. Читать через cloud-API из Ansible нормально, но не создавать/изменять.

Bootstrap-конфиг. Параметры ОС которые нужны до первого Ansible-запуска - имя хоста, сетевые настройки при статическом IP, базовые разделы - это cloud-init или Terraform user_data. Граница здесь размытая, и мы разрешили использовать user_data только для вещей без которых Ansible физически не дотянется до сервера.

Почему это важно именно сейчас

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

Записанный стандарт - это не про недоверие к инженерам. Это про то чтобы не тратить время на повторное изобретение одного и того же решения. Когда следующий инженер садится за IaC-задачу, он должен знать в какой файл писать, не спрашивая коллег.

Граница Terraform/Ansible - первое правило в нашем IaC-стандарте. За декабрь добавим ещё несколько про структуру модулей и именование ресурсов. Работа небыстрая, но раз начали - надо доводить.

Контакт

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

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