왜 IaC 도구 선택이 문제가 되는가
인프라 규모가 커지면 반복되는 리소스 정의, 조건 분기, 환경별 값 주입이 늘어난다. Terraform의 HCL은 선언형에 최적화되어 있지만 반복·조건·추상화가 필요한 순간 count, for_each, dynamic 블록에 의존하게 되고 가독성이 급격히 떨어진다. Pulumi는 TypeScript·Python·Go 같은 범용 언어로 인프라를 기술해 이 문제를 언어 자체의 제어 흐름으로 해결한다. 다만 "코드로 뭐든 할 수 있다"는 자유는 그대로 복잡성 관리 부담으로 돌아온다. 도구 선택은 팀 역량과 운영 방식에 대한 트레이드오프다.
같은 리소스를 두 방식으로 표현하기
버킷 3개를 서로 다른 리전에 만드는 상황을 비교하면 차이가 분명하다. HCL은 for_each와 map을 조합한다.
variable "buckets" {
default = {
logs = "us-east-1"
backup = "eu-west-1"
archive = "ap-northeast-2"
}
}
resource "aws_s3_bucket" "this" {
for_each = var.buckets
bucket = "acme-${each.key}"
tags = { region = each.value }
}
Pulumi(Python)는 일반 반복문과 함수로 같은 결과를 만든다. 조건 분기나 외부 API 호출도 언어 문법 그대로 쓸 수 있다.
import pulumi
import pulumi_aws as aws
buckets = {"logs": "us-east-1", "backup": "eu-west-1", "archive": "ap-northeast-2"}
for name, region in buckets.items():
aws.s3.Bucket(
f"acme-{name}",
bucket=f"acme-{name}",
tags={"region": region},
)
if name == "backup":
# 백업 버킷에만 버전 관리 적용
aws.s3.BucketVersioning(f"{name}-ver", bucket=f"acme-{name}",
versioning_configuration={"status": "Enabled"})
상태(state) 관리의 실제 차이
두 도구 모두 상태 파일 기반이다. Terraform은 S3+DynamoDB 백엔드를 직접 구성해 잠금을 관리하는 경우가 많다. Pulumi는 관리형 서비스(Pulumi Cloud)를 기본 백엔드로 두지만, pulumi login s3://my-bucket처럼 셀프 호스팅 백엔드로 전환할 수 있다. 상태 손상 시 복구 난이도는 비슷하나, Pulumi는 상태에 리소스별 입력값을 암호화해 저장하므로 시크릿 처리 방식이 다르다.
테스트와 검증
Pulumi의 가장 실질적 이점은 언어 생태계의 테스트 도구를 그대로 쓴다는 점이다. 단위 테스트로 정책을 사전 검증할 수 있다.
import unittest, pulumi
class Mocks(pulumi.runtime.Mocks):
def new_resource(self, args):
return [args.name + "_id", args.inputs]
def call(self, args):
return {}
pulumi.runtime.set_mocks(Mocks())
import infra # 스택 정의 모듈
class TestTagging(unittest.TestCase):
@pulumi.runtime.test
def test_all_buckets_tagged(self):
def check(args):
urn, tags = args
assert tags and "region" in tags, f"{urn} 태그 누락"
# 리소스 등록 후 출력값 검증
Terraform 진영은 terraform validate, tflint, Terratest(Go)로 유사한 검증을 하지만, 리소스 정의 언어와 테스트 언어가 분리된다는 점이 다르다.
핵심 항목 비교
| 항목 | Terraform | Pulumi |
|---|---|---|
| 정의 언어 | HCL(전용 DSL) | TS·Python·Go·C# |
| 반복·조건 | for_each/count/dynamic | 언어 기본 문법 |
| 학습 곡선 | DSL만 익히면 됨 | 언어+SDK 이해 필요 |
| 테스트 | Terratest 등 별도 | 언어 네이티브 테스트 |
| 상태 백엔드 | S3 등 직접 구성 | 관리형 기본, 셀프 가능 |
| 생태계 성숙도 | 매우 넓음 | 넓으나 상대적으로 좁음 |
선택 기준과 주의점
HCL의 선언형 제약은 단점만은 아니다. 표현력을 제한하는 대신 팀 전체가 읽고 리뷰하기 쉬운 코드를 강제한다. 반대로 Pulumi는 개발자가 추상화·헬퍼 함수·클래스를 남용하면 "인프라 코드가 애플리케이션처럼 복잡해지는" 함정에 빠진다. 인프라 전담 팀이 없고 백엔드 개발자가 겸업한다면 익숙한 언어를 쓰는 Pulumi가 진입 장벽을 낮춘다. 반대로 전담 SRE·플랫폼 팀이 있고 모듈 재사용과 리뷰 표준화가 중요하면 Terraform의 제약이 오히려 자산이 된다.
마이그레이션 시 고려사항
Pulumi는 pulumi import와 tf2pulumi로 기존 Terraform 자산을 변환할 수 있으나, 완전 자동은 아니며 상태 매핑을 수작업으로 점검해야 한다. 전면 전환보다 신규 스택부터 도구를 적용해 운영 경험을 쌓은 뒤 확대하는 편이 위험을 줄인다. 두 도구 모두 프로바이더 버전 고정과 상태 잠금 정책을 초기에 정해두지 않으면 팀 규모가 커질 때 충돌과 드리프트로 되돌아온다.