Новости

[Перевод] Как с помощью Terraform создавать различные окружения

Применяя Terraform, действуйте по принципу “не повторяйся” (DRY) при создании инфраструктуры в различных средах/регионах/облачных провайдерах

Terraform упростил способ организации инфраструктуры в облаке и управления ею в виде кода. Но лучшие практики, такие как разделение инфраструктуры в соответствии с несколькими типами окружения (staging / QA / production. стейджинг / тестирование и обеспечение качества / продакшн), не меняются. Возможно, для потребностей вашего бизнеса, необходимо распространить инфраструктуру на несколько географических областей. Или вы задумываетесь о применении стратегии мультиоблачных вычислений.

Для решения такой ситуации надо суметь прописать несколько различных типов используемого окружения в коде. Задача состоит в том, чтобы максимально факторизировать код в соответствии с принципом DRY (Don’t Repeat Yourself. Не повторяйся). Существует множество способов добиться этого с помощью Terraform.

В этой статье мы рассмотрим две стратегии для достижения этой цели с помощью Terraform. У каждой из них есть свои сильные и слабые стороны, в конце мы сравним их. Начнем!

2 cтратегии для создания различных сред

В обеих представленных стратегиях используются модули, для удобства включенные в проекты. Они могут быть версионированы в отдельных GIT-репозиториях. Каждый модуль вызывается в определенном слое, а удаленные состояния terraform хранятся в версионированном бэкенде S3. Разделенные слои улучшают согласованность и облегчают возможность отката.

1# Разделенные каталоги

Иерархия проектов с различными средами на основе разделенных каталогов

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

terraform {
  backend "s3" {
    bucket = "terraform-remote-states"
    # The key definition changes following the environment
    key    = "environments/staging/network.tf"
    region = "us-east-1"
  }
}

module "network" {
  source               = "../modules/network"
  region               = "us-east-1"
  network_cidr         = "10.0.0.0/16"
  private_subnet_cidrs = ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24"]
  public_subnet_cidrs  = ["10.0.3.0/24", "10.0.4.0/24", "10.0.5.9/24"]
}

Как уже упоминалось, мы используем бэкэнд для сохранения удаленных состояний. Определение ключа, т.е. место хранения удаленного состояния, зависит от среды и слоя. В модульном блоке различные значения могут меняться в связи со средой.

Зависимости между слоями решаются с помощью источников данных. Можно использовать существующие ресурсы в облаке или получать данные из удаленных состояний:

data "terraform_remote_state" "network" {
  backend = "s3"

  config = {
    bucket = "terraform-remote-states"
    key    = "environments/staging/network.tf"
    region = "us-east-1"
  }
}

locals {
  vpc_id = data.terraform_remote_state.network.outputs.vpc_id
}

Для деплоя необходимо выполнить terraform init и terraform apply в следующем порядке:

$ terraform -chdir="./environments/staging/network" init
$ terraform -chdir="./environments/staging/network" apply
$ terraform -chdir="./environments/staging/database" init
$ terraform -chdir="./environments/staging/database" apply
$ terraform -chdir="./environments/staging/application" init
$ terraform -chdir="./environments/staging/application" apply

2# Рабочие пространства

Иерархия проектов с различными средами на основе рабочих пространств (workspaces)
Иерархия проектов с различными средами на основе рабочих пространств (workspaces)

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

Используя эту возможность, мы определим несколько окружений. Каждое из них будет воркспейсом. В определении бэкенда добавим параметр workspace_key_prefix. Он относится к бэкенду S3 и определяет путь к состоянию S3 как /workspace_key/workspace_key_prefix/workspace_name:

terraform {
  backend "s3" {
    bucket               = "terraform-remote-states"
    workspace_key_prefix = "environments"
    key                  = "network"
    region               = "us-east-1"
  }
}

Удаленные состояния в S3 будут выглядеть следующим образом:

[Перевод] Как с помощью Terraform создавать различные окружения

Сетевой уровень остается таким же, как и раньше:

terraform {
  backend "s3" {
    bucket = "terraform-remote-states"
    # The key definition changes following the environment
    key    = "environments/staging/network.tf"
    region = "us-east-1"
  }
}

module "network" {
  source               = "../modules/network"
  region               = "us-east-1"
  network_cidr         = "10.0.0.0/16"
  private_subnet_cidrs = ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24"]
  public_subnet_cidrs  = ["10.0.3.0/24", "10.0.4.0/24", "10.0.5.9/24"]
}

Воркспейс управляется командой Terraform. Первым шагом является создание нового воркспейса:

$ terraform -chdir="./network" workspace new staging

Затем вы должны выбрать его:

$ terraform -chdir="./network" workspace select staging

Каталог vars, представленный на диаграмме выше, содержит файлы переменных для настройки окружения. Неплохое решение, если применить следующую конфигурацию:

$ terraform init -chdir="./network" 
$ terraform apply -chdir="./network" -var-file="./vars/staging.tfvars"

Другой способ — использовать вместо этого locals и поиграть с terraform.workspace :

terraform {
  backend "s3" {
    bucket               = "terraform-remote-states"
    workspace_key_prefix = "environments"
    key                  = "network"
    region               = "us-east-1"
  }
}

variable "network_cidr" {
  type    = list(string)
  default = {
    staging    = "10.0.0.0/16"
    qa         = "10.1.0.0/16"
    production = "10.2.0.0/16"
  }
}

variable "private_subnet_cidrs" {
  type = list(string)
  default = {
    staging    = ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24"]
    qa         = ["10.1.0.0/24", "10.1.1.0/24", "10.1.2.0/24"]
    production = ["10.2.0.0/24", "10.2.1.0/24", "10.2.2.0/24"]
  }
}

variable "public_subnet_cidrs" {
  type = list(string)
  default = {
    staging    = ["10.0.3.0/24", "10.0.4.0/24", "10.0.5.0/24"]
    qa         = ["10.1.3.0/24", "10.1.4.0/24", "10.1.5.0/24"]
    production = ["10.2.3.0/24", "10.2.4.0/24", "10.2.5.0/24"]
  }
}

locals {
  network_cidr          = lookup(var.network_cidr, terraform.workspace, null)
  private_subnet_cidrs  = lookup(var.private_subnet_cidrs, terraform.workspace, null)
  public_subnet_cidrs   = lookup(var.public_subnet_cidrs, terraform.workspace, null)
}

module "network" {
  source                = "../modules/network"
  region                = "us-east-1"
  network_cidr          = local.network_cidr
  private_subnet_cidrs  = local.private_subnet_cidrs
  public_subnet_cidrs   = local.public_subnet_cidrs
}

Сравнение стратегий

Разделенные каталоги

Плюсы:

  • Среды четко разделены и идентифицируемы

  • Больше гранулярности: можно настраивать слои окружения

  • Меньше шансов применить конфигурацию в неподходящем окружении

Минусы:

  • Необходимость дублировать часть файловой структуры для создания нового окружения

  • Несколько уровней каталогов в проекте

Рабочие пространства (воркспейсы)

Плюсы:

  • Масштабируемость с повторяющимися окружениями

  • Простота

Минусы:

  • Больше вероятности совершить ошибку, выбрав не то рабочее пространство

  • Кастомизация слоя окружения менее очевидна

Заключение

Не существует единого решения для управления несколькими окружениями в проекте Terraform. Два рассмотренных нами подхода имеют свои достоинства, и недостатки. Все зависит от ваших ожиданий.

Нужно ли вам быстро масштабироваться или темпы создания окружений более растянуты во времени? Хотите ли вы иметь файловую изоляцию между окружениями или рассчитываете на механизм абстракции рабочего пространства?

В любом случае, вы можете компенсировать недостатки этого способа другими решениями. Используя пайплайн непрерывного развертывания, вы можете значительно сократить количество ошибок, которые связаны с выбором неверного рабочего пространства. Создание нового окружения в файловой структуре можно генерировать «на лету», используя шаблонизацию.

Кроме того, рассмотренные нами два метода не являются исчерпывающими. Вы можете вдохновиться ими и создать новый, который лучше всего подходит для вашего случая использования.


Материал подготовлен в рамках курса «Infrastructure as a code». Если вам интересно узнать подробнее о формате обучения и программе, познакомиться с преподавателем курса — приглашаем на день открытых дверей онлайн. Регистрация здесь.

Добавить комментарий

Кнопка «Наверх»