[Terraform] Backend

도은호·2025년 9월 30일

terraform

목록 보기
30/32
  • Backend = State의 저장·잠금·버전관리를 담당. 팀/CI에서는 원격 백엔드 필수.
  • 표준: S3(암호화/버전관리) + DynamoDB(락). 백엔드 변경 시 init -migrate-state.

1) 왜 필요한가?

  • 여러 개발자/파이프라인이 같은 인프라를 변경 → 충돌/손상 방지
  • 중앙에서 암호화/접근제어/버전관리 일관 적용

구성 이미지

flowchart LR
  Dev1 -- init/plan/apply --> S3[(State Bucket)]
  Dev2 -- init/plan/apply --> S3
  S3 -- 버전관리/암호화 --> S3v[Versioned Objects]
  DDB[(DynamoDB Lock)] --- 잠금/해제 --- S3

1) 해설

  1. Dev1/Dev2 → S3(State Bucket)

    • 여러 개발자(또는 CI)가 terraform init/plan/apply를 실행하면, 상태 파일(.tfstate)을 S3 버킷에 저장/읽기
    • 로컬 파일 대신 원격 한 곳(S3)에 상태를 모아 충돌과 분실을 줄임
  2. S3 — “버전관리/암호화” → Versioned Objects

    • S3 버킷에 버전관리(Versioning) 를 켜두면, 과거 상태로 복구가 가능.
    • 서버측 암호화(SSE, KMS 권장)민감값이 포함될 수 있는 상태 파일을 보호
  3. DynamoDB Lock — 잠금/해제 — S3

    • 동시에 둘이 apply 하면 망함. 그래서 DynamoDB 테이블이 “락(잠금)” 을 잡아 동시 실행을 막아
    • 작업이 끝나면 락이 해제 (비정상 종료로 락이 남으면 한 번만 신중히 해제)

2) 전체 흐름

  • S3: 상태 파일 중앙 저장소 (버전관리 + 암호화)
  • DynamoDB: 상태 변경 시 단일 작업만 허용하는 분산 락
  • 여러 사람/파이프라인이 동시에 작업해도 충돌 방지 + 복구 가능성 확보

3) 실습용 최소 구성

3-1) 루트에 백엔드 선언

실제 값(버킷/테이블 이름)은 아래 3-2에서 만든 리소스에 맞춰 바꿔야함

# backend.hcl (예: 루트에 위치)
terraform {
  backend "s3" {
    bucket         = "my-tfstate-bucket"
    key            = "project/dev/app.tfstate" # 워크스페이스/스택별로 경로 규칙화 추천
    region         = "ap-northeast-2"
    encrypt        = true
    dynamodb_table = "tf-locks"
  }
}

3-2) S3 + DynamoDB(락) 인프라 예시

백엔드 선언 전에 한 번 만들어두면 편함 (별도 스택 권장)

variable "bucket_name" { default = "my-tfstate-bucket" }
variable "table_name"  { default = "tf-locks" }

resource "aws_s3_bucket" "state" {
  bucket        = var.bucket_name
  force_destroy = false
  tags = { Name = var.bucket_name }
}

resource "aws_s3_bucket_versioning" "versioning" {
  bucket = aws_s3_bucket.state.id
  versioning_configuration { status = "Enabled" }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "sse" {
  bucket = aws_s3_bucket.state.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256" # 운영은 KMS 권장 (aws:kms + kms_key_id)
    }
  }
}

resource "aws_s3_bucket_public_access_block" "block" {
  bucket                  = aws_s3_bucket.state.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_dynamodb_table" "lock" {
  name         = var.table_name
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  attribute { name = "LockID"; type = "S" }
}

4) 동작 확인(필수 체크)

  • 버전관리 확인: 콘솔에서 S3 버킷 → 해당 app.tfstate 객체의 버전 탭을 보면 이전 버전들이 쌓입니다.
  • 락 동작 확인: 동시에 두 군데에서 terraform apply를 시도하면 한쪽이 “state is locked” 메시지를 보고 대기/실패합니다.
  • 락이 안 풀릴 때: 이전 작업이 비정상 종료된 경우일 수 있습니다. DynamoDB 테이블에서 락 아이템(파티션키 LockID)을 수동 삭제는 신중히(정말 아무도 작업 중이 아니라고 확신될 때만).

5) 흔한 오류 & 빠른 해결

  • AccessDenied / KMS 권한 오류

    • S3/KMS에 대한 읽기/쓰기/암호화 권한을 가진 IAM 정책이 있는지 확인.
    • KMS 사용 시 kms:Encrypt/Decrypt/GenerateDataKey 포함 필요.
  • state is locked

    • 진짜 동시 실행이거나, 이전 프로세스가 죽었을 수 있습니다.
    • 잠시 후 다시 시도 → 여전히 안 되면 DynamoDB에서 락 키 확인 후 수동 해제(신중).
  • 리전/버킷 이름 불일치

    • backend "s3"region, bucket, key가 실제 리소스와 맞는지 재확인.
  • 버전관리 미설정

    • 과거 상태 복구가 불가합니다. 반드시 Versioning Enable.

6) 보안/운영 체크리스트

  • 버전관리 켬 (롤백 대비)
  • 암호화: SSE-KMS 권장(+ 키 관리 정책)
  • 공개차단: 퍼블릭 접근 완전 차단
  • 락 테이블 운영(동시 실행 금지)
  • 키 설계: project/env/stack.tfstate처럼 명확한 경로 규칙
  • 권한 최소화(IAM): S3/DDB에 필요한 권한만 부여
  • 백엔드 변경 시 terraform init -migrate-state 로 안전 이전
  • State를 Git에 커밋 금지 (.tfstate, .tfstate.backup, .terraform/)

7) 요약

S3에 상태를 버전/암호화로 안전하게 보관하고, DynamoDB 락으로 동시 실행을 막아 충돌과 손상 없는 팀 협업을 만든다.


2) 선언 예시(루트)

terraform {
  backend "s3" {
    bucket         = "my-tfstate-bucket"
    key            = "project/dev/network.tfstate"
    region         = "ap-northeast-2"
    encrypt        = true            # KMS 연동 권장
    dynamodb_table = "tf-locks"      # 동시 실행 락
  }
}

3) 백엔드 인프라 만들기 예시

resource "aws_s3_bucket" "terraform_state" {
  bucket        = var.bucket_name
  force_destroy = true
  tags = { Name = var.bucket_name }
}

resource "aws_s3_bucket_versioning" "enabled" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration { status = "Enabled" }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
  bucket = aws_s3_bucket.terraform_state.id
  rule {
    apply_server_side_encryption_by_default { sse_algorithm = "AES256" } # KMS 권장
  }
}

resource "aws_s3_bucket_public_access_block" "public_access" {
  bucket                  = aws_s3_bucket.terraform_state.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_dynamodb_table" "terraform_lock" {
  name         = var.table_name
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  attribute { name = "LockID"; type = "S" }
}

4) 운영 팁

  • 백엔드 변경 시: terraform init -migrate-state
  • S3 버킷은 퍼블릭 차단/암호화/KMS 필수, 버전관리로 과거 상태 복구 가능
  • 키 설계(예: project/${terraform.workspace}/stack.tfstate)로 분리
  • 랙/충돌 오류 시 DynamoDB 테이블/락 키 확인, 수동 제거는 신중히

5) 트러블슈팅

  • “State 파일 잠금 실패”: 이전 작업이 락을 잡고 있거나 비정상 종료 → 잠금 키 확인
  • “항상 변동”: 외부 비결정 값(시간/랜덤 keepers) 제거, ignore_changes 남용 주의
  • 권한 오류: S3/DynamoDB에 대한 IAM 정책 점검(읽기/쓰기/락)

🔗 관련 소스

S3 백엔드/잠금 테이블 인프라 — global/s3

참고 템플릿 (루트의 terraform { backend "s3" {} } 블록에 적용)

terraform {
  backend "s3" {
    bucket         = "<your-bucket>"
    key            = "tfstate/${terraform.workspace}/terraform.tfstate"
    region         = "ap-northeast-2"
    dynamodb_table = "<your-lock-table>"
    encrypt        = true
  }
}
profile
`•.¸¸.•´´¯`••._.• 🎀 𝒸𝓇𝒶𝓏𝓎 𝓅𝓈𝓎𝒸𝒽💞𝓅𝒶𝓉𝒽 🎀 •._.••`¯´´•.¸¸.•`

0개의 댓글