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

Terraform 0.6: state в git и plan перед apply - первые шаги к IaC без Puppet

Описываем инфраструктуру тестового окружения в публичном облаке через Terraform 0.6.x: state-файл в git, обязательный plan, никакого Puppet для облака.

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

Terraform 0.6.x активно используется командами для управления облачной инфраструктурой как кодом в начале 2016 года

Несколько недель назад мы наконец-то попробовали Terraform на практике. Не на боевой инфраструктуре, конечно, - взяли тестовое окружение одного из проектов на сопровождении в публичном облаке и написали конфигурацию с нуля. Рассказываем, что получилось и где споткнулись.

Контекст: зачем вообще

До сих пор облачные ресурсы у нас создавались руками через веб-консоль или CLI провайдера. Три виртуалки, балансировщик, несколько групп безопасности - казалось бы, немного. Но воспроизвести окружение точь-в-точь через полгода уже нетривиально: кто-то добавил правило в security group и забыл задокументировать, кто-то поднял четвёртую виртуалку для отладки и не удалил. Классика.

Puppet мы используем для конфигурирования серверов внутри периметра, но для управления самими облачными ресурсами - типами инстансов, сетями, дисками - он не очень подходит. Это разные уровни абстракции.

Terraform нацелен именно на provisioning инфраструктуры: создать, изменить, удалить облачные объекты через API провайдера. Причём для нескольких провайдеров сразу, если нужно. Нас интересовал один провайдер, но концепция показалась правильной.

Как это выглядит

Конфигурация Terraform - это .tf-файлы в HCL (HashiCorp Configuration Language). Синтаксис похож на упрощённый JSON с возможностью комментариев и интерполяции переменных. Объявляешь провайдера, потом описываешь ресурсы:

provider "openstack" {
  auth_url    = "${var.os_auth_url}"
  tenant_name = "${var.os_tenant}"
  user_name   = "${var.os_user}"
  password    = "${var.os_password}"
}

resource "openstack_compute_instance_v2" "app" {
  name            = "app-01"
  image_name      = "Ubuntu 14.04"
  flavor_name     = "m1.small"
  key_pair        = "deploy"
  security_groups = ["default", "app-servers"]
}

resource "openstack_networking_floatingip_v2" "app_fip" {
  pool = "external"
}

resource "openstack_compute_floatingip_associate_v2" "app_fip" {
  floating_ip = "${openstack_networking_floatingip_v2.app_fip.address}"
  instance_id = "${openstack_compute_instance_v2.app.id}"
}

output "app_ip" {
  value = "${openstack_networking_floatingip_v2.app_fip.address}"
}

Terraform сам разбирается с порядком создания: понимает что floating IP надо привязать после того, как создан инстанс, потому что видит зависимость через интерполяцию.

Три вещи, которые понравились сразу

Первое - terraform plan. Перед применением изменений Terraform показывает что именно будет создано, изменено или удалено. Зелёный плюс, жёлтая тильда, красный минус. Очень трудно ошибиться незаметно. Мы сделали правилом: никакого apply без предварительного plan и его ревью. Кажется очевидным, но без этого инструмента такой дисциплины не было.

Второе - state-файл как единственная истина. Terraform хранит текущее состояние инфраструктуры в terraform.tfstate - JSON-файл с описанием всех созданных ресурсов и их ID в облаке. Без него Terraform не знает что уже существует. Мы положили state в git вместе с конфигурацией. Простое решение с очевидными ограничениями (только один человек работает с инфраструктурой одновременно), но для нашего масштаба достаточно. По крайней мере, история изменений есть.

Третье - идемпотентность. Запустить plan или apply дважды подряд безопасно. Если состояние соответствует конфигурации - Terraform ничего не делает и честно сообщает об этом. Это снимает страх «а вдруг что-то лишнее пересоздастся».

Что не так с state в git

Честно говоря, решение класть terraform.tfstate в git работает, но с оговорками.

State содержит чувствительные данные - например, пароли от баз данных, если вы создаёте RDS-инстансы через Terraform. В нашем случае секретов в state не было, но это надо проверять заранее. .gitignore для state - тоже не выход, потому что тогда теряешь версионирование.

Параллельная работа двух инженеров с одним state - источник конфликтов и потенциального рассинхрона. Решение - remote state через S3 или consul, но это дополнительная инфраструктура. Пока обходимся договорённостью «один рабочий в одно время».

Как мы организовали структуру

Для одного окружения файлы разложили так:

infra/
  main.tf          # основные ресурсы
  variables.tf     # объявление переменных
  outputs.tf       # выходные значения
  terraform.tfvars # значения переменных (в .gitignore - там облачные credentials)
  terraform.tfstate
  terraform.tfstate.backup

Credentials облачного провайдера - в terraform.tfvars, который не коммитится. Структура ресурсов - в main.tf. Получается документация инфраструктуры в виде кода: открываешь репозиторий и видишь что запущено.

Ansible рядом с Terraform

Terraform создаёт виртуальные машины, Ansible их конфигурирует. Это разные инструменты для разных задач, и они хорошо дополняют друг друга.

После terraform apply у нас есть IP-адреса новых серверов через terraform output. Дальше передаём их в Ansible-инвентарь и запускаем playbook-и. Автоматизировать склейку между двумя инструментами можно, но пока это ручной шаг - скопировать output и вставить в inventory. Некрасиво, но работает.

Что ещё не попробовали

Terraform умеет в модули - переиспользуемые блоки конфигурации. Мы пока всё держим в одном main.tf, потому что окружение небольшое. При росте числа ресурсов модули будут нужны.

Remote state с блокировкой - тоже не трогали. Consul или S3 для хранения state решают проблему параллельной работы, но добавляют зависимость. Отложили до момента когда проблема станет реальной, а не гипотетической.

Где сейчас

На тестовом окружении всё работает. Вся инфраструктура описана кодом, история изменений в git, воспроизведение окружения занимает минуты вместо часа в консоли провайдера. Puppet для конфигурирования серверов остался, Terraform занял место рядом - для provisioning облачных ресурсов.

Переносить на боевые окружения пока не торопимся - хотим набрать уверенности на тестовом. Но уже понятно что концепция работает и на текущие задачи инструмента хватает.

Контакт

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

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