• prevent_destroy. Это второй параметр жизненного цикла, с которым вы сталкиваетесь (первым был create_before_destroy в главе 2). Если присвоить ему true, при попытке удаления соответствующего ресурса (например, при выполнении terraformdestroy) Terraform вернет ошибку. Это позволяет предотвратить случайное удаление важных ресурсов, таких как бакет S3 со всем вашим состоянием Terraform. Конечно, если вы действительно хотите его удалить, просто закомментируйте этот параметр.
• versioning. В данном разделе включается управления версиями в бакете S3, в результате чего каждое обновление хранящегося в нем файла будет создавать его новую версию. Это позволяет просматривать старые версии и откатываться к ним в любой момент.
•server_side_encryption_configuration. В этом разделе включается шифрование по умолчанию на стороне сервера для всех данных, которые записываются в бакет S3. Благодаря этому ваши файлы состояния и любые конфиденциальные данные, которые могут в них содержаться, всегда будут шифроваться при сохранении в S3.
Далее нужно создать таблицу DynamoDB, которая будет использоваться для блокирования. DynamoDB — это распределенное хранилище типа «ключ — значение» от Amazon. Оно поддерживает строго согласованное чтение и условную запись — все, что необходимо для распределенной системы блокирования. Более того, оно полностью управляемое, поэтому вам не нужно заниматься никакой дополнительной инфраструктурой, а большинство сценариев применения Terraform легко впишутся в бесплатный тарифный план37.
Чтобы использовать DynamoDB для блокирования в связке с Terraform, нужно создать таблицу с первичным ключом под названием LockID (с аналогичным написанием). Это можно сделать с помощью ресурса aws_dynamodb_table:
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-up-and-running-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
Выполните terraforminit, чтобы загрузить код провайдера, а затем terraformapply, чтобы развернуть ресурсы. Примечание: чтобы иметь возможность развертывать этот код, у вашего пользователя IAM должны быть права на создание бакетов S3 и таблиц DynamoDB, как описано в разделе «Подготовка вашей учетной записи в AWS» на с. 60. После завершения развертывания вы получите бакет S3 и таблицу DynamoDB, но ваше состояние Terraform по-прежнему будет храниться локально. Чтобы хранить его в бакете S3 (с шифрованием и блокированием), нужно добавить в свой код раздел backend. Это конфигурация самой системы Terraform, поэтому она находится внутри блока terraform и имеет следующий синтаксис:
terraform {
backend "
[CONFIG...]
}
}
BACKEND_NAME — это имя хранилища, которое вы хотите использовать (например, "s3"), а CONFIG содержит один или несколько аргументов, предусмотренных специально для этого хранилища (скажем, имя бакета S3, который нужно использовать). Так выглядит конфигурация backend для бакета S3:
terraform {
backend "s3" {
# Поменяйте это на имя своего бакета!
bucket = "terraform-up-and-running-state"
key = "global/s3/terraform.tfstate"
region = "us-east-2"
# Замените это именем своей таблицы DynamoDB!
dynamodb_table = "terraform-up-and-running-locks"
encrypt = true
}
}
Пройдемся по этим параметрам.
•bucket. Имя нужного бакета S3. Не забудьте поменять его на название созданного ранее бакета.
• key. Файловый путь внутри бакета S3, по которому Terraform будет записывать файл состояния. Позже вы увидите, почему в предыдущем примере этому параметру присвоено global/s3/terraform.tfstate.
• region. Регион AWS, в котором находится бакет S3. Не забудьте указать регион того бакета, который вы создали ранее.
• dynamodb_table. Таблица DynamoDB, которая будет использоваться для блокирования. Не забудьте указать имя той таблицы, которую вы создали ранее.
•encrypt. Если указать true, состояние Terraform будет шифроваться при сохранении в S3. Это дополнительная мера, которая гарантирует шифрование данных во всех ситуациях, так как мы уже включили шифрование по умолчанию для S3.
Чтобы состояние Terraform сохранялось в этом бакете, нужно опять выполнить terraforminit. Эта команда не только загрузит код провайдера, но и сконфигурирует хранилище Terraform (еще одно ее применение вы увидите чуть позже). Более того, она идемпотентная, поэтому ее повторное выполнение безопасно:
$ 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:
Terraform автоматически определит, что у вас уже есть локальный файл состояния, и с вашего позволения скопирует его в новое хранилище S3. Если ввести yes, можно увидеть следующее:
Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes.
После выполнения этой команды ваше состояние Terraform будет сохранено в бакете S3. Чтобы в этом убедиться, откройте консоль управления S3 (https://amzn.to/ 2Kw5qAc) в своем браузере и выберите свой бакет. Вы должны увидеть нечто похожее на рис. 3.1.
После включения этого хранилища Terraform будет автоматически загружать последнее состояние из бакета S3 перед выполнением команды и сохранять его туда после того, как команда будет выполнена. Чтобы увидеть, как это работает, добавьте следующие выходные переменные:
output "s3_bucket_arn" {
value = aws_s3_bucket.terraform_state.arn
description = "The ARN of the S3 bucket"
}
output "dynamodb_table_name" {
value = aws_dynamodb_table.terraform_locks.name
description = "The name of the DynamoDB table"
}
Рис. 3.1. Файл состояния Terraform, хранящийся в S3
Эти переменные выведут на экран ARN (Amazon Resource Name) вашего бакета S3 и имя вашей таблицы DynamoDB. Чтобы в этом убедиться, выполните terraformapply:
$ terraform apply
Acquiring state lock. This may take a few moments...
aws_dynamodb_table.terraform_locks: Refreshing state...
aws_s3_bucket.terraform_state: Refreshing state...
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Releasing state lock. This may take a few moments...
Outputs:
dynamodb_table_name = terraform-up-and-running-locks
s3_bucket_arn = arn:aws:s3:::terraform-up-and-running-state
Заметьте, что теперь Terraform устанавливает блокировку перед запуском команды apply и снимает ее после!
Еще раз зайдите в консоль S3 по адресу https://amzn.to/2Kw5qAc, обновите страницу и нажмите серую кнопку Show (Показать) рядом с надписью Versions (Версии). На экране должно появиться несколько версий вашего файла terraform.tfstate, хранящегося в бакете S3 (рис. 3.2).
Рис. 3.2. Несколько версий состояния Terraform в S3
Это означает, что Terraform действительно загружает данные состояния в S3 и из него и ваш бакет хранит каждую ревизию файла состояния, что может пригодиться для отладки и отката к более старой версии, если что-то пойдет не так.
Ограничения хранилищ Terraform
У хранилищ Terraform есть несколько ограничений и подводных камней, о которых вам следует знать. Прежде всего, когда вы используете Terraform для создания бакета S3, в котором вы хотите хранить состояние Terraform, это похоже на ситуацию с курицей и яйцом. Чтобы этот подход работал, вам пришлось выполнить следующее.
1. Написать код Terraform, чтобы создать бакет S3 и таблицу DynamoDB, а затем развернуть этот код с использованием локального хранилища.
2. Вернуться к коду Terraform, добавить в него конфигурацию для удаленного хранилища, чтобы применить свежесозданные бакет S3 и таблицу DynamoDB, и выполнить команду terraforminit, чтобы скопировать ваше локальное состояние в S3.
Если вы когда-нибудь захотите удалить бакет S3 и таблицу DynamoDB, придется выполнить обратные действия.
1. Перейти к коду Terraform, удалить конфигурацию backend и снова выполнить команду terraforminit, чтобы скопировать состояние Terraform обратно на локальный диск.
2. Выполнить terraformdestroy, чтобы удалить бакет S3 и таблицу DynamoDB.
Этот двухступенчатый процесс довольно непростой, зато вы можете использовать во всем своем коде Terraform одни и те же бакет S3 и таблицу DynamoDB, поэтому нужно выполнить его лишь раз (или единожды для каждой учетной записи AWS, если у вас их несколько). Если у вас уже есть бакет S3, вы можете сразу указывать конфигурацию backend в своем коде Terraform без дополнительных действий.
Второе ограничение более болезненное: в разделе backend в Terraform нельзя применять никакие переменные или ссылки. Следующий код не будет работать.
# Это НЕ будет работать. В конфигурации хранилища нельзя использовать переменные.
terraform {
backend "s3" {
bucket = var.bucket
region = var.region
dynamodb_table = var.dynamodb_table
key = "example/terraform.tfstate"
encrypt = true
}
}
Значит, необходимо вручную копировать и вставлять имя и регион бакета S3, а также название таблицы DynamoDB в каждый ваш модуль Terraform. Вы подробно познакомитесь с модулями Terraform в главах 4 и 6. Пока достаточно понимать, что модули — это способ организации и повторного использования кода Terraform и что настоящий код Terraform обычно состоит из множества мел