Terraform 심화: 모듈·워크스페이스·remote state·import·드리프트
상위: 00.DevOps MOC
개요
Terraform - 기초에서 provider·resource·state·plan/apply를 다뤘다면, 이 글은 규모가 커진 인프라를 유지보수 가능하게 만드는 기법을 다룬다. 코드가 커지면 복붙이 늘고, 환경(dev/stage/prod)이 갈라지며, 콘솔에서 급하게 만진 자원과 코드가 어긋나기 시작한다. 이를 다스리는 네 축이 모듈(재사용), 워크스페이스/환경 분리, import(기존 자원 편입), 드리프트 감지다. 그리고 그 밑바탕에는 언제나 “state는 절대 수동 편집하지 않는다”는 원칙이 깔린다.
원리와 핵심 개념
모듈: 재사용 단위
모듈은 variable(입력) → resource → output(출력)으로 캡슐화된 재사용 블록이다. 반복되는 인프라 패턴(예: “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