Terraform. Изолирование сред через Workspaces с использованием AWS S3 Bucket и DynamoDB

Каждый раз, когда вы запускаете Terraform, создается файл состояния - terraform.tfstate. В него записывается вся инфраструктура, которую вы создали. Например, когда вы запустите Terraform в каталоге /my/terraform, будет создан файл /my/terraform/terraform.tfstate.

В этой статье разберемся зачем нужны файлы terraform.tfstate, затем узнаем, что такое Workspaces (воркспейсы, рабочие пространства) и зачем они нужны. И наконец, все это задеплоим.
Содержимое этого файла хранится в формате JSON, в котором записывается сопоставление ресурсов Terraform в ваших файлах конфигурации с представлением этих ресурсов в реальном мире.

К примеру, напишем такой код в main.tf:
resource "aws_instance" "example" {
    ami = "ami-0c55b159cbfafe1f0"
    instance_type = "t2.micro"
}

После того, как будет запущен terraform apply создастся файл terraform.tfstateс таким содержимым:
{
    "version": 4,
    "terraform_version": "0.12.0",
    "serial": 1,
    "lineage": "1f2087f9-4b3c-1b66-65db-8b78faafc6fb",
    "outputs": {},
    "resources": [
        {
            "mode": "managed",
            "type": "aws_instance",
            "name": "example",
            "provider": "provider.aws",
            "instances": [
                {
                    "schema_version": 1,
                    "attributes": {
                    "ami": "ami-0c55b159cbfafe1f0",
                    "availability_zone": "us-east-2c",
                    "id": "i-00d689a0acc43af0f",
                    "instance_state": "running",
                    "instance_type": "t2.micro",
                    "(...)": "(truncated)"
                    }
                }
            ]
        }
    ]
}

Используя JSON, Terraform знает, что ресурс aws_instance и имя example относятся к EC2 инстансу в AWS акаунте с ID i-00d689a0acc43af0f.

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

Если вы используете Terraform для личного проекта, хранение состояния в одном terraform.tfstate на вашем компьютере - это нормальная практика. Но если вы будете использовать Terraform в продакшене, вы столкнетесь с несколькими проблемами:

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

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

Изоляция
При внесении изменений в инфраструктуру рекомендуется изолировать различные среды. Например, при внесении изменений в среду test или среду stage вы должны быть уверены, что работа в среде prod не нарушится.

Ниже пройдемся по каждому пункту.

Общее хранилище файлов tfstate

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

Удаленные бэкенды решают такие проблемы:

Человеческий фактор
После того, как будет настроен удаленный бэкенд, Terraform будет автоматически загружать файл состояния из этого бэкенда каждый раз, когда вы будете запускать terraform plan или terraform apply.

Блокировка
Запустив terraform apply, Terraform автоматически включит блокировку. И если в это время кто-то уже запустил terraform apply, то вам придется ждать. Но вы можете запустить terraform apply с параметром -lock-timeout=<TIME>, чтобы Terraform подождал заданное время. Пример: -lock-timeout=10m сообщит Terraform подождать 10 минут.

Шифрование секретов
Удаленные бэкенды изначально поддерживают шифрование при передаче и шифрование самого файла состояния. Более того, эти бэкенды предоставляют способы настройки разрешений доступа (например, использование политик IAM с Amazon S3 Bucket), чтобы вы могли контролировать, кто имеет доступ к вашим файлам состояния и секретам, которые они могут содержать.

Amazon S3 (Simple Storage Service)


Amazon S3 - лучший выбор в качестве удаленного бэкенда:
  • Данные будут зашифрованы с использованием AES-256, а для чтения и записи данных между Terraform и Amazon S3 используется SSL.
  • Поддерживается блокировка через DynamoDB.
  • Поддерживается управление версиями, поэтому каждая ревизия файла состояния сохраняется, и можно откатиться к более старой версии, если что-то пойдет не так. 

Создание корзины в Amazon S3

Чтобы включить хранилище с удаленным состоянием в Amazon S3, давайте создадим корзину S3.

Создайте файл main.tf и вставьте в него код (как всё это работает подробно прокомментировано в коде):

#############################################################################
# КАКОЙ ИСПОЛЬЗУЕТСЯ ПРОВАЙДЕР И РЕГИОН
#############################################################################

provider "aws" {
    region = "us-east-2"
}

#############################################################################
# Amazon S3 Bucket
#############################################################################

# Создадим ресурс "aws_s3_bucket" и дадим ему имя "terraform_state"
# Дадим глобально-уникальное имя корзине (bucket). Имя должно быть уникальным на весь интернет
resource "aws_s3_bucket" "terraform_state" {
    bucket = "yatakoi-terraform-state"

    # Защита от случайного удаления S3 bucket
    # Даже командой terraform destroy невозможно будет удалить эту корзину
    # Если потребуется ее удалить, то просто закомментируйте эти строки
    lifecycle {
        prevent_destroy = true
       }

    # Включить версионирование, чтобы мы могли видеть полную историю ревизий файлов состояния
    # При каждом деплое будет создаваться навая версия файла состояния
    # Таким образом можно легко будет откатиться на нужную версию
    versioning {
        enabled = true
    }
    
    # Включить шифрование на стороне Amazon
       server_side_encryption_configuration {
        rule {
            apply_server_side_encryption_by_default {
            sse_algorithm = "AES256"
            }
        }
    }
}

#############################################################################
# Amazon DynamoDB - это распределенное хранилище ключей и значений
# Он поддерживает строго согласованные операции чтения и условной записи,
#  которые являются всеми компонентами, необходимыми для системы распределенной блокировки
#############################################################################

# В DynamoDB создадим таблицу "terraform_locks" с первичным ключом "LockID" для использования блокировки
# 
resource "aws_dynamodb_table" "terraform_locks" {
    name = "yatakoi-terraform-state-locks"
    billing_mode = "PAY_PER_REQUEST"
    hash_key = "LockID"
    
    attribute {
        name = "LockID"
        type = "S"
    }
}

Теперь можно выполнить команду terraform init, чтобы Terraform загрузил провайдера AWS. И после этого можно выполнить terraform apply, чтобы задеплоить эту конфигурацию в AWS.

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

[root@centos-2gb-nbg-vpn aws]# terraform init

Initializing the backend...

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "aws" (hashicorp/aws) 2.43.0...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.aws: version = "~> 2.43"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
[root@centos-2gb-nbg-vpn aws]# terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_dynamodb_table.terraform_locks will be created
  + resource "aws_dynamodb_table" "terraform_locks" {
      + arn              = (known after apply)
      + billing_mode     = "PAY_PER_REQUEST"
      + hash_key         = "LockID"
      + id               = (known after apply)
      + name             = "yatakoi-terraform-state-locks"
      + stream_arn       = (known after apply)
      + stream_label     = (known after apply)
      + stream_view_type = (known after apply)

      + attribute {
          + name = "LockID"
          + type = "S"
        }

      + point_in_time_recovery {
          + enabled = (known after apply)
        }

      + server_side_encryption {
          + enabled = (known after apply)
        }
    }

  # aws_s3_bucket.terraform_state will be created
  + resource "aws_s3_bucket" "terraform_state" {
      + acceleration_status         = (known after apply)
      + acl                         = "private"
      + arn                         = (known after apply)
      + bucket                      = "yatakoi-terraform-state"
      + bucket_domain_name          = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + region                      = (known after apply)
      + request_payer               = (known after apply)
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + server_side_encryption_configuration {
          + rule {
              + apply_server_side_encryption_by_default {
                  + sse_algorithm = "AES256"
                }
            }
        }

      + versioning {
          + enabled    = true
          + mfa_delete = false
        }
    }

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_s3_bucket.terraform_state: Creating...
aws_dynamodb_table.terraform_locks: Creating...
aws_dynamodb_table.terraform_locks: Creation complete after 7s [id=yatakoi-terraform-state-locks]
aws_s3_bucket.terraform_state: Creation complete after 7s [id=yatakoi-terraform-state]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
[root@centos-2gb-nbg-vpn aws]#

Казалось бы всё, но после деплоя у вас будет корзина S3 и таблица DynamoDB, а вот состояние Terraform все равно будет храниться локально. 

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

terraform {
    backend "<ИМЯ_БЭКЕНДА>" {
        [КОНФИГ...]
    }
}

Где:
  • <ИМЯ_БЭКЕНДА> - имя бэкенда, который вы уже создали. У меня это yatakoi-terraform-state.
  • [КОНФИГ...] - аргументы для созданной S3 корзины.
Давайте теперь добавим конфигурацию S3 бэкенда в конец файла main.tf:
# Сообщаем самому Terraform, где хранится удаленный бэкенд
terraform {
    backend "s3" {
        # Имя корзины, которое определили в самом начале
        bucket = "yatakoi-terraform-state"
        # Путь к файлу состояния Terraform
        key = "global/s3/terraform.tfstate"
        region = "us-east-2"
        
        # Имя таблицы в DynamoDB, которое определили в самом начале
        dynamodb_table = "yatakoi-terraform-state-locks"
        encrypt = true
    }
}

Чтобы поручить Terraform сохранить файл состояния в корзине yatakoi-terraform-state, необходимо снова запустить terraform init. Эта команда не только  загружает код провайдера, но и конфигурирует бэкэнд Terraform'а.

После того как вы добавите вышенаписанный код в конец файла, достаточно будет выполнить команду terraform init, затем ввести "yes" и состояние Terraform'а окажется в корзине Amazon S3.

[root@centos-2gb-nbg-vpn aws]# terraform init

Initializing the backend...
Acquiring state lock. This may take a few moments...
Do you want to copy existing state to the new backend?
  Pre-existing state was found while migrating the previous "local" backend to the
  newly configured "s3" backend. No existing state was found in the newly
  configured "s3" backend. Do you want to copy this state to the new "s3"
  backend? Enter "yes" to copy and "no" to start with an empty state.

  Enter a value: yes

Releasing state lock. This may take a few moments...

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

Initializing provider plugins...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.aws: version = "~> 2.43"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
[root@centos-2gb-nbg-vpn aws]#

Далее идём на AWS S3 https://s3.console.aws.amazon.com/s3/home?region=us-east-2и убеждаемся в этом:
Можно увеличить по клику

При включенном бэкенде Terraform автоматически извлечет последнее состояние из этого S3 перед выполнением команды и автоматически отправит последнее состояние в S3 после выполнения команды. 

Чтобы увидеть это в действии, добавьте в конец файла следующие выходные переменные:
#############################################################################
# ВЫХОДНЫЕ ПЕРЕМЕННЫЕ
#############################################################################

output "s3_bucket_arn" {
    value = aws_s3_bucket.terraform_state.arn
    description = "ARN(Amazon Resource Name) S3 Bucket"
}

output "dynamodb_table_name" {
    value = aws_dynamodb_table.terraform_locks.name
    description = "Таблица DynamoDB"
}

Запустите terraform apply и получите результат:
[root@centos-2gb-nbg-vpn aws]# terraform apply
Acquiring state lock. This may take a few moments...
aws_s3_bucket.terraform_state: Refreshing state... [id=yatakoi-terraform-state]
aws_dynamodb_table.terraform_locks: Refreshing state... [id=yatakoi-terraform-state-locks]

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Releasing state lock. This may take a few moments...

Outputs:

dynamodb_table_name = yatakoi-terraform-state-locks
s3_bucket_arn = arn:aws:s3:::yatakoi-terraform-state
[root@centos-2gb-nbg-vpn aws]#

А теперь снова зайдите на AWS S3 https://s3.console.aws.amazon.com/s3/home?region=us-east-2 и вы увидите, что после того как вы сделали terraform apply появилась Latest version файла состояния, а прошлая версия сместилась вниз.


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

Ограничения с бэкендами Terraform

Бэкэнды Terraform имеют несколько ограничений и ошибок, о которых вам нужно знать.

Первое ограничение - это ситуация с использованием Terraform для создания корзины S3, в которой вы хотите сохранить состояние Terraform. Чтобы сделать это, вы должны были использовать двухэтапный процесс:
  1. Написать код Terraform, чтобы создать корзину S3 и таблицу DynamoDB, затем развернуть этот код с локальным бэкэндом.
  2. Вернуться к коду Terraform, добавить к нему конфигурацию удаленного бэкэнда, чтобы использовать только что созданную корзину S3 и таблицу DynamoDB, и запустить terraform init, чтобы скопировать локальное состояние в S3.
Если вы захотите удалить корзину S3 и таблицу DynamoDB, вам нужно будет выполнить этот двухэтапный процесс в обратном порядке:
  1. Перейти к коду Terraform, удалить внутреннюю конфигурацию и повторно запустить terraform init, чтобы скопировать состояние Terraform обратно на локальный диск.
  2. Запустить terraform destroy, чтобы удалить корзину S3 и таблицу DynamoDB.
Второе ограничение более болезненно: бэкенд-блок в Terraform не позволит вам использовать какие-либо переменные или ссылки.

Это означает, что вам нужно вручную скопировать и вставить имя корзины S3, регион, имя таблицы DynamoDB и т.д. в каждый из ваших модулей Terraform. Хуже того, вы должны очень тщательно не копировать и вставлять значение ключа (key = "global/s3/terraform.tfstate"), но обеспечить уникальный ключ для каждого развернутого вами модуля Terraform, чтобы случайно не перезаписать состояние какого-либо другого модуля! Необходимость делать много копий и вставок и множество ручных изменений подвержена ошибкам, особенно если вам необходимо развернуть и управлять многими модулями Terraform во многих средах.

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

# backend.hcl
bucket = "yatakoi-terraform-state"
region = "us-east-2"
dynamodb_table = "yatakoi-terraform-state-locks"
encrypt = true

В коде Terraform остается только ключевой параметр, так как вам все равно нужно установить другое значение ключа для каждого модуля:
# Частичная конфигурация. Настройки (например, bucket, region) 
#  будут переданы из файла через аргументы -backend-config в 'terraform init'
terraform {
    backend "s3" {
        key = "global/s3/terraform.tfstate"
    }
}

Чтобы собрать все ваши частичные конфигурации, запустите terraform init с аргументом -backend-config:
$ terraform init -backend-config=backend.hcl

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

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

Подробнее о Terragrunt на оф. сайте: https://terragrunt.gruntwork.io/

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

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

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

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

Есть два способа изолировать файлы состояний:

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

Изоляция через File Layout
Полезно для производственных случаев использования, для которых вам необходимо сильное разделение между средами.

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

Рабочие пространства Terraform позволяют хранить состояние Terraform в нескольких отдельных рабочих пространствах. Terraform начинается с одного рабочего пространства, называемого «default», и если вы никогда не указываете рабочее пространство явно, рабочим пространством по умолчанию является то, которое вы будете использовать все время. Чтобы создать новое рабочее пространство или переключаться между рабочими пространствами, есть команда terraform workspace.

Давайте поэкспериментируем с рабочими пространствами для некоторого кода Terraform, в котором используется один инстанс EC2.

Создайте каталог example. Это и будет наше тестовое рабочее пространство. В этом каталоге создайте файл main.tf и добавьте код ниже:

resource "aws_instance" "example" {
    ami = "ami-0c55b159cbfafe1f0"
    instance_type = "t2.micro"
}

Далее, сконфигурируйте бэкенд для этого инстанса, используя корзину S3 и таблицу DynamoDB, которые вы создали ранее, но с ключом workspaces-example/terraform.tfstate. В прошлой конфигурации в файле main.tf у нас была корзина с ключом global/s3/terraform.tfstate, а теперь её ключ должен быть workspaces-example/terraform.tfstate:

# Сообщаем самому Terraform, где хранится удаленный бэкенд
terraform {
    backend "s3" {
        # Имя корзины, которое определили в самом начале
        bucket = "yatakoi-terraform-state"
        # Путь к файлу состояния Terraform
        key = "workspaces-example/terraform.tfstate"
        region = "us-east-2"
        
        # Имя таблицы в DynamoDB, которое определили в самом начале
        dynamodb_table = "yatakoi-terraform-state-locks"
        encrypt = true
    }
}

Теперь сделаем terraform init и terraform apply:

$ 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.

Состояние этого деплоя хранится в рабочей области default. Выполните terraform workspace show, которая покажет текущее рабочее пространство:

[root@centos-2gb-nbg-vpn example]# terraform workspace show
default

Рабочая область default хранит состояние именно в том месте, которое вы указали в key для корзины S3.

Увеличить по клику

Давайте теперь создадим воркспейс с именем "example1":

[root@centos-2gb-nbg-vpn example]# 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.

Теперь обратите внимание, что произойдет, если вы попытаетесь запустить terraform plan:

[root@centos-2gb-nbg-vpn example]# terraform plan
Acquiring state lock. This may take a few moments...
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.example will be created
  + resource "aws_instance" "example" {
      + ami                          = "ami-0c55b159cbfafe1f0"
      + arn                          = (known after apply)
      + associate_public_ip_address  = (known after apply)
      + availability_zone            = (known after apply)
      + cpu_core_count               = (known after apply)
      + cpu_threads_per_core         = (known after apply)
      + get_password_data            = false
      + host_id                      = (known after apply)
      + id                           = (known after apply)
( ... )


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

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

Releasing state lock. This may take a few moments...
[root@centos-2gb-nbg-vpn example]#

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

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

[root@centos-2gb-nbg-vpn example]# terraform apply
(...)
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Еще раз создадим воркспейс, но с именем "example2" и запустим terraform apply:

[root@centos-2gb-nbg-vpn example]# 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.

[root@centos-2gb-nbg-vpn example]# terraform apply
(...)
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Теперь у вас есть три доступных воркспейса, которые вы можете увидеть командой terraform workspace list:

[root@centos-2gb-nbg-vpn example]# terraform workspace list
  default
  example1
* example2

Звездочкой помечен воркспейс, который сейчас используется.

Выбрать воркспейс:

[root@centos-2gb-nbg-vpn example]# terraform workspace select example1
Switched to workspace "example1".

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

Увеличить по клику




В env: вы увидите свои воркспейсы с именами example1 и example2.

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

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

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

resource "aws_instance" "example" {
    ami = "ami-0c55b159cbfafe1f0"
    instance_type = terraform.workspace == "default" ? "t2.medium" : "t2.micro"
}

В предыдущем коде используется троичный синтаксис, чтобы условно установить для instance_type значение t2.medium или t2.micro, в зависимости от значения terraform.workspace.

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

• Файлы состояния для всех воркспейсов хранятся в одном и том же бэкенде (например, в том же сегменте S3). Это означает, что вы используете одинаковые средства проверки подлинности и контроля доступа для всех воркспейсов, что является одной из основных причин того, что воркспейсы являются неподходящим механизмом для изоляции сред (например, изолирование от Prod).

• Воркспейсы не видны в коде или на терминале, если вы не запускаете terraform workspace. При просмотре кода модуль, развернутый в одном ворксейсе, выглядит точно так же, как модуль, развернутый в 10 воркспейсах. Это усложняет обслуживание, поскольку у вас нет хорошего представления о вашей инфраструктуре.

• Соединение двух предыдущих элементов приводит к тому, что воркспейсы могут быть подвержены ошибкам. Отсутствие видимости позволяет легко забыть, в каком воркспейсе вы находитесь, и случайно внести изменения в неправильное (например, случайно запустить terraform destroy в воркспейсе Prod, а не в Stage), а также потому, что вы должны использовать один и тот же механизм аутентификации для всех ворксейсов.

Чтобы обеспечить надлежащую изоляцию между средами, а не воркспейсами, вы, скорее всего, захотите использовать макет файла, который является темой следующего раздела. Но прежде чем двигаться дальше, обязательно очистите три только что развернутых экземпляра EC2, запустив terraform workspace select <имя> и затем terraform destroy в каждом из трех воркспейсов.

Комментариев 2

  1. Офлайн
    Сергій
    Сергій 23 марта 2020 15:45
    + 0 -
    Привіт ! Хороша стаття, дякую ! Я саме зараз з нуля піднімаю проєкт з нуля і мені треба якраз правильно розділити з самого початку бекенд на дев, стейдж і прод, тому поділись інформацією чи додай статтю чи силку на "вляется темой следующего раздела." :)

    1. Офлайн
      yatakoi 25 марта 2020 03:44
      + +2 -
      Привет )

      Для этого советую купить книгу Terraform Up & Running, но обязательно 2-ю редакцию, а то в 1-й код давно устарел. Там все четко разжевано по твоему вопросу. Книга естественно на английском.

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