“딸깍의 함정”
테라폼을 처음 접하면 모든 리소스(VPC, DB, EC2 등)를 하나의 .tf 파일 혹은 하나의 프로젝트 폴더에 몰아넣고 terraform apply를 날리고 싶다는 유혹에 빠지기 쉽다고 생각합니다. 하지만 프로덕션 환경에서 이런 방식은 매우 위험합니다.
인프라의 규모가 커질수록 **폭발 반경(Blast Radius)**을 최소화하고 관리 효율을 높이기 위한 Layered Architecture 도입이 필수적입니다.
레이어(Layer) 도입의 계기와 의의
모든 인프라를 한 곳에서 관리하면 특정 리소스(예: 보안 그룹 규칙)를 수정하다가 실수로 VPC 전체를 재생성하거나, 상태(State) 파일이 꼬여 전체 인프라가 마비되는 리스크가 존재합니다.
이를 방지하기 위해 인프라를 변화의 주기와 중요도에 따라 계층(Layer)으로 나눠 관리하는 것이 더 효율적입니다.
- L1. Base/Network Layer: VPC, Subnet, IGW, NAT Gateway, Peering, TGW등 베이스 인프라. (거의 변하지 않음)
- L2. Data/Persistence Layer: RDS, Redis, S3 등 데이터 저장소. (실수,변경,유실 등으로 인한 영향도가 매우 높음)
- L3. Service Layer: EKS/ECS 클러스터,Lambda,IAM Role 등 실제 애플리케이션 서비스. (추가/수정/삭제가 빈번함) …
이렇게 나누면 서비스 레이어를 수정하다가 실수해도 기반 네트워크나 데이터베이스에는 영향을 주지 않는 물리적/논리적 격리가 가능해집니다.
Best Practices: 격리와 재사용성
1. 폭발 반경(Blast Radius) 격리
각 레이어는 별도의 Remote State를 가져야 합니다. 하나의 레이어가 망가져도 다른 레이어의 State는 안전하게 보호됩니다.
2. 레이어 간 데이터 참조 (terraform_remote_state)
하위 레이어(VPC)의 정보가 상위 레이어(Service)에서 필요할 때, 직접 하드코딩하지 않고 하위 레이어의 Output을 참조합니다.
# Service 레이어에서 VPC 레이어의 Output을 읽어오는 예시
data "terraform_remote_state" "vpc" {
backend = "s3"
config = {
bucket = "my-terraform-state-bucket"
key = "env:/prod/vpc.tfstate"
region = "ap-northeast-2"
}
}
# 읽어온 데이터 사용
resource "aws_instance" "app" {
subnet_id = data.terraform_remote_state.vpc.outputs.private_subnets[0]
# ...
}
모범 사례: State 관리를 넘어서는 모듈화
단순히 리소스를 나열하는 것이 아니라, 검증된 아키텍처를 **모듈(Module)**로 만들고 이를 각 레이어에서 호출하는 것이 효율적입니다.
- 표준화: 회사나 팀의 표준 태깅 규칙, 보안 설정을 모듈에 내재화합니다.
- 버전 관리: 모듈을 별도 Git 레포지토리로 관리하고 버전 태그를 사용하여 상위 레이어에서 특정 버전의 모듈을 고정해서 사용합니다.
마치며
테라폼의 고수가 된다는 것은 복잡한 문법을 아는 것보다 “어떻게 인프라를 잘 쪼개고 연결할 것인가"에 대한 설계 능력을 갖추는 것을 의미한다고 생각합니다.
다음 편에서는 실제 네트워크 레이어 설계 시 고려해야 할 VPC 디자인 전략(AZ 구성 및 Peering vs TGW)에 대해 심도 있게 다뤄보겠습니다.