4.8 KiB
4.8 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| devops-terraform-patterns | Terraform — 모듈 / 워크스페이스 / state | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
Terraform
Infra 를 코드로. Module = 재사용 단위, State = 진실 source, Workspace = 환경 분리. 1.x 부터 OpenTofu 로 fork — 둘 다 호환. State 는 항상 remote backend.
📖 핵심 개념
- HCL: 선언형. resource / data / module / variable / output.
- State:
terraform.tfstate— 무엇이 만들어졌는지. - Plan / Apply: dry-run / 실행.
- Module: 재사용 가능한 폴더 묶음.
- Workspace: 같은 코드 다른 state.
💻 코드 패턴
폴더 구조
infra/
modules/
rds/
main.tf
variables.tf
outputs.tf
envs/
dev/
main.tf
backend.tf
terraform.tfvars
prod/
main.tf
backend.tf
terraform.tfvars
Module
# modules/rds/main.tf
variable "name" { type = string }
variable "instance_class" { type = string; default = "db.t4g.micro" }
variable "vpc_security_group_ids" { type = list(string) }
variable "subnet_ids" { type = list(string) }
resource "aws_db_subnet_group" "this" {
name = "${var.name}-subnets"
subnet_ids = var.subnet_ids
}
resource "aws_db_instance" "this" {
identifier = var.name
engine = "postgres"
engine_version = "16"
instance_class = var.instance_class
allocated_storage = 20
storage_encrypted = true
vpc_security_group_ids = var.vpc_security_group_ids
db_subnet_group_name = aws_db_subnet_group.this.name
backup_retention_period = 7
deletion_protection = true
skip_final_snapshot = false
username = "app"
manage_master_user_password = true
}
output "endpoint" { value = aws_db_instance.this.endpoint }
Env 호출
# envs/prod/main.tf
module "rds_main" {
source = "../../modules/rds"
name = "app-prod"
instance_class = "db.r7g.large"
vpc_security_group_ids = [aws_security_group.db.id]
subnet_ids = data.aws_subnets.private.ids
}
output "rds_endpoint" { value = module.rds_main.endpoint }
Backend (remote state)
# envs/prod/backend.tf
terraform {
required_version = ">= 1.6"
backend "s3" {
bucket = "tf-state-myco-prod"
key = "main.tfstate"
region = "us-east-1"
dynamodb_table = "tf-locks"
encrypt = true
}
}
Provider lock
terraform {
required_providers {
aws = { source = "hashicorp/aws"; version = "~> 5.0" }
}
}
.terraform.lock.hcl 은 commit.
Variables / tfvars
# variables.tf
variable "env" { type = string }
variable "region" { type = string; default = "us-east-1" }
# terraform.tfvars (NOT committed if secrets)
env = "prod"
Workflow
terraform init
terraform fmt -recursive
terraform validate
terraform plan -out=tfplan
terraform apply tfplan
Secrets
# ❌ tfvars 안 secret
db_password = "..."
# ✅ env or AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db" {
secret_id = "app/db"
}
resource "aws_db_instance" "this" {
password = jsondecode(data.aws_secretsmanager_secret_version.db.secret_string)["password"]
}
tfsec / checkov (보안 검사)
tfsec .
checkov -d .
CI 에서 plan + tfsec 자동.
Drift detection
terraform plan -detailed-exitcode
# exit 0 = no change, 2 = drift
🤔 의사결정 기준
| 환경 | 분리 |
|---|---|
| 작은 팀, 1 env | workspace 또는 단일 폴더 |
| Multi-env | 폴더 분리 (envs/dev, envs/prod) |
| 큰 조직 / 격리 | 별도 state file + IAM 분리 |
| Shared infra (VPC) | 별도 module + remote state read |
| Pulumi vs Terraform | Pulumi = TS/Python, Terraform = HCL |
| OpenTofu | 라이센스 자유 — 같이 호환 |
❌ 안티패턴
- State 로컬 파일: 잃으면 인프라 분실. 항상 remote.
- State lock 없음: 동시 apply 시 깨짐. dynamodb 또는 S3 locking.
- Secret tfvars 에 commit: leak.
- 수동 console 변경 + apply: drift, 다음 apply 가 덮음.
- Module 거대 (1000줄): 작게 단위.
- Resource 직접 dev/prod 혼용: 분리.
- Provider version unlock: 무작위 brake.
terraform destroy실수: prod 보호 —prevent_destroylifecycle.
🤖 LLM 활용 힌트
- modules/ + envs/ + remote state.
- secret = AWS Secrets Manager / Vault.
- CI = init + fmt + validate + plan + tfsec.