Terraform: инфраструктура на уровне кода — страница 20 из 65

ких модулей. Что еще хуже, вам нужно быть очень осторожными, чтобы не скопировать значение key. Чтобы разные модули случайно не перезаписывали состояние друг друга, это значение должно быть уникальным для каждого из них! Частое копирование и ручное редактирование чреваты ошибками, особенно если нужно развертывать и администрировать множество модулей Terraform во многих средах.

По состоянию на май 2019 года единственным решением является использование частичной конфигурации, в которой можно опустить определенные параметры раздела backend и передавать их вместо этого в аргументе командной строки -backend-config при вызове terraforminit. Например, вы можете вынести повторяющиеся параметры хранилища, такие как bucket и region, в отдельный файл под названием backend.hcl:

# backend.hcl

bucket         = "terraform-up-and-running-state"

region         = "us-east-2"

dynamodb_table = "terraform-up-and-running-locks"

encrypt        = true

В коде Terraform останется только параметр key, поскольку вам все равно нужно устанавливать ему разные значения в разных модулях:

# Частичная конфигурация. Другие параметры (такие как bucket, region) будут

# переданы команде 'terraform init' в виде файла с использованием

# аргументов -backend-config

terraform {

  backend "s3" {

    key = "example/terraform.tfstate"

  }

}

Чтобы собрать воедино все фрагменты вашей конфигурации, выполните команду terraforminit с аргументом -backend-config:

$ terraform init -backend-config=backend.hcl

Terraform объединит частичную конфигурацию из файла backend.hcl и вашего кода Terraform, чтобы получить полный набор параметров для вашего модуля.

Еще один вариант заключается в применении Terragrunt, инструмента с открытым исходным кодом, который пытается компенсировать то, чего не хватает в Terraform. Terragrunt может помочь избежать дублирования базовых параметров хранилища (имя и регион бакета, имя таблицы DynamoDB) за счет определения их в едином файле и автоматического применения относительного файлового пути модуля в качестве значения key. Пример с Terragrunt будет показан в главе 8.


Изоляция файлов состояния

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

Например, при попытке развертывания новой версии своего приложения в среде финального тестирования вы можете нарушить его работу в промышленных ­условиях. Или еще хуже, вы можете повредить весь файл состояния (скажем, из-за отсутствия блокирования либо из-за редкой программной ошибки в Terraform), в результате чего ваша инфраструктура выйдет из строя во всех средах38.

Весь смысл поддержки нескольких сред состоит в том, что они изолированы друг от друга, поэтому, если вы управляете всеми ими из одного набора конфигурационных файлов Terraform, вы нарушаете эту изоляцию. По аналогии с переборками, которые не дают затопить все секции судна из-за протечки в одной из них, вы должны предусмотреть «переборки» для своей архитектуры Terraform (рис. 3.3).

Рис. 3.3. Добавление «переборок» в архитектуру Terraform

Как проиллюстрировано на рис. 3.3, мы описываем все наши среды не в одном наборе конфигурационных файлов (вверху), а в разных наборах (внизу), поэтому проблема в одной среде полностью изолирована от других. Файлы состояния можно изолировать двумя способами.

Изоляция через рабочие области. Подходит для быстрых изолированных проверок с одной и той же конфигурацией.

• Изоляция с помощью описания структуры файлов. Подходит для промышленного использования, когда требуется строгая изоляция между средами.


Изоляция через рабочие области

Вы можете хранить свое состояние Terraform в нескольких отдельных именованных рабочих областях. У Terraform изначально одна рабочая область, которая используется по умолчанию. Чтобы создать новую рабочую область или переключиться между областями, нужно выполнить команду terraformworkspace. Поэкспериментируем с неким кодом Terraform, который развертывает сервер EC2:

resource "aws_instance" "example" {

  ami           = "ami-0c55b159cbfafe1f0"

  instance_type = "t2.micro"

}

Сконфигурируйте для этого сервера хранилище на основе бакета S3 и таблицы DynamoDB, которые вы создали ранее в этой главе, но в качестве значения для key укажите workspaces-example/terraform.tfstate:

terraform {

  backend "s3" {

    # Поменяйте это на имя своего бакета!

    bucket = "terraform-up-and-running-state"

    key    = "workspaces-example/terraform.tfstate"

    region = "us-east-2"

    # Замените это именем своей таблицы DynamoDB!

    dynamodb_table = "terraform-up-and-running-locks"

    encrypt        = true

  }

}

Выполните команды terraforminit и terraformapply, чтобы развернуть этот код:

$ terraform init

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes.

Initializing provider plugins...

(...)

Terraform has been successfully initialized!

$ terraform apply

(...)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

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

$ terraform workspace show

default

Рабочая область по умолчанию хранит ваше состояние именно в том месте, которое вы указали в параметре key. Как видно на рис. 3.4, взглянув на свой бакет S3, вы увидите файл terraform.tfstate и папку workspaces-example.

Рис. 3.4. Бакет S3 после сохранения состояния в рабочей области по умолчанию

Создадим новую рабочую область над названием example1, используя команду terraformworkspacenew:

$ terraform workspace new example1

Created and switched to workspace "example1"!

You're now on a new, empty workspace. Workspaces isolate their state, so if you run "terraform plan" Terraform will not see any existing state for this configuration.

Теперь посмотрите, что получится, если мы попытаемся выполнить terraformplan:

$ terraform plan

Terraform will perform the following actions:

# aws_instance.example will be created

    + resource "aws_instance" "example" {

    + ami                    = "ami-0c55b159cbfafe1f0"

    + instance_type          = "t2.micro"

    (...)

  }

Plan: 1 to add, 0 to change, 0 to destroy.

Terraform хочет создать с нуля совершенно новый сервер EC2! Дело в том, что файлы состояния в каждой рабочей области изолированы друг от друга и, поскольку вы теперь в рабочей области example1, Terraform больше не использует файл состояния из рабочей области по умолчанию. Следовательно, не видит сервер EC2, который был там создан.

Попробуйте выполнить команду terraformapply, чтобы развернуть этот второй сервер EC2 в новой рабочей области:

$ terraform apply

(...)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Повторим этот пример еще один раз и создадим новую рабочую область под названием example2:

$ terraform workspace new example2

Created and switched to workspace "example2"!

You're now on a new, empty workspace. Workspaces isolate their state, so if you run "terraform plan" Terraform will not see any existing state for this configuration.

Снова выполните terraformapply, чтобы развернуть третий сервер EC2:

$ terraform apply

(...)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Теперь у вас есть три рабочие области; в этом можно убедиться с помощью команды terraformworkspacelist:

$ terraform workspace list

  default

  example1

* example2

Вы можете переключиться между ними в любой момент, используя команду terraformworkspaceselect:

$ terraform workspace select example1

Switched to workspace "example1".

Чтобы понять, как это работает внутри, еще раз загляните в свой бакет. Вы должны увидеть новую папку под названием env:, как показано на рис. 3.5.

Рис. 3.5. Бакет S3 после того, как вы начали использовать собственные рабочие области

Внутри env: вы найдете по одной папке для каждой из ваших рабочих областей (рис. 3.6).

Внутри каждой из этих рабочих областей Terraform использует значение key, которое вы указали в конфигурации хранилища, поэтому вы должны найти файлы example1/workspaces-example/terraform.tfstate и example2/workspaces-example/terraform.tfstate. Иными словами, переключение на другую рабочую область равнозначно изменению пути хранения вашего файла состояния.

Рис. 3.6. Terraform создает по одной папке для каждой рабочей области

Это удобно, когда вы уже развернули модуль Terraform и хотите с ним поэкспериментировать (например, попробовать изменить структуру кода), но так, чтобы ваши эксперименты не отразились на состоянии уже развернутой инфраструктуры. Вы можете выполнить команду terraformworkspacenew и развернуть новую копию той же инфраструктуры, но с отдельным файлом состояния.

Вы можете даже сделать так, чтобы этот модуль менял свое поведение в зависимости от того, в какой рабочей области вы находитесь. Для этого он может считывать имя рабочей области с помощью выражения terraform.workspace. Например, в рабочей области по умолчанию можно указать тип сервера EC2 t2.medium, а во всех остальных областях — t2.micro (чтобы, скажем, сделать свои эксперименты более экономными):