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

Terraform + Ansible: как мы формализовали IaC-процесс и зачем это вообще нужно

Terraform создаёт VM и сеть, Ansible конфигурирует ОС и приложения, GitLab CI запускает всё при merge в master. Описываем структуру репо и review-процесс.

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

Terraform + Ansible как устойчивая связка для provisioning и configuration management - формализация IaC-процесса с GitLab CI пайплайном

Когда инфраструктура небольшая, можно держать всё в голове. Когда она растёт - начинается "а кто добавил это правило в файрволл", "почему на стейджинге nginx 1.10, а на проде 1.12", "кто руками поставил пакет и не занёс в плейбук". Мы через это прошли, и к середине этого года у нас наконец сложился процесс, который не стыдно назвать IaC без кавычек.

Разделение ответственности: Terraform делает инфраструктуру, Ansible делает систему

Ключевое решение, которое сняло большую часть вопросов - чёткая граница между двумя инструментами.

Terraform - это всё что связано с инфраструктурным уровнем: создать виртуальную машину, настроить сеть, создать диск, назначить IP, прописать группу безопасности. Terraform знает о существовании ресурсов в облаке или гипервизоре и управляет ими через API. После terraform apply у нас есть работающая VM с сетевыми настройками, но на ней только базовый образ ОС.

Ansible берёт эту VM и делает из неё то что нужно: ставит пакеты, генерирует конфиги из шаблонов, заводит пользователей, настраивает сервисы. Ansible не знает как создать виртуальный диск в vSphere - это не его дело. Он знает как настроить nginx и положить правильный nginx.conf.

Граница проходит примерно там, где заканчивается API провайдера и начинается SSH. Всё что делается до первого SSH - Terraform. Всё что после - Ansible.

Это разделение казалось очевидным когда его сформулировали, но до этого у нас был период когда часть провижнинга делалась через provisioner "remote-exec" прямо в Terraform-конфиге, а часть через Ansible. Получалось два места где живёт конфигурация ОС, и синхронизировать их вручную - развлечение на любителя.

Структура репозитория

Репо у нас называется infra, и в нём два верхнеуровневых раздела:

infra/
  terraform/
    modules/
      vm-base/
      network/
      security-groups/
    environments/
      prod/
        compute/
          main.tf
          variables.tf
          outputs.tf
        network/
          main.tf
      staging/
        compute/
          main.tf
        network/
          main.tf
  ansible/
    roles/
      common/
      nginx/
      postgres/
      app-backend/
    inventory/
      prod.yml
      staging.yml
    playbooks/
      site.yml
      web.yml
      db.yml
    group_vars/
      all.yml
      prod.yml
      staging.yml

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

GitLab CI: что запускается и когда

Пайплайн срабатывает при push в любую ветку и при merge в master.

При создании MR запускаются:

  • terraform plan - показывает diff к инфраструктуре, сохраняется как артефакт
  • ansible-lint + yamllint - проверка синтаксиса и стиля ролей
  • molecule test для ролей которые изменились в этом MR

При merge в master запускаются:

  • terraform apply с сохранённым plan-файлом из MR (только если plan был)
  • Ansible --check - ещё одна проверка без реального применения
  • Ручной триггер на ansible-playbook - автоматически не запускается

Последний пункт - осознанный выбор. Автоматический деплой конфигурации при merge в master звучит красиво, но требует уровня доверия к тестам которого у нас пока нет. molecule покрывает роли в изоляции, но не гарантирует что комбинация из пяти ролей на реальном хосте не даст сюрприз. Поэтому Ansible-часть запускается вручную после мержа, с артефактами из CI и глазами инженера на экране.

Terraform apply при мерже - автоматический, потому что там есть plan.bin который был проревьюен, и apply применяет строго его. Инфраструктурные изменения как правило атомарнее конфигурационных.

Review-процесс

MR в infra проходит через два типа проверки.

Автоматика: CI запускает plan и lint, прикрепляет вывод terraform plan в комментарий к MR через GitLab API. Это означает что ревьюер видит точный diff инфраструктуры прямо в интерфейсе, не запуская ничего локально.

Ревью коллеги: обязательно для любого изменения в environments/prod/ или в ролях которые используются на проде. Для стейджинга - рекомендательно, но на практике тоже делается, просто быстрее.

Отдельно договорились про несколько правил. Первое - никаких изменений в prod через консоль или SSH напрямую: если что-то не прошло через MR, оно не должно появляться на проде. Это дисциплина, не техническое ограничение, и она ломается под давлением дедлайнов - знаем по опыту. Второе - хотфиксы тоже через MR, просто маленький и быстрый.

Что реально работает, что скрипит

Хорошо: история изменений инфраструктуры в Git - это отдельная ценность. Когда что-то сломалось в 3 ночи, git log --oneline за последние сутки часто сразу указывает на виновный коммит.

Хорошо: terraform plan в MR как живой документ изменений. Никаких "я добавил правило в файрволл" - вот коммит, вот plan, вот кто апрувил.

Скрипит: синхронизация инвентаря между Terraform и Ansible. IP-адрес новой VM появляется в Terraform outputs, но в Ansible-инвентарь его нужно добавить руками. Это место где можно ошибиться и потом долго искать почему playbook не находит хост.

Скрипит: тесты для Ansible покрывают роли в изоляции, но интеграционных тестов на уровне "полный стек на реальном окружении" нет. Это осознанный пробел который давит на совесть.

Процесс не идеальный, но работает. Сопровождение инфраструктуры с таким подходом становится понятнее: новый инженер видит в Git полную историю, а не легенды о том почему на одном сервере стоит нестандартная версия пакета.

Следующий шаг который обсуждаем - динамический инвентарь из Terraform state и автоматическая передача IP-адресов из outputs в Ansible без ручного шага. Это уберёт главное место где процесс зависит от человека с руками.

Контакт

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

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