Terraform 심화: 모듈·워크스페이스·remote state·import·드리프트

상위: 00.DevOps MOC

개요

Terraform - 기초에서 provider·resource·state·plan/apply를 다뤘다면, 이 글은 규모가 커진 인프라를 유지보수 가능하게 만드는 기법을 다룬다. 코드가 커지면 복붙이 늘고, 환경(dev/stage/prod)이 갈라지며, 콘솔에서 급하게 만진 자원과 코드가 어긋나기 시작한다. 이를 다스리는 네 축이 모듈(재사용), 워크스페이스/환경 분리, import(기존 자원 편입), 드리프트 감지다. 그리고 그 밑바탕에는 언제나 “state는 절대 수동 편집하지 않는다”는 원칙이 깔린다.

원리와 핵심 개념

모듈: 재사용 단위

모듈variable(입력) → resourceoutput(출력)으로 캡슐화된 재사용 블록이다. 반복되는 인프라 패턴(예: “VPC + 서브넷 + NAT” 한 세트)을 모듈로 만들면, 호출부는 파라미터만 바꿔 여러 번 인스턴스화한다.

# modules/bucket/variables.tf
variable "name"  { type = string }
variable "env"   { type = string }
 
# modules/bucket/main.tf
resource "aws_s3_bucket" "this" {
  bucket = "${var.name}-${var.env}"
  tags   = { Environment = var.env }
}
output "arn" { value = aws_s3_bucket.this.arn }
# 루트에서 호출 — 버전 고정된 모듈을 여러 번 사용
module "docs_bucket" {
  source = "./modules/bucket"   # 레지스트리/Git이면 version = "1.2.0"
  name   = "myorg-docs"
  env    = var.env
}

원격 모듈(Git, Terraform Registry)은 반드시 version을 고정한다. 고정하지 않으면 남의 모듈 업데이트가 조용히 내 plan을 바꾼다.

환경 분리: 워크스페이스 vs 디렉터리

같은 코드로 dev/stage/prod를 만드는 두 접근이 있다.

방식구조장점한계
워크스페이스하나의 backend, terraform workspace로 state만 분리코드 100% 공유, 전환 간단backend가 하나라 권한/설정을 환경별로 못 나눔
디렉터리 분리envs/prod, envs/dev 각각 backend환경별 백엔드·권한·변수 완전 격리코드 중복(모듈로 완화)
terraform workspace new prod
terraform workspace select prod
# 코드에서 현재 워크스페이스 참조
# bucket = "myorg-docs-${terraform.workspace}"

워크스페이스는 가볍지만, prod와 dev의 backend·자격증명·상태 저장 위치를 물리적으로 분리하고 싶다면 디렉터리 분리가 더 안전하다. 프로덕션 조직은 대개 후자 + 공용 모듈 조합을 쓴다.

remote state와 잠금

여러 사람·CI가 동시에 apply하면 state가 손상된다. 원격 백엔드(S3 등)는 잠금으로 apply를 직렬화한다. 또한 계층을 나눈 인프라(네트워크 state ↔ 앱 state)는 terraform_remote_state data source로 다른 state의 output을 읽어 참조한다.

data "terraform_remote_state" "network" {
  backend = "s3"
  config = {
    bucket = "myorg-tf-state"
    key    = "network/prod/terraform.tfstate"
    region = "ap-northeast-2"
  }
}
# 다른 state가 내보낸 VPC ID 참조
resource "aws_instance" "app" {
  subnet_id = data.terraform_remote_state.network.outputs.private_subnet_id
}

import: 기존 자원 편입

콘솔이나 다른 도구로 만든 기존 자원을 Terraform 관리 아래로 들이는 것이 import다. 핵심은 “import는 state에 매핑만 추가할 뿐, HCL 코드는 만들어 주지 않는다”는 점(과거 CLI 방식). 그래서 대응하는 resource 블록을 먼저 작성해야 한다. Terraform 1.5+는 이를 코드로 선언하는 import 블록을 제공한다.

# 선언형 import (Terraform 1.5+): plan에 반영되고 리뷰 가능
import {
  to = aws_s3_bucket.legacy
  id = "existing-legacy-bucket"
}
resource "aws_s3_bucket" "legacy" {
  bucket = "existing-legacy-bucket"
}
# terraform plan -generate-config-out=gen.tf 로 HCL 초안 생성 가능
# 전통 CLI 방식 (state에만 매핑 추가)
terraform import aws_s3_bucket.legacy existing-legacy-bucket

드리프트(drift) 감지

드리프트는 코드가 기술한 상태와 실제 인프라가 어긋난 상태다. 누군가 콘솔에서 보안 그룹 규칙을 손으로 추가하면 드리프트가 생긴다. plan은 실행 시 refresh로 실제 상태를 읽어 드리프트를 diff로 보여준다.

terraform plan -refresh-only   # 코드 변경 없이 드리프트만 탐지
# 출력에 "~ (변경 감지)"가 뜨면 실제 인프라가 코드와 다름

CI에서 주기적으로 plan -detailed-exitcode(변경 있으면 exit 2)를 돌려 드리프트를 알림으로 잡는 것이 운영 베스트프랙티스다.

실전: state 조작은 명령으로만

state를 바꿔야 하는 상황(리팩터링으로 리소스 주소 변경, 자원 제거 등)은 JSON을 손대지 말고 전용 명령으로 처리한다.

terraform state list                      # 관리 중 자원 목록
terraform state mv aws_s3_bucket.old \
                   aws_s3_bucket.new       # 주소 변경(리팩터링)
terraform state rm aws_s3_bucket.legacy    # 관리에서 제외(실제 자원은 유지)
terraform state show aws_s3_bucket.docs    # 특정 자원 state 조회

Terraform 1.1+는 코드 안에서 리팩터링을 선언하는 moved 블록도 지원해, state mv를 수동으로 치지 않고 리뷰 가능한 코드로 남길 수 있다.

함정과 베스트프랙티스

함정결과대응
tfstate JSON 수동 편집매핑 파손, 자원 재생성/유실state mv/rm·moved 블록만 사용
모듈 버전 미고정남의 업데이트가 plan을 흔듦version 핀, lock 파일 커밋
워크스페이스로 prod 격리 착각하나의 backend/권한 공유 위험프로덕션은 디렉터리·backend 분리
import만 하고 코드 누락다음 plan이 자원을 삭제하려 함코드 먼저 작성 → 그 다음 import
드리프트 방치apply가 예기치 않게 되돌림주기적 plan -refresh-only로 감지
  • 모놀리식 state 금지: 계층 분리 + terraform_remote_state로 blast radius를 줄인다.
  • 콘솔 변경 금지 문화: 급한 수정도 코드로. 부득이했다면 즉시 코드에 반영 후 plan을 깨끗하게 만든다.
  • CI 드리프트 게이트: 정기 plan으로 사람이 몰래 만진 변경을 자동 탐지한다.

정리

규모 있는 Terraform 운영은 모듈로 중복을 없애고, 환경 분리(워크스페이스는 가볍게·프로덕션은 backend 분리)로 격리하며, remote state + 잠금으로 동시성을 통제한다. 기존 자원은 대응 코드를 먼저 쓴 뒤 import로 편입하고, 드리프트plan -refresh-only로 주기 감지한다. 무엇을 하든 state는 손으로 고치지 않고 state mv/rm·moved·import 블록 같은 선언·명령 경로로만 조작한다. 기초 개념은 Terraform - 기초, 배포 파이프라인 연계는 배포 전략 - Rolling·Blue-Green·Canary를 참고한다.

키워드: #terraform #iac module workspace remote-state import drift