Урок 22-27: Модули, State и Multi-Cloud
Часть 1: Создание Модулей
Структура модуля
terraform-modules/
├── vpc/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ └── README.md
├── security_group/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── compute/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── modules-usage/
├── main.tf
├── variables.tf
└── terraform.tfvars
Модуль VPC
Файл: modules/vpc/variables.tf
variable "vpc_name" {
type = string
description = "Name of VPC"
}
variable "cidr_block" {
type = string
default = "10.0.0.0/16"
description = "CIDR block for VPC"
}
variable "enable_dns" {
type = bool
default = true
description = "Enable DNS support"
}
variable "tags" {
type = map(string)
default = {}
description = "Tags for resources"
}
Файл: modules/vpc/main.tf
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
enable_dns_hostnames = var.enable_dns
enable_dns_support = var.enable_dns
tags = merge(var.tags, {
Name = var.vpc_name
})
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = merge(var.tags, {
Name = "${var.vpc_name}-igw"
})
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2a"
tags = merge(var.tags, {
Name = "${var.vpc_name}-public-subnet"
})
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = merge(var.tags, {
Name = "${var.vpc_name}-public-rt"
})
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
Файл: modules/vpc/outputs.tf
output "vpc_id" {
value = aws_vpc.main.id
}
output "subnet_id" {
value = aws_subnet.public.id
}
output "igw_id" {
value = aws_internet_gateway.main.id
}
Использование модуля
Файл: main.tf
module "vpc" {
source = "./modules/vpc"
vpc_name = "production-vpc"
cidr_block = "10.0.0.0/16"
enable_dns = true
tags = {
Environment = "production"
Project = "MyApp"
}
}
module "vpc_staging" {
source = "./modules/vpc"
vpc_name = "staging-vpc"
cidr_block = "10.1.0.0/16"
enable_dns = true
tags = {
Environment = "staging"
Project = "MyApp"
}
}
Доступ к выходам модуля
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = module.vpc.subnet_id
tags = {
Name = "web-server"
}
}
output "prod_vpc_id" {
value = module.vpc.vpc_id
}
output "staging_vpc_id" {
value = module.vpc_staging.vpc_id
}
Часть 2: Remote State
Хранение State в S3
Файл: terraform.tf
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-west-2"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
Создание S3 бакета для State
# backend.tf (используется только один раз локально)
resource "aws_s3_bucket" "terraform_state" {
bucket = "my-terraform-state-${data.aws_caller_identity.current.account_id}"
lifecycle {
prevent_destroy = true
}
}
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
lifecycle {
prevent_destroy = true
}
}
data "aws_caller_identity" "current" {}
Миграция State
# Инициализировать локально
terraform init
# Мигрировать в S3
terraform init -migrate-state
# Просмотр состояния
terraform state list
terraform state show
# Вытянуть state локально для backup
terraform state pull > terraform.tfstate.backup
Часть 3: Workspaces
Использование Workspaces
# Создать workspace
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
# Список workspaces
terraform workspace list
# Переключиться на workspace
terraform workspace select prod
# Текущий workspace
terraform workspace show
# Удалить workspace
terraform workspace delete dev
Использование в коде
locals {
workspace = terraform.workspace
environment_config = {
dev = {
instance_type = "t2.micro"
instance_count = 1
}
staging = {
instance_type = "t2.small"
instance_count = 2
}
prod = {
instance_type = "t2.large"
instance_count = 3
}
}
config = local.environment_config[local.workspace]
}
resource "aws_instance" "web" {
count = local.config.instance_count
ami = "ami-0c55b159cbfafe1f0"
instance_type = local.config.instance_type
tags = {
Name = "${local.workspace}-web-${count.index + 1}"
Environment = local.workspace
}
}
State файлы для Workspaces
.terraform/
├── terraform.tfstate # default workspace
└── terraform.tfstate.d/
├── dev
│ └── terraform.tfstate
├── staging
│ └── terraform.tfstate
└── prod
└── terraform.tfstate
Часть 4: Multi-Region
Создание ресурсов в нескольких регионах
provider "aws" {
alias = "us-west-2"
region = "us-west-2"
}
provider "aws" {
alias = "eu-west-1"
region = "eu-west-1"
}
resource "aws_instance" "us_web" {
provider = aws.us-west-2
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "us-web"
Region = "us-west-2"
}
}
resource "aws_instance" "eu_web" {
provider = aws.eu-west-1
ami = "ami-0d71ea30463e0ff89" # Different AMI ID in EU
instance_type = "t2.micro"
tags = {
Name = "eu-web"
Region = "eu-west-1"
}
}
output "us_instance_ip" {
value = aws_instance.us_web.public_ip
}
output "eu_instance_ip" {
value = aws_instance.eu_web.public_ip
}
Multi-Region с модулями
module "vpc_us" {
source = "./modules/vpc"
providers = {
aws = aws.us-west-2
}
vpc_name = "us-vpc"
cidr_block = "10.0.0.0/16"
}
module "vpc_eu" {
source = "./modules/vpc"
providers = {
aws = aws.eu-west-1
}
vpc_name = "eu-vpc"
cidr_block = "10.1.0.0/16"
}
Часть 5: Multi-Account
Работа с несколькими AWS аккаунтами
provider "aws" {
alias = "dev"
region = "us-west-2"
assume_role_arn = "arn:aws:iam::ACCOUNT_ID_DEV:role/TerraformRole"
assume_role_session_name = "terraform-dev"
}
provider "aws" {
alias = "prod"
region = "us-west-2"
assume_role_arn = "arn:aws:iam::ACCOUNT_ID_PROD:role/TerraformRole"
assume_role_session_name = "terraform-prod"
}
resource "aws_instance" "dev" {
provider = aws.dev
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "dev-web"
Account = "dev"
}
}
resource "aws_instance" "prod" {
provider = aws.prod
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.large"
tags = {
Name = "prod-web"
Account = "prod"
}
}
Часть 6: Google Cloud Platform (GCP)
Базовая конфигурация GCP
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
provider "google" {
project = var.gcp_project
region = var.gcp_region
}
variable "gcp_project" {
type = string
}
variable "gcp_region" {
type = string
default = "us-central1"
}
Создание VM в GCP
resource "google_compute_instance" "web" {
name = "web-server"
machine_type = "e2-micro"
zone = "us-central1-a"
boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
}
}
network_interface {
network = "default"
access_config {
// Автоматический external IP
}
}
tags = ["web-server"]
}
Создание Storage Bucket в GCP
resource "google_storage_bucket" "app_data" {
name = "${var.gcp_project}-app-data"
location = "US"
force_destroy = false
versioning {
enabled = true
}
encryption {
default_kms_key_name = google_kms_crypto_key.storage.id
}
lifecycle_rule {
condition {
age = 30
}
action {
type = "Delete"
}
}
}
Полный пример: Production Environment
# terraform.tf
terraform {
required_version = ">= 1.0"
backend "s3" {
bucket = "company-terraform-state"
key = "prod/terraform.tfstate"
region = "us-west-2"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
# main.tf
module "vpc_prod" {
source = "./modules/vpc"
vpc_name = "prod-vpc"
cidr_block = "10.0.0.0/16"
tags = {
Environment = "prod"
}
}
module "compute_prod" {
source = "./modules/compute"
subnet_id = module.vpc_prod.subnet_id
environment = "prod"
instance_count = 3
tags = {
Environment = "prod"
}
}
# outputs.tf
output "vpc_id" {
value = module.vpc_prod.vpc_id
}
output "instance_ips" {
value = module.compute_prod.instance_ips
}