Terraform придумала компания HashiCorp, и по сути это такой «конструктор инфраструктуры как кода». Вместо того чтобы кликать мышкой в консоли AWS или еще где-то, ты описываешь всё текстом, запускаешь пару команд — и получаешь нужные ресурсы: от сетей и машин до баз и DNS.
Главная идея простая: инфраструктура живет в репозитории, как обычный код. Её можно ревьюить, катить через pull request, откатывать, проверять линтерами и всё вот это DevOps-счастье.
Конфиги пишутся на HCL (HashiCorp Configuration Language). На вид он чем-то напоминает YAML, но с поправкой на то, что затачивали его именно под инфраструктуру. Всё лежит в файлах с расширением .tf: там и виртуалки, и сети, и базы, и политики доступа, и DNS-записи — короче, вся архитектура.
Как Terraform в целом работает #
- Декларативный подход — ты описываешь желаемое состояние: «хочу два инстанса, одну базу и такую-то сеть». Как именно это создавать — уже забота Terraform.
- Plan — перед применением Terraform показывает, что он планирует сделать: какие ресурсы создать, какие поменять, какие удалить. Удобно, чтобы не прострелить себе ногу.
- Apply — шаг, где всё реально создается или обновляется. Вот тут уже летят запросы в API облаков.
- State — Terraform держит снимок текущей инфраструктуры в файле
terraform.tfstate. По нему он понимает разницу между тем, что есть, и тем, что ты хочешь получить. - Идемпотентность — если ты второй раз запустишь тот же самый код без изменений, Terraform ничего не будет пересоздавать. Оно просто скажет: «всё уже как в конфиге».
Что можно сделать за один прогон #
Типичный сценарий в AWS: одним terraform apply ты можешь:
- собрать VPC, подсети, таблицы маршрутизации;
- поднять несколько EC2-инстансов с нужным AMI;
- описать правила фаервола через Security Groups;
- создать IAM-роли и политики под сервисы и людей;
- повесить всё это на балансировщик и выдать наружу.
Провайдеры и экосистема #
Terraform сам по себе API облаков не знает — этим занимаются провайдеры. Каждый провайдер умеет общаться с конкретной платформой или сервисом: AWS, Azure, GCP, Alibaba Cloud, Kubernetes, Helm, Cloudflare, GitHub, Datadog, Vault, vSphere и куча других штук. Подключаешь нужный провайдер — и можешь управлять ресурсами в его мире.
Почему его везде используют #
- Перед применением видно, что именно поменяется (plan), что сильно успокаивает.
- Одна и та же конфигурация нормально едет в разные окружения: dev, stage, prod — меняются только переменные.
- Всё хранится в Git: есть история изменений, diff, откаты, ревью.
- Хорошо заходит в CI/CD и GitOps: линтеры (
tflint,checkov,OPA), проверки полисей, автодеплой.
В итоге Terraform — это почти стандартный инструмент в DevOps/SRE-подразделениях, особенно если речь про мультиоблака и гибридные инфраструктуры.
Пример: модуль с EC2-инстансом #
Обычно проект с модулями выглядит где-то так:
terraform-project/
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars
└── modules/
└── ec2-instance/
├── main.tf
├── variables.tf
└── outputs.tf
main.tf в корне #
provider "aws" {
region = "us-east-1"
}
module "web_server" {
source = "./modules/ec2-instance"
instance_name = "frontend-node"
instance_type = "t3.micro"
ami_id = "ami-0c55b159cbfafe1f0"
}
Внутри модуля: modules/ec2-instance/main.tf #
resource "aws_instance" "this" {
ami = var.ami_id
instance_type = var.instance_type
tags = {
Name = var.instance_name
}
}
Переменные модуля: modules/ec2-instance/variables.tf #
variable "instance_name" {
description = "Name tag for the EC2 instance"
type = string
}
variable "instance_type" {
description = "Type of EC2 instance"
type = string
default = "t2.micro"
}
variable "ami_id" {
description = "AMI ID for the instance"
type = string
}
Outputs модуля: modules/ec2-instance/outputs.tf #
output "instance_id" {
value = aws_instance.this.id
}
output "public_ip" {
value = aws_instance.this.public_ip
}
terraform.tfvars: значения по умолчанию #
instance_type = "t3.micro"
ami_id = "ami-0c55b159cbfafe1f0"
Что в итоге делает модуль #
- Поднимает EC2-инстанс с конкретной AMI и типом.
- Логику держит в отдельном каталоге
modules/ec2-instance, чтобы можно было переиспользовать. - Все настройки прокидываются через переменные (в том числе из
.tfvars). - На выход отдает id и публичный IP, с которыми потом можно что-то делать дальше.
Такой стиль позволяет спокойно масштабировать инфраструктуру, использовать один и тот же модуль в разных окружениях и без боли прикручивать всё это к CI/CD.
Удаленное хранение стейта: S3 + DynamoDB #
Когда над проектом работают не только ты и твой кот, локального terraform.tfstate уже мало. Обычно стейт уносят в S3, а блокировки делают через DynamoDB, чтобы два terraform apply не уехали одновременно.
backend.tf (можно слить с main.tf) #
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "envs/dev/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-lock-table"
encrypt = true
}
}
DynamoDB здесь нужен только для lock — он не даёт двум процессам одновременно менять стейт.
Пример data source: взять свежий AMI #
Terraform умеет не только создавать ресурсы, но и просто вытаскивать данные — через data source.
data "aws_ami" "latest_amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}
И дальше где-нибудь в ресурсе:
ami = data.aws_ami.latest_amazon_linux.id
Управлять Kubernetes через Terraform + Helm #
Зачастую Terraform используют как «оркестратор всего», а Helm — как способ ставить чарты в кластер. Есть отдельный Helm-провайдер, который позволяет вызывать чарты прямо из Terraform.
provider "helm" {
kubernetes {
config_path = "~/.kube/config"
}
}
resource "helm_release" "nginx" {
name = "nginx"
repository = "https://charts.bitnami.com/bitnami"
chart = "nginx"
version = "15.2.0"
set {
name = "service.type"
value = "LoadBalancer"
}
}
- Один
terraform applyможет сначала создать, например, EKS-кластер, а потом сверху накатить Helm-чарты. - Всё, включая сами чарты и их значения, живет в Git и воспроизводится автоматом.
- Можно передавать данные из Terraform-модулей прямо в Helm-параметры.
