자물쇠 버킷, 서버, 클라우드 아이콘으로 표현한 S3 백엔드 기반 안전한 IaC 상태 관리
글 요약
Terraform State를 S3 Backend와 DynamoDB Lock으로 안전하게 관리하는 방법을 정리합니다. 동시성 충돌, State 파일 손상, 권한 관리 등 실무에서 자주 겪는 문제와 해결 노하우를 한 번에 확인할 수 있습니다.
“apply 도중에 누가 또 apply 해서 State가 꼬였어요…”
Terraform State 관리는 IaC 운영의 핵심인데, 많은 팀이 로컬 파일로 두거나 단순히 S3에만 올려둡니다. 이 글에서는 S3 Backend + DynamoDB Lock 조합으로 동시성을 제어하고, 실무에서 발생하는 실패 사례와 해결 방법, 그리고 다른 Backend 방식과의 차이점까지 전부 알려드립니다.
Terraform을 처음 쓸 때는 별생각 없이 로컬에 terraform.tfstate 파일을 두는 경우가 많습니다. 소규모 프로젝트라면 큰 문제가 안 되지만, 팀 단위로 운영하기 시작하는 순간 State 파일 충돌, 동시 apply로 인한 리소스 중복 생성, 암호화되지 않은 민감 정보 노출 같은 사고가 한 번씩 터지거든요. 실제로 클라우드 인프라팀에서 State 파일이 망가져서 하루 동안 장애를 격은 사례를 수도 없이 봐왔습니다.
그래서 이 글에서는 AWS 환경에서 가장 검증된 조합인 S3 + DynamoDB Lock 패턴을 기준으로, State 관리 전략을 단계별로 풀어보겠습니다.
📑 목차
Terraform은 선언형 코드로 인프라를 정의하지만, 실제로 어떤 리소스가 어떤 상태로 존재하는지를 추적하려면 State 파일이 반드시 필요합니다. State가 깨지면 plan과 apply가 모두 의미를 잃게 되니까요. 특히 멀티 계정·멀티 리전으로 인프라가 확장될수록, 이 State를 어떻게 보관하고 잠그느냐가 전체 운영 안정성을 가른다고 해도 과언이 아닙니다.
📌 이 글의 핵심 정리
- Terraform State는 인프라의 진실 공급원(source of truth)이며, 로컬에 두는 순간 팀 운영은 위험해집니다.
- S3 Backend는 State의 중앙 저장소 역할을 하고, 버전 관리와 암호화를 무료로 제공합니다.
- DynamoDB Lock 테이블은 동시 apply를 원천 차단하여 State 파일 깨짐을 방지합니다.
- State 잠금 해제, 백업 복구, 권한 분리 같은 운영 절차를 미리 만들어 두는 것이 핵심입니다.
- Terraform Cloud, GCS, Consul 등 다른 Backend와 비교해 AWS 환경에서는 S3+DynamoDB가 가장 비용 효율적입니다.
Terraform State의 역할과 관리 필요성
Terraform State는 단순한 JSON 파일처럼 보이지만, 실제로는 리소스 ID와 실제 인프라의 매핑 정보를 모두 담고 있습니다. EC2 인스턴스의 ID, VPC의 ARN, RDS 엔드포인트 등이 모두 State 안에 기록되어 있거든요. 그래서 누군가가 임의로 State 파일을 수정하거나 동시에 여러 곳에서 apply를 실행하면, 실제 AWS 리소스와 코드 사이의 불일치가 발생합니다.
State가 망가지면 어떤 일이 벌어지는가
State 파일이 손상되거나 버전이 어긋나면, terraform plan 결과가 완전히 다르게 나옵니다. 실제로 존재하는 리소스를 “삭제해야 한다”고 판단하거나, 반대로 만들어진 줄 모르는 리소스를 또 만들려고 하기도 합니다. 이런 경우 중복 리소스 생성 비용이나 데이터 유실로 이어지는데, 복구하는 데 보통 몇 시간에서 며칠까지 걸리더라고요.
중앙화된 Backend가 필요한 이유
팀원이 각자의 노트북에 State를 두면, 누가 최신 버전을 가지고 있는지 알 수 없습니다. 중앙 저장소를 사용하면 모든 사람이 하나의 동일한 State를 바라보기 때문에 plan 결과가 일관되고, CI/CD 파이프라인에서도 동일한 컨텍스트로 인프라를 관리할 수 있습니다.
S3 Backend 설정과 운영 노하우
S3 버킷과 DynamoDB 잠금 테이블로 Terraform 상태를 안전하게 관리하는 IaC 운영 환경을 옆 각도에서 클로즈업으로 보여주는 이미지
S3는 Terraform에서 가장 널리 쓰이는 Backend 저장소입니다. 버전 관리, KMS 암호화, 서버 사이드 암호화가 기본 제공되고, 데이터 전송 비용도 무료에 가까워서 비용 부담이 거의 없거든요. 여기서는 실무에서 검증된 설정 패턴을 정리했습니다.
S3 버킷 생성 시 필수 옵션
- 버킷 버전 관리 활성화: 실수로 State가 망가졌을 때 이전 버전으로 즉시 롤백할 수 있습니다.
- 암호화 설정: SSE-KMS를 적용하여 State 안에 포함된 비밀값을 안전하게 보호합니다.
- 퍼블릭 액세스 차단: 실수로 버킷 정책이 잘못 설정되어도 외부 노출을 막아줍니다.
- 수명 주기 정책: 오래된 State 버전은 Glacier로 이동시켜 스토리지 비용을 절감할 수 있습니다.
💡 S3 Backend 꿀팁
- 버킷 이름은 전 세계에서 유일해야 하므로, 회사 도메인 기반 명명 규칙을 미리 정해두는 것이 좋습니다.
- State 파일 경로를 env/dev/, env/staging/, env/prod/처럼 환경별로 분리하면 운영이 훨씬 깔끔해집니다.
- 버킷 자체는 별도의 관리 계정에 두고, 실제 워크로드 계정에는 AssumeRole로 접근하도록 구성하면 권한이 깨끗하게 분리됩니다.
Backend 초기화 절차
Backend 설정은 terraform { backend “s3” {} } 블록으로 선언한 뒤 terraform init을 실행하면 됩니다. 이때 S3 버킷이 비어 있지 않으면 마이그레이션 여부를 묻는 프롬프트가 나오는데, 여기서 잘못 승인하면 기존 State가 덮어쓰기되므로 신중하게 진행해야 합니다. 운영 환경에서는 반드시 새 버킷에 빈 경로로 시작하는 것이 안전합니다.
DynamoDB Lock으로 동시성 문제 해결하기
S3만으로는 동시 실행 문제를 막을 수 없습니다. S3는 단순한 객체 스토리지라서 두 사람이 동시에 같은 State 파일을 읽고 수정하면 마지막 쓴 사람 것으로 덮어써집니다. 이걸 방지하려면 DynamoDB Lock 테이블을 함께 사용해야 합니다.
Lock 테이블 구조와 동작 원리
DynamoDB 테이블의 기본 키(Primary Key)는 LockID라는 문자열 속성으로 구성됩니다. Terraform이 apply를 시작할 때 이 키에 조건부 쓰기(ConditionExpression)를 시도하고, 다른 사람이 이미 잠금을 잡고 있다면 락 획득 실패로 즉시 에러가 발생합니다. 결과적으로 동시 apply가 원천 차단되거든요.
테이블 생성 시 주의사항
⚠️ DynamoDB Lock 주의
- 테이블 파티션 키는 반드시 LockID(String)로 생성해야 합니다. 다른 이름으로 만들면 Terraform이 테이블을 인식하지 못합니다.
- 온디맨드 요금제를 선택하면 트래픽이 적은 프로젝트에서도 비용이 거의 발생하지 않습니다.
- 테이블을 다른 용도로 재사용하지 마세요. 충돌이 발생하면 Terraform이 정상 동작하지 않을 수 있습니다.
State 관리 실패 사례와 전문가의 해결법
실무에서 가장 많이 겪는 사고 중 하나가 잠금이 풀리지 않아 apply가 영원히 멈추는 현상입니다. CI/CD 파이프라인이 비정상 종료되거나, 로컬에서 실행하던 도중 노트북이 강제 종료되면 Lock이 그대로 남아 있거든요. 이 상태에서 다른 사람이 apply를 시도하면 “Error acquiring the state lock” 메시지와 함께 모든 작업이 차단됩니다.
대표적인 실패 시나리오
- GitHub Actions에서 apply 도중 워커가 OOM으로 강제 종료 → Lock이 그대로 잔존
- 개발자가 동시에 두 개의 터미널에서 같은 환경에 apply 실행 → State 덮어쓰기로 인한 리소스 중복
- S3 버전 관리 미활성화로 사고 발생 시 복구할 이전 버전 자체가 부재
- IAM 권한을 너무 넓게 부여해서 외부 개발자가 실수로 State를 삭제
전문가의 단계별 해결 노하우
첫 번째로, terraform force-unlock 명령으로 잔존 락을 해제할 수 있습니다. 다만 이 명령은 정말 다른 작업이 진행 중이 아닐 때만 사용해야 안전합니다. 두 번째로, CI/CD에서는 timeout과 retry 로직을 추가해서 워커가 죽더라도 Lock이 자동으로 풀리도록 설계하는 것이 좋습니다. 세 번째로, S3 버전 관리를 반드시 켜두고, 주기적으로 terraform state pull로 백업을 떠두면 어떤 사고에도 빠르게 복구할 수 있습니다.
💡 Lock 운영 꿀팁
- DynamoDB 콘솔에서 LockID 항목을 직접 삭제하기 전에, 반드시 해당 항목의 정보(Who, Operation, Created)가 더 이상 유효한지 확인하세요.
- CI/CD에서는 직렬화 큐(SQS, S3 대기열 등)를 두어 apply 작업을 한 번에 하나씩만 실행하도록 강제하는 것이 안전합니다.
- 긴급한 복구 절차는 런북(Runbook)으로 문서화해서 팀 모두가 즉시 따라 할 수 있게 만들어 두세요.
다른 Backend 방식과 비교 분석
S3 + DynamoDB가 정답은 아닙니다. 조직의 요구사항에 따라 Terraform Cloud, GCS, Consul, HTTP Backend 같은 대안도 충분히 고려할 가치가 있거든요. 아래 표는 실무에서 자주 비교되는 네 가지 방식의 핵심 차이를 정리한 것입니다.
| 구분 | S3 + DynamoDB | Terraform Cloud | GCS Backend | Consul Backend |
|---|---|---|---|---|
| 저장소 종류 | AWS 관리형 | HashiCorp SaaS | GCP 관리형 | HashiCorp 자체 호스팅 |
| 잠금 방식 | DynamoDB | 내장 (자동) | GCS 객체 락 | Consul 세션 |
| 비용 | 매우 낮음 | 사용자 수 기준 과금 | 매우 낮음 | 서버 운영 비용 발생 |
| 암호화 | SSE-KMS 지원 | 플랫폼 기본 제공 | CMEK 지원 | 자체 설정 필요 |
| 운영 부담 | 낮음 | 매우 낮음 | 낮음 | 높음 |
| 주요 사용 시나리오 | AWS 중심 일반 기업 | 팀 협업·정책 강제 | GCP 멀티클라우드 | 온프레미스 중심 |
S3 + DynamoDB의 차별점은 무엇이냐고요. 가장 큰 장점은 월 수천 원대의 저렴한 비용으로 인프라를 안전하게 운영할 수 있다는 점입니다. Terraform Cloud는 사용자 수에 비례해 과금되기 때문에 50명 이상이 동시에 IaC를 다루는 조직에서는 비용이 빠르게 늘어나거든요. 반면 S3는 요청 비용이 사실상 무료이고 DynamoDB 온디맨드도 무료 티어 안에서 충분히 커버됩니다.
반면 Terraform Cloud의 차별점은 Sentinel 기반 정책 강제와 팀 워크플로우입니다. PR 단위로 plan을 자동 실행하고 승인 게이트를 둘 수 있어서 거버넌스가 중요한 금융·공공기관에 적합합니다. 하지만 데이터를 HashiCorp SaaS에 두는 것이 컴플라이언스 요건에 걸릴 수 있으니, 이런 경우에는 결국 S3 + DynamoDB처럼 자기 계정 안에서 모든 것을 해결하는 방식이 정답이 됩니다.
실무 보안 및 운영 베스트 프랙티스
State를 안전하게 관리하려면 단순히 S3와 DynamoDB를 켜는 것만으로는 부족합니다. 접근 제어, 암호화 키 관리, 모니터링이 함께 설계되어야 비로소 운영 가능한 수준이 됩니다.
권한 분리 원칙
State 버킷에 대한 권한은 CI/CD 서비스 역할과 운영자 AssumeRole에만 부여하고, 일반 개발자는 plan만 가능하도록 권한을 잘게 쪼개는 것이 이상적입니다. State 자체에는 RDS 비밀번호, API 키 같은 민감 정보가 평문으로 들어 있기 때문에, 실수로라도 외부에 노출되면 안 되거든요.
암호화와 감사 로깅
- KMS 키는 별도의 키 정책을 만들어 최소 권한 원칙으로 운영합니다.
- S3 버킷에 CloudTrail 데이터 이벤트 로깅을 활성화하여 누가 언제 State를 읽고 썼는지 추적합니다.
- DynamoDB Lock 테이블은 PITR(포인트 인 타임 복구)을 켜두면 과거 특정 시점으로 되돌릴 수 있습니다.
State 분리 전략
규모가 커지면 단일 State로 모든 리소스를 관리하는 것이 오히려 위험해집니다. 네트워크·컴퓨팅·데이터 같은 도메인 단위로 State를 분리하면, 한 부분의 apply가 실패해도 다른 부분에는 영향을 주지 않습니다. terraform state mv 명령을 사용하면 운영 중에도 리소스를 다른 State로 안전하게 이동시킬 수 있거든요.
⚠️ State 분리 주의
- State를 너무 잘게 쪼개면 리소스 간 의존성 관리가 복잡해집니다. 도메인 경계를 신중하게 정하세요.
- 분리 작업 전에는 반드시 state pull로 백업을 받아두고, 테스트 환경에서 먼저 검증하세요.
자주 묻는 질문
Q. S3 Backend만 써도 충분하지 않나요? 왜 DynamoDB Lock이 필요한가요?
A. S3는 객체 저장소일 뿐이라 동시성 제어를 제공하지 않습니다. 두 명이 동시에 apply하면 마지막 작업이 State를 덮어써서 리소스가 꼬이거나 중복 생성될 수 있거든요. DynamoDB Lock이 없으면 사실상 팀 운영이 불가능에 가깝다고 보시면 됩니다.
Q. Lock이 풀리지 않을 때는 어떻게 하나요?
A. 먼저 다른 곳에서 apply가 진행 중인지 확인한 뒤, 진행 중이 아니라면 terraform force-unlock <LOCK_ID> 명령으로 해제할 수 있습니다. DynamoDB 콘솔에서 LockID 항목을 직접 삭제하는 것도 방법이지만, 이 작업은 반드시 런북에 따라 신중하게 진행해야 합니다.
Q. State 파일에 비밀번호를 평문으로 두면 안 되나요?
A. State에는 사실상 평문 형태로 저장되기 때문에, S3 자체 암호화만으로는 부족합니다. 가능하면 AWS Secrets Manager나 Parameter Store 같은 전용 비밀 관리 도구에 저장하고, Terraform에서는 데이터 참조만 하도록 구성하는 것이 안전합니다.
Q. 멀티 리전 환경에서는 State를 어떻게 관리해야 하나요?
A. 리전별로 Backend를 분리하고, 각 리전의 State 파일 경로를 명확히 구분하는 것이 일반적입니다. 글로벌 리소스는 별도의 State로 분리해서 리전 장애가 다른 리전의 IaC에 영향을 주지 않도록 설계합니다.
Q. State 마이그레이션 시 주의할 점은 무엇인가요?
A. 마이그레이션 전에는 반드시 기존 State 파일을 S3 버전 관리 기능이나 별도 백업본으로 보존해 두어야 합니다. 그리고 terraform init -migrate-state 명령은 한 번만 실행해야 하며, 도중에 실패하면 그대로 중단하고 원인을 파악한 뒤 다시 시도하는 것이 안전합니다.
Terraform State 관리는 처음엔 단순한 파일 저장처럼 보이지만, 실제로는 팀 운영의 신뢰성을 결정하는 핵심 요소입니다. S3 Backend와 DynamoDB Lock을 조합하면 비용 대비 매우 안정적인 운영 환경을 만들 수 있고, 여기에 권한 분리·암호화·모니터링까지 더하면 엔터프라이즈 수준에서도 충분히 활용할 수 있거든요. 오늘 정리한 내용을 바로 적용해서 안전한 IaC 운영 환경을 만들어 보시길 권합니다.
면책조항
본 글은 작성자의 실무 경험과 공식 문서를 바탕으로 정리한 내용이며, 특정 환경에서의 결과를 보장하지 않습니다. AWS 서비스 요금, IAM 정책, Terraform 버전 등에 따라 실제 적용 방법이 달라질 수 있으므로, 도입 전에는 반드시 공식 문서와 소속 조직의 보안 가이드를 함께 확인하시기 바랍니다. 글에 포함된 코드는 예시이며, 프로덕션 환경 적용 전 충분한 테스트가 필요합니다.