홈/인프라/Terraform IaC 실전: 모듈·state·환경 분리
인프라TerraformIaC

Terraform IaC 실전: 모듈·state·환경 분리

모듈 경계, remote state, workspace vs 디렉터리 분리, plan 리뷰 게이트까지 프로덕션에서 깨지지 않는 Terraform 운영법.

Terraform IaC 실전: 모듈·state·환경 분리
목차
  1. IaC가 필요한 이유
  2. 프로젝트 디렉터리 구조
  3. 모듈 설계
  4. 상태(State) 원격 관리
  5. CI/CD 통합
  6. 실전 팁
  7. 콘솔 수동 변경(드리프트)과 리팩터링 시 재생성 막기
  8. 참고: 공식 문서
  9. 모듈 경계 잡는 법
  10. State·잠금
  11. CI 게이트
  12. 참고 자료
  13. 자주 묻는 질문 (FAQ)
  14. 검증 환경·편집자 주
  15. 실무 적용 순서 (요약)

IaC가 필요한 이유

클릭으로 만든 인프라는 재현이 불가능합니다. Terraform으로 인프라를 코드로 정의하면 버전 관리, 코드 리뷰, 자동화가 가능해집니다.

프로젝트 디렉터리 구조

CODE
terraform/
├── environments/
│   ├── prod/
│   │   ├── main.tf
│   │   └── terraform.tfvars
│   └── staging/
└── modules/
    ├── vpc/
    ├── eks/
    └── rds/

모듈 설계

HCL
# modules/vpc/main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.cidr_block
  enable_dns_hostnames = true

  tags = merge(var.common_tags, {
    Name = "${var.project}-${var.environment}-vpc"
  })
}

variable "cidr_block" {
  type    = string
  default = "10.0.0.0/16"

  validation {
    condition     = can(cidrhost(var.cidr_block, 0))
    error_message = "유효한 CIDR 형식이어야 합니다."
  }
}

상태(State) 원격 관리

HCL
terraform {
  backend "s3" {
    bucket         = "my-company-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "ap-northeast-2"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}
Bash
# State 잠금 테이블 생성
aws dynamodb create-table \
  --table-name terraform-state-lock \
  --attribute-definitions AttributeName=LockID,AttributeType=S \
  --key-schema AttributeName=LockID,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

CI/CD 통합

YAML
name: Terraform
on:
  pull_request:
    paths: ['terraform/**']

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform fmt -check -recursive
      - run: terraform validate
      - run: terraform plan -out=tfplan

실전 팁

tfstate 파일 절대 git에 커밋 금지 (.gitignore)

CODE
*.tfstate
*.tfstate.backup
.terraform/

sensitive 변수 마스킹

HCL
variable "db_password" {
  type      = string
  sensitive = true
}

Terraform은 단순한 도구가 아닙니다. 팀의 인프라 운영 방식 자체를 바꾸는 문화적 전환입니다.

콘솔 수동 변경(드리프트)과 리팩터링 시 재생성 막기

state와 잠금을 갖춰도, 운영 중에는 누군가 콘솔에서 직접 바꾼 설정이나 모듈 구조 변경 때문에 plan이 예상치 못한 삭제·재생성을 제안하는 일이 생깁니다.

원인

  • 장애 대응 중 콘솔에서 보안 그룹 규칙·인스턴스 크기를 직접 수정(드리프트)
  • 리소스를 모듈 안으로 옮기거나 이름을 바꿔 주소가 달라짐 → Terraform은 "기존 삭제 + 새로 생성"으로 해석
  • Terraform 밖에서 만든 리소스를 코드에 추가만 하고 state에 가져오지 않음

검증 명령

Bash
# 드리프트만 확인 (인프라 변경 없이 state와 실제 비교)
terraform plan -refresh-only
# CI용: 변경이 있으면 종료 코드 2
terraform plan -detailed-exitcode -out=tfplan; echo "exit=$?"
# 삭제·재생성 대상만 골라 보기
terraform show -json tfplan | jq -r '.resource_changes[] | select(.change.actions | index("delete")) | .address'

해결

  • 드리프트가 의도한 변경이면 코드에 반영하고, 아니면 apply로 코드 상태로 되돌립니다.
  • 리팩터링으로 주소가 바뀌면 moved 블록을 추가해 삭제 없이 state 주소만 옮깁니다.
HCL
moved {
  from = aws_security_group.web
  to   = module.web.aws_security_group.this
}
  • 기존 리소스는 Terraform 1.5 이상의 import 블록으로 가져온 뒤 plan이 "변경 없음"이 될 때까지 코드를 맞춥니다.
  • 참고: 최근 Terraform 버전의 S3 백엔드는 use_lockfile = true로 S3 자체 잠금을 지원하므로, 사용 중인 버전의 백엔드 문서에서 DynamoDB 잠금 권장 여부를 확인하세요.

재발 방지

  • -detailed-exitcode 기반 드리프트 점검을 매일 스케줄로 돌려 종료 코드 2면 알림을 보냅니다.

참고: 공식 문서

이 글에서 다루는 동작·설정·에러의 1차 출처는 다음 공식 문서입니다. 버전별 옵션과 정확한 동작은 여기서 확인하세요.

모듈 경계 잡는 법

CODE
modules/
  network/     # VPC, subnet, SG
  data/        # RDS, bucket
  compute/     # ASG, EKS node group
envs/
  dev/
  staging/
  prod/
  • 리소스 단위 모듈보다 도메인 단위(network/data/compute)가 재사용·리뷰에 유리.
  • 모듈 version을 고정(Git tag/registry). main 추적 금지.

State·잠금

HCL
terraform {
  backend "s3" {
    bucket         = "org-tf-state"
    key            = "prod/network/terraform.tfstate"
    region         = "ap-northeast-2"
    dynamodb_table = "tf-locks"
    encrypt        = true
  }
}
항목권장
State 위치원격(S3/GCS/Azure Blob) + 잠금
시크릿state에 평문 넣지 말 것 → Vault/SSM
환경 분리workspace보다 디렉터리+별도 state

CI 게이트

YAML
# PR: plan만, main: apply
- run: terraform fmt -check
- run: terraform validate
- run: terraform plan -out=tfplan
# apply는 protected branch + 수동 승인

참고 자료

자주 묻는 질문 (FAQ)

Q. workspace로 dev/prod를 나눠도 되나요?
가능하지만 실수로 잘못된 workspace에 apply하기 쉽습니다. 디렉터리 분리가 더 안전합니다.

Q. state가 꼬이면?
terraform state list → rm/mv로 정리하고, 인프라와 드리프트를 refresh/import로 맞춥니다. 백업본 없이 state 파일을 직접 편집하지 마세요.

검증 환경·편집자 주

  • 검증 시점: 2026-09 (문서·CLI 플래그는 벤더 업데이트에 따라 달라질 수 있음)
  • 명령어는 읽기 전용 조회 위주이며, 프로덕션 적용 전 스테이징에서 plan/dry-run을 권장합니다.
  • 본문은 공식 문서 링크와 운영에서 반복되는 실패 패턴을 기준으로 편집 검토했습니다.

실무 적용 순서 (요약)

  1. 현재 상태 측정(메트릭·비용·오류율) 없이 도구부터 바꾸지 말 것
  2. 가장 작은 범위에서 변경 → 롤백 조건 명시
  3. 체크리스트를 런북/티켓 템플릿에 옮겨 팀 공유
  4. 한 달 뒤 지표로 효과 재평가 후 확대
✦ ✦ ✦
출처·검증 안내

글마다 출처와 확인 범위가 다릅니다. 아래에 확인일·확인 범위·출처가 기록된 글만 그 범위에서 확인된 것입니다. 기록이 없는 글은 적용 전 사용 중인 버전의 공식 문서로 직접 확인하고, 오류는 이메일로 알려주세요. 편집 정책 · 이메일로 제보

이 글에는 사람이 확인한 날짜·범위 기록이 아직 없습니다.

발행 ·
주제 관련 공식 문서(자동 연결 · 문장별 근거 아님)Terraform 공식 문서 ↗

댓글

첫 번째 댓글을 남겨보세요.