resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}
Ниже показан небольшой фрагмент файла terraform.tfstate (урезанный, чтобы его было легче читать), который будет создан после выполнения terraformapply:
{
"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 с идентификатором i-00d689a0acc43af0f в вашей учетной записи AWS. При каждом запуске Terraform может запросить у AWS текущее состояние этого сервера и сравнить его с вашей конфигурацией, чтобы определить, какие изменения следует внести. Иными словами, вывод команды plan — это расхождение между кодом на вашем компьютере и инфраструктурой, развернутой в реальном мире (согласно идентификаторам в файле состояния).
Файл состояния является приватным API
Файл состояния — это приватный API, который меняется с каждым новым выпуском и предназначен сугубо для внутреннего использования в Terraform. Вы никогда не должны редактировать его вручную или считывать его напрямую.
Если вам по какой-то причине нужно модифицировать файл состояния (что должно быть редкостью), используйте команды terraform import или terraform state (примеры работы с ними показаны в главе 5).
Если вы используете Terraform в личном проекте, можно спокойно хранить файл terraform.tfstate локально на своем компьютере. Но если вы ведете командную разработку реального продукта, может возникнуть несколько проблем.
•Общее хранилище для файлов состояния. Чтобы обновлять инфраструктуру с помощью Terraform, у каждого члена команды должен быть доступ к одним и тем же файлам состояния. Это означает, что вам нужно хранить эти файлы в общедоступном месте.
• Блокирование файлов состояния. Разделение данных сразу же создает новую проблему: блокирование. Если два члена команды запускают Terraform одновременно, может возникнуть состояние гонки, так как обновление файлов состояния происходит параллельно со стороны двух разных процессов. Без блокирования это может привести к конфликтам, потере данных и повреждению файлов состояния.
•Изоляция файлов состояния. При изменении инфраструктуры рекомендуется изолировать разные окружения. Например, при правке состояния в среде предварительного или финального тестирования следует убедиться, что это никак не навредит промышленной системе. Но как изолировать изменения, если вся инфраструктура описана в одном и том же файле состояния Terraform?
В следующих разделах мы подробно исследуем все эти проблемы и посмотрим, как их решить.
Общее хранилище для файлов состояния
Самый распространенный метод, который позволяет нескольким членам команды работать с общим набором файлов, заключается в использовании системы управления версиями (например, Git). Хотя ваш код Terraform точно должен храниться именно таким образом, применение того же подхода к состоянию Terraform — плохая идея по нескольким причинам.
•Человеческий фактор. Вы можете легко забыть загрузить последние изменения из системы управления версиями перед запуском Terraform или сохранить свои собственные обновления постфактум. Рано или поздно кто-то в вашей команде случайно запустит Terraform с устаревшими файлами состояния, что приведет к откату или дублированию уже развернутых ресурсов.
• Блокирование. Большинство систем управления версиями не предоставляют никаких средств блокирования, которые могли бы предотвратить одновременное выполнение terraformapply двумя разными членами команды.
•Наличие конфиденциальных данных. Все данные в файлах состояния Terraform хранятся в виде обычного текста. Это чревато проблемами, поскольку некоторым ресурсам Terraform необходимо хранить чувствительные данные. Например, если вы создаете базу данных с помощью ресурса aws_db_instance, Terraform сохранит имя пользователя и пароль к ней в файле состояния в открытом виде. Открытое хранение конфиденциальных данных где бы то ни было, включая систему управления версиями, — плохая идея. По состоянию на май 2019 года сообщество Terraform обсуждает открытую заявку (http://bit.ly/33gqaVe), созданную по этому поводу, хотя для решения данной проблемы есть обходные пути, которые мы вскоре обсудим.
Вместо системы управления версиями для совместного управления файлами состояния лучше использовать удаленные хранилища, поддержка которых встроена в Terraform. Хранилище определяет то, как Terraform загружает и сохраняет свое состояние. По умолчанию для этого применяется локальное хранилище, с которым вы работали все это время. Оно хранит файлы состояния на вашем локальном диске. Но поддерживаются также удаленные хранилища с возможностью совместного доступа. Среди них можно выделить Amazon S3, Azure Storage, Google Cloud Storage и такие продукты, как Terraform Cloud, Terraform Pro и Terraform Enterprise от HashiCorp.
Удаленные хранилища решают все три проблемы, перечисленные выше.
•Человеческий фактор. После конфигурации удаленного хранилища Terraform будет автоматически загружать из него файл состояния при каждом выполнении команд plan и apply и аналогично сохранять его туда после выполнения apply. Таким образом, возможность ручной ошибки исключается.
• Блокирование. Большинство удаленных хранилищ имеют встроенную поддержку блокирования. При выполнении terraformapply Terraform автоматически устанавливает блокировку. Если в этот момент данную команду выполняет кто-то другой, блокировка уже установлена и вам придется подождать. Команду apply можно ввести с параметром -lock-timeout=
•Конфиденциальные данные. Большинство удаленных хранилищ имеют встроенную поддержку активного и пассивного шифрования файлов состояния. Более того, они обычно позволяют настраивать права доступа (например, при использовании политик IAM в сочетании с бакетом Amazon S3), чтобы вы могли управлять тем, кто может обращаться к вашим файлам состояния и конфиденциальным данным, которые могут в них находиться. Конечно, было бы лучше, если бы в Terraform поддерживалось шифрование конфиденциальных данных прямо в файлах состояния, но эти удаленные хранилища минимизируют большинство рисков безопасности (файл состояния не хранится в открытом виде где-нибудь на вашем диске).
Если вы используете Terraform в связке с AWS, лучшим выбором в качестве удаленного хранилища будет S3, управляемый сервис хранения файлов от Amazon. Этому есть несколько причин.
• Это управляемый сервис, поэтому для его использования не нужно развертывать и обслуживать дополнительную инфраструктуру.
• Он рассчитан на 99,999999999%-ную устойчивость и 99,99%-ную доступность. Это означает, что вам не стоит сильно волноваться о потере данных и перебоях в работе34.
• Он поддерживает шифрование, что снижает риск хранения чувствительных данных в файлах состояния. Хотя это лишь частичное решение, так как любой член вашей команды с доступом к бакету S3 сможет просматривать эти файлы в открытом виде, но так данные будут шифроваться при сохранении (Amazon S3 поддерживает шифрование на серверной стороне с помощью AES-256) и передаче (Terraform использует SSL для чтения и записи данных в Amazon S3).
• Он поддерживает блокирование с помощью DynamoDB (подробнее об этом —чуть позже).
• Он поддерживает управление версиями, поэтому вы сможете хранить каждую ревизию своего состояния и в случае возникновения проблем откатываться на более старую версию.
• Он недорогой, поэтому большинство сценариев применения Terraform легко вписываются в бесплатный тарифный план35.
Чтобы включить удаленное хранение состояния в Amazon S3, для начала нужно подготовить бакет S3. Создайте файл main.tf в новой папке (это не должна быть папка, в которой вы храните конфигурацию из главы 2) и вверху укажите AWS в качестве провайдера:
provider "aws" {
region = "us-east-2"
}
Затем создайте бакет S3, используя ресурс aws_s3_bucket:
resource "aws_s3_bucket" "terraform_state" {
bucket = "terraform-up-and-running-state"
# Предотвращаем случайное удаление этого бакета S3
lifecycle {
prevent_destroy = true
}
# Включаем управление версиями, чтобы вы могли просматривать
# всю историю ваших файлов состояния
versioning {
enabled = true
}
# Включаем шифрование по умолчанию на стороне сервера
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
}
Этот код устанавливает четыре аргумента.
•bucket. Это имя бакета S3. Имейте в виду, что имена бакетов должны быть уникальными на глобальном уровне среди всех клиентов AWS. Поэтому вместо "terraform-up-and-running-state" вы должны подобрать свое собственное название (так как бакет с этим именем я уже создал36). Запомните его и обратите внимание на то, какой регион AWS вы используете: чуть позже вам понадобятся оба эти фрагмента информации.