Terraform은 인프라를 코드로 관리하는 IaC(Infrastructure as Code) 도구입니다. 이는 선언적 관리로 안정적이고 일관된 인프라 프로비저닝을 지원하죠. 그러나 팀이 커지고 환경이 다양해지면, Terraform을 사용할 때 코드 중복과 관리 복잡도 문제가 생깁니다. 이 글에서는 모듈화, 워크스페이스와 변수 파일(.tfvars
) 관리, Terragrunt*로 Terraform의 이러한 문제를 해결하는 방법을 알아보겠습니다.
(이 글은 Terraform의 주요 개념(예: Backend)을 이해하는 독자를 대상으로 작성했습니다)
*Terragrunt: Terraform으로 작성한 IaC를 확장하는 오케스트레이션 도구
Terraform 기본 이해
먼저 Terraform 워크플로와 기본 파일을 짚고 가겠습니다.
워크플로
Terraform의 워크플로는 Write(Define & Initialize), Plan, Apply 이렇게 세 단계로 구성됩니다. 각 단계에서 수행하는 활동은 다음과 같 습니다.
- Write 단계: 여러 클라우드 제공업체와 서비스에 리소스를 정의하고 버전을 관리합니다.
- Plan 단계: 기존 인프라와 구성을 기반으로 실행 계획을 생성합니다. 이 단계에서는 변경 사항을 미리 확인할 수 있습니다.
- Apply 단계: 승인 후 리소스 의존성을 고려해 제안된 작업을 올바른 순서로 수행합니다. 이 단계에서 실제 인프라가 변경됩니다.
Terraform 워크플로. 출처=https://developer.hashicorp.com/terraform/intro
기본 파일
다음은 기존에 Terraform을 사용한 폴더 구조 예시입니다. 보통 한 폴더 안에 아래와 같은 파일 묶음이 있습니다.
├── .terraform.lock.hcl
├── main.tf
├── terraform.tfstate
├── terraform.tfvars
├── user_data.sh
├── variables.tf
└── version.tf
Terraform의 워크플로 단계별로 각 파일은 다음 역할을 합니다.
- Write 단계
main.tf
: 인프라 리소스를 정의하는 메인 구성 파일입니다. 이 파일에서 실제로 생성할 인프라를 선언합니다.variables.tf
: Terraform에서 사용할 변수를 선언합니다. 이로써 다양한 환경에서 설정값을 쉽게 변경할 수 있습니다.version.tf
: Terraform과 필요한 Provider의 버전을 지정해 버전 호환성 문제를 방지합니다.user_data.sh
: EC2 인스턴스를 시작할 때 실행될 스크립트를 포함합니다. 인스턴스를 초기화할 때 필요한 설정이나 소프트웨 어 설치를 자동화할 수 있습니다.
- Plan 단계
terraform.tfvars
: 변숫값을 지정해 다른 환경에서도 재사용할 수 있는 설정을 정의합니다. 이는variables.tf
와 함께terraform plan
명령에 사용돼 인프라 변경 사항을 예측하는 데 쓰입니다..terraform.lock.hcl
: Provider와 모듈 버전을 잠가 Terraform을 실행할 때, 동일한 버전이 사용되도록 고정합니다. 이 파일은terraform init
명령을 실행할 때 생성되지만, 계획 단계에서 일관된 버전을 사용해 예측 가능한 계획을 수립하는 데 중요한 역할을 합니다.
- Apply 단계
terraform.tfstate
: 현재 인프라 상태를 저장해 변경 사항을 관리하는 핵심 파일입니다.terraform apply
명령을 실행할 때, 이 파일이 업데이트돼 실제 인프라 상태를 반영합니다.
코드 중복, 관리 복잡도 해결 방법
DevOps에서 다양한 환경을 다룰 때 생산성을 향상하려면, PoC(Proof of Concept) 환경을 신속하게 구축해야 하죠. 그러나 단순히 인프라만 자동화한다고 생산성이 높아지지는 않습니다. 각 환경에 맞춘 개별 코드 작성에 따른 코드 중복 문제와 유지 관리 문제부터 해결해야 하죠.
Terraform을 사용할 때도 마찬가지입니다. 앞서 언급했듯 팀이 커지고 환경이 다양해지면, Terraform을 사용할 때 코드 중복과 관리 복잡도 문제가 발생하는데요. 이 문제는 다음 방법으로 해결할 수 있습니다.
- 모듈화: 반복적인 인프라 구성을 모듈로 만들고 여러 환경에서 재사용해 코드 중복을 줄입니다.
- 워크스페이스와 변수 파일(
.tfvars
) 관리: 각 환경에 별도의 워크스페이스를 사용해 서로 독립적으로 인프라를 관리합니다. 또 환경별 변숫값을 지정해 다양한 환경에서 코드 유연성을 높입니다.
코드 재사용성을 높이려면 위 방법은 둘 다 사용하는 게 좋습니다. 각 방법을 자세히 살펴보겠습니다.
폴더 구조 예시
먼저 Terraform을 사용하는 폴더 구조 예시를 짚고 가겠습니다. 이 구조는 A 환경과 B 환경으로 구분해 다양한 환경에서 코드를 재사용하도록 설정됐습니다. 각 환경에 맞는 초기화 스크립트와 변수 파일을 사용해 동일한 main.tf
코드가 환경별로 적절히 실행되도록 구성됐습니다.
├── [backend.tf](http://backend.tf/) # 백엔드 설정 파일
├── [main.tf](http://main.tf/) # 메인 인프라 리소스 정의 파일
├── [output.tf](http://output.tf/) # 출력 변수 정의 파일
├── terraform.tfstate.d # 각 워크스페이스별 상태 파일 저장 디렉토리
│ ├── A.tfstate # A 환경 상태 파일 저장
│ └── B.tfstate # B 환경 상태 파일 저장
├── tfvars # 환경별 변수 파일 저장 디렉토리
│ ├── A.tfvars # A 환경에 필요한 변수 파일
│ └── B.tfvars # B 환경에 필요한 변수 파일
├── user_data # 인스턴스 초기화 스크립트 저장 디렉토리
│ ├── [default.sh](http://default.sh/) # 기본 초기화 스크립트
│ ├── [A.sh](http://a.sh/) # A 환경 초기화 스크립트
│ └── [B.sh](http://b.sh/) # B 환경 초기화 스크립트
└── [variables.tf](http://variables.tf/) # 변수 선언 파일
방법 1. 모듈화(Don’t Repeat Yourself)
Terraform 모듈화는 반복적으로 작성해야 하는 인프라 구성 요소를 모듈 단위로 재사용하도록 지원합니다. 이로써 코드 중복을 줄이고, DRY(Don't Repeat Yourself) 원칙도 지킬 수 있습니다.
출처=픽사베이예를 들어, AWS EC2 인스턴스를 구성하는 모듈을 생성하면, 아래와 같이 여러 환경에서 재사용하고 코드 중복도 줄일 수 있죠.
# main.tf
module "ec2_instance" {
source = "terraform-aws-modules/ec2-instance/aws" # AWS EC2 인스턴스를 구성하는 모듈 지정
name = "EC2-Sample-Instance" # 인스턴스 이름 설정
instance_type = "t2.micro" # 인스턴스 타입 지정
key_name = "sample-key" # 키 페어 이름 설정
monitoring = true # 모니터링 활성화
vpc_security_group_ids = ["sg-12345678"] # 보안 그룹 ID 설정
subnet_id = "subnet-eddcdzz4" # 서브넷 ID 설정
tags = { # 인스턴스 태그 정의
Terraform = "true"
Environment = "sample-env"
}
}
방법 2. 워크스페이스와 변수 파일(.tfvars
) 관리
Terraform 워크스페이스와 변수 파일(.tfvars
)을 사용하면, 환경별로 상태 파일을 분리할 수 있습니다. 이는 여러 환경에서 인프라를 독립적으로 관리하는 데 도움이 되죠.
앞서 살펴본 Terraform 모듈화로 코드 중복을 줄여도 현재 구조에서 여전히 하나의 tfstate 파일(동일한 상태 파일)을 공유하는 문제가 생길 수 있는데요. 동일한 상태 파일을 사용하면, 여러 환경에서 발생하는 변경 사항이 서로 영향을 미쳐 관리가 더 복잡해집니다. 그러나 워크스페이스와 변수 파일(.tfvars
)을 사용하면 이러한 문제를 해결할 수 있죠. 앞서 언급한 Terraform 사용 폴더 구조 예시를 토대로 상세 방법을 알아보겠습니다.
-
워크스페이스 생성, 선택
앞서 살펴본 Terraform 사용 폴더 구조는 A 환경과 B 환경으로 구분해 다양한 환경에서 코드를 재사용하도록 설정됐는데요. 만약 A 환경과 B 환경에서 각각 EC2 인스턴스를 생성하려면, 워크스페이스를 활용해 환경을 분리하면 됩니다. 각 워크스페이스는 독립된 상태 파일을 사용하는데요. 이에 서로 다른 환경에서 EC2 인스턴스를 독립적으로 생성하고 관리할 수 있습니다.
먼저, 아래 명령을 입력해 환경을 분리합니다.
terraform workspace new A
terraform workspace select A그다음, 워크스페이스가 선택된 상태에서
terraform plan
또는terraform apply
명령을 실행합니다. 그러면 아래와 같이terraform.tfstate.d
폴더에 해당 워크스페이스 이름으로 폴더 구조가 생성되고요. 상태 파일(terraform.tfstate
)이 그 안에 저장됩니다....(중략)...
└── terraform.tfstate.d
└── A
└── terraform.tfstate -
.tfvars
파일 생성, 적용.tfvars
파일을 사용하면 환경별로 변숫값을 지정할 수 있습니다. 저는 Docker가 설치된 EC2를 띄우기 위해 아래와 같이A.tfvars
파일을 설정했는데요. 다음 예제를 참고하세요.# A.tfvars
instance_name = "IG-POC-SBX-EC2-A"
instance_type = "t3.small"
instance_count = 1
tags = {
Expiry = "2024.12.31"
Owner = "Chad"
Environment = "SBX"
Purpose = "Sample Instance for A Environment"
}
user_data_path = "user_data/A.sh" -
환경별 EC2 인스턴스 프로비저닝
이제 각 환경에 맞는 Terraform 구성을 적용합니다. 이 과정은 워크스페이스 선택, 계획 수립, 적용 단계로 구성됩니다.
아래는 B 환경에 EC2 인스턴스를 프로비저닝하는 단계별 명령입니다.
-
B 워크스페이스 생성
terraform workspace new B
-
B 워크스페이스 선택
terraform workspace select B
-
B 환경 계획 수립
terraform plan -var-file=tfvars/B.tfvars
-
B 환경에 인프라 적용
terraform apply -var-file=tfvars/B.tfvars
-
Terragrunt 기반 문제 근본 해결 방법
위 방법 외에도, Terraform을 사용할 때 코드 중복과 관리 복잡도 문제를 근본적으로 해결하는 방법이 또 있습니다. 바로 Terraform으로 작성한 IaC를 확장하는 오케스트레이션 도구인 ‘Terragrunt’를 활용하는 거죠. Terragrunt는 앞서 살펴본 워크스페이스와 변수 파일(.tfvars
) 관리 방법을 더 효율적이고 깔끔하게 래핑해 제공합니다. Terragrunt를 활용하는 이유와 구체적인 사용법을 자세히 알아보겠습니다.
Terragrunt 사용 이유: Terraform Backend 구성 한계
Terraform의 Backend 기능은 인프라 상태를 원격에 저장하고 공유합니다. 그러나 Backend 설정은 terraform init
단계에서 정적으로 정의돼야 하는데요. 이는 init
단계에서 Terraform이 상태 파일을 관리하고 원격 저장소의 상태 파일을 사용하기 위해 사전에 초기화 단계에서 backend
설정이 필요하기 때문입니다.
문제는 이러한 이유로 여러 환경에 걸쳐 동적인 Terraform Backend 구성을 적용하기 어렵다는 건데요. 그러나 Terragrunt를 활용하면 이 문제를 해결할 수 있습니다. 이는 각 환경에 맞는 상태 파일을 효율적으로 관리하도록 지원하기 때문입니다.
Terragrunt 기본 이해
Terragrunt는 여러 환경을 효율적으로 관리하는 기능을 제공해 Terraform 코드의 재사용성을 높입니다. Terragrunt를 사용하면 Terraform을 실행하기 전에 환경별로 설정 파일을 동적으로 생성하거나 구성 파일을 모아주는데요. 이로써 환경별로 다른 Backend 구성을 유연하게 적용할 수 있죠. Terragrunt 기본 이해의 일환으로, HCL 파일 개념과 특징을 살펴보겠습니다.
-
HCL(Hierarchical Configuration Language) 파일
Terragrunt의 변수와 설정 관리, 전달 방식. 출처=TerragruntTerragrunt
는HCL
형식의 파일을 사용해 상위 레벨에서 변수와 설정을 관리하고, 이 설정을 하위 모듈에 전달합니다.
다음은 Terragrunt와 HCL 파일을 활용한 폴더 구조 예시입니다. 각 환경에 따라 설정된 구성 파일을 독립적으로 관리할 수 있습니다.
├── environment # 환경별 설정 디렉토리
│ ├── docker # Docker 환경 설정 디렉토리
│ │ ├── docker.sh # Docker 환 경 초기화 스크립트
│ │ └── terragrunt.hcl # Docker 환경에 대한 하위 HCL 파일
│ └── gitlab-runner # GitLab Runner 환경 설정 디렉토리
│ ├── gitlab-runner.sh # GitLab Runner 환경 초기화 스크립트
│ └── terragrunt.hcl # GitLab Runner 환경에 대한 하위 HCL 파일
├── modules # 모듈 디렉토리
│ └── ec2 # EC2 인스턴스 모듈
└── terragrunt.hcl # 상위 HCL 파일 (공통 설정 파일)-
상위 HCL 파일(
terragrunt.hcl
)상위
terragrunt.hcl
파일에서는 공통 Backend와 소스 경로를 아래와 같이 설정해 하 위 모듈이 이를 참조하도록 합니다.# terragrunt.hcl
remote_state {
backend = "s3"
config = {
bucket = "sample-terraform-state-bucket"
key = "terraform/${path_relative_to_include()}/terraform.tfstate"
region = "ap-northeast-2"
encrypt = true
}
generate = {
path = "backend.tf"
if_exists = "overwrite"
}
}
terraform {
source = "${get_parent_terragrunt_dir()}/modules/ec2"
}
inputs = {
...(중략)...
}-
remote_state
: 원격 상태 파일을 관리하는 설정으로, S3 버킷에 상태 파일을 저장해 여러 환경에서 인프라 상태를 공유하고 관리하도록 합니다.key
경로는${path_relative_to_include()}
함수를 사용해 동적으로 설정돼 각 환경의 고유 경로에 상태 파일이 저장됩니다. -
terraform
: 작성한 EC2 모듈의 경로를 참조해 해당 모듈을 사용하도록 설정합니다. 여기서는 상위 디렉터리에 위치한modules/ec2
모듈을 가져와 EC2 인스턴스를 생성하도록 구성됐습니다. -
inputs
: EC2 인스턴스를 생성할 때 필요한 변수(예: 인스턴스 이름, 타입, 보안 그룹 ID 등)를 정의해 모듈에 전달합니다.
-
-
하위 HCL 파일(
environment/docker/terragrunt.hcl
)# environment/docker/terragrunt.hcl
include {
path = find_in_parent_folders()
}
inputs = {
instance_name = "IG-POC-SBX-EC2-DOCKER"
instance_type = "t3.small"
instance_count = 2
tags = {
Expiry = "2024.12.31"
Owner = "Chad"
Environment = "SBX"
Purpose = "Docker Runner"
}
user_data_path = "docker.sh"
}-
include
: 상위 폴더에 있는terragrunt.hcl
파일을 포함합니다. -
inputs
: 인스턴스 이름, 타입, 태그 등을 재정의합니다.
-
-
Terragrunt 적용
Terragrunt를 사용하면 각 환경에 맞게 Backend 키가 동적으로 설정되는 과정을 확인할 수 있는데요. docker
환경을 예로, Terragrunt를 적용하는 방법을 알아보겠습니다.
-
환경 디렉터리로 이동
먼저 원하는 환경 디렉터리(
environment/docker
)로 이동합니다. 이 디렉터리에는 해당 환경의terragrunt.hcl
파일이 있습니다.cd environment/docker
-
terragrunt init
명령으로 초기화terragrunt init
명령을 실행해 모듈 소스와 Backend 설정을 초기화합니다. 이 단계는 Terragrunt를 처음 실행할 때 필요하며, S3 Backend와 모듈 소스를 설정합니다.terragrunt init
-
terragrunt plan
명령으로 변경 사항 확인변경 사항을 검토하기 위해
terragrunt plan
명령을 실행합니다. 이 명령은 현재 상태와 변경할 계획을 비교해 ‘어떤 리소스가 추가, 변경, 삭제될지’ 보여줍니다.terragrunt plan
-
terragrunt apply
명령으로 인프라 적용변경 사항을 확인한 후,
terragrunt apply
명령을 실행해 인프라 구성을 적용합니다. Terragrunt는 이 명령을 실행하면서 각 환경에 맞게 S3 버킷 내 고유한 경로에 상태 파일을 생성하고 저장합니다.terragrunt apply
-
Backend 확인
S3 버킷에서
S3 버킷에서 상태 파일 생성 확인 화면terraform/environmentdocker
경로 하위에 상태 파일이 생성된 걸 확인할 수 있습니다.
여기서
docker
환경에 맞춰 동적으로 설정된 Backend 구성이 ‘S3 버킷에 올바르게 저장됐는지’ 볼 수도 있습니다. 아울러 다른 환경에도 동일한 방식으로 디렉터리를 이동하고,terragrunt apply
명령을 실행해 독립적인 상태 파일을 생성할 수 있습니다.
맺음말
지금까지 Terraform을 사용할 때 발생하는 코드 중복과 관리 복잡도 문제를 해결하는 방법을 살펴봤습니다. 먼저 모듈화, 워크스페이스와 변수 파일(.tfvars
) 관리 방법을 알아봤고요. Terragrunt로 여러 환경에 걸쳐 Terraform Backend 구성을 동적으로 적용해 코드 중복과 관리 복잡도 문제를 근본적으로 해결하는 방법도 살펴봤습니다. 특히 Terragrunt가 ‘여러 환경에서 인프라를 일관성 있고, 유연하게 관리하는 데 도움이 된다’는 점을 확인했습니다.
아울러 예시와 함께 terragrunt.hcl
파일로 S3에 동적으로 Backend를 설정하는 방법을 알아봤습니다. 또 각 환경의 terragrunt apply
명령으로 상태 파일이 환경별로 구분돼 저장되는 과정을 확인했습니다. Terragrunt의 상위 HCL 파일과 하위 HCL 파일을 활용해 공통 설정을 쉽게 관리하며, 각 환경에 맞춘 설정을 추가하는 방법을 살펴봤고요. 이로써 DRY 원칙을 지키며 인프라를 효율적으로 재사용하는 방법을 익혔습니다.
특히 Terragrunt로 인프라 구성을 관리하면, 서로 다른 환경에서 충돌 없이 독립적으로 인프라를 운영할 수 있고요. 인프라 변경 사항이 각 환경에 맞게 저장돼 유지 보수성과 확장성이 크게 향상됩니다. 여러분도 이 글에서 소개한 모듈 화, 워크스페이스/변수 파일 관리 방식을 실행하거나, 다양한 프로젝트에 Terragrunt를 적용해 인프라를 더 유연하고 확장 가능하게 관리하시길 바랍니다.
지금 이 기술이 더 궁금하세요? 인포그랩의 DevOps 전문가가 알려드립니다.