글 요약
Terraform State Lock 충돌로 협업 배포가 멈출 때, DynamoDB Lock 해제부터 State 복구 절차까지 단계별로 정리했습니다. 이 글 하나면 Lock 장애를 새벽에도 혼자 끝낼 수 있어요.
“apply가 갑자기 멈췄어요. 누가 Terraform State Lock을 잡고 있는 건가요?”
State Lock 장애는 인프라 팀 혈압을 단숨에 올리는 단골 이슈더라고요. DynamoDB Lock 해제 명령어, State 복구 절차, 그리고 같은 문제를 또 만들지 않을 예방 운영 노하우까지 모두 담았습니다. 팀원이 전원 재운 새벽에도 끝낼 수 있게 정리합니다.
State Lock이 꼬이는 순간 terraform plan은 “Error acquiring the state lock”을 뱉고 그대로 멈춥니다. 처음 겪는 사람은 이 메시지를 보고 DB 연결 문제라고 오해하거든요. 실제로는 동시 실행을 막기 위한 Lock이 정상적으로 작동한 결과인데, 문제는 그 Lock이 제대로 풀리지 않을 때 터집니다. 협업 인원이 늘어날수록, CI/CD 파이프라인이 늘어날수록 이런 장애는 기하급수로 늘어난다고 하더라고요.
특히 AWS 환경에서는 DynamoDB 기반 Lock이 사실상 표준처럼 자리 잡았어요. 다만 Lock 테이블 설계, IAM 권한, 그리고 State 파일 자체가 꼬였을 때의 복구까지는 대부분의 가이드가 잘 다루지 않습니다. 그래서 실전에서 진짜 부딪히는 상황별로 풀어봤어요.
📑 목차
� 이 글의 핵심 정리
- State Lock은 동시 실행 방지라는 명확한 목적이 있고, AWS 환경에선 DynamoDB Lock이 가장 검증된 선택입니다.
- Lock 장애는 대부분 강제 종료, 네트워크 타임아웃, IAM 권한, 테이블 미생성, State 파일 손상 다섯 가지에서 발생합니다.
- terraform force-unlock은 1차 대응이지만, 진짜 원인은 Lock 테이블 상태와 State 파일에 있는 경우가 많습니다.
- S3+Local, Consul, GCS Lock과 비교했을 때 DynamoDB는 일관성, 관리 부담, 비용 세 가지에서 우위입니다.
- State 복구는 백업 → 진단 → 임포트/복원 → 검증 순서가 안전하며, 복구 전엔 반드시 백업을 떠두어야 합니다.
State Lock이란? 인프라 팀이 꼭 알아야 할 핵심 개념
Terraform은 State 파일을 통해 “지금 어떤 리소스가 실제로 배포되어 있는지” 추적하거든요. 협업 환경에서 여러 사람이 동시에 terraform apply를 돌리면 State 파일이 서로 덮어쓰여질 수 있고, 그 순간 인프라 상태와 코드가 어긋나 복구가 거의 불가능해집니다. 그래서 도입된 장치가 바로 State Lock이에요. Lock이 잡혀 있는 동안에는 다른 모든 명령이 대기하거나 거부됩니다.
Lock이 보호하는 대상과 동작 흐름
State Lock은 단순히 “사용 중” 표시만 하는 게 아닙니다. 누가(LockID), 언제(LockInfo 시간), 어떤 명령을(Operation) 실행 중인지까지 기록하거든요. apply 시작 → Lock 획득 → State 읽기/쓰기 → 작업 완료 → Lock 해제. 이 일곱 단계 중 어디서든 끊기면 Lock은 그대로 남고, 다음 사람은 진입을 못해요. 그래서 Lock의 정체성을 정확히 아는 게 장애 해결의 출발점입니다.
Backend와 Lock의 관계
Lock은 Backend에 종속돼요. S3 Backend를 쓴다면 DynamoDB 테이블이 Lock을 담당하고, GCS Backend라면 GCS 자체가 Lock을 제공합니다. 로컬 파일 Backend는 OS의 파일 락에 의존하기 때문에 네트워크 공유 저장소와는 궁합이 안 맞고요. 즉 Backend를 바꾸면 Lock 메커니즘도 바뀝니다. 이걸 모르면 “S3는 멀쩡한데 왜 Lock이 안 잡히지”라는 미궁에 빠집니다.
DynamoDB Lock 동작 원리와 테이블 설계
DynamoDB Lock은 조건부 쓰기(ConditionExpression)로 동작합니다. 테이블에는 보통 LockID(Partition Key) 하나만 두고, 누군가 PutItem을 시도할 때 기존 항목이 없거나 Digest가 일치할 때만 성공시키는 방식이죠. 이렇게 하면 동시에 두 명이 Lock을 잡을 수 없고, 동시에 두 명이 해제할 수도 없어요. 결과적으로 동시성 제어가 DB 레벨에서 보장되거든요.
테이블 생성 시 반드시 챙길 설정
Partition Key는 반드시 LockID(문자열)여야 합니다. 다른 이름으로 만들면 Terraform이 못 읽고 에러를 뱉어요. 그리고 Pay-per-Request로 만드는 게 운영 부담이 가장 적습니다. 프로비저닝 모드로 두면 트래픽이 몰리는 순간 capacity가 부족해 ProvisionedThroughputExceededException이 발생하거든요. 평소엔 비용이 0에 가깝기 때문에 요청 기반이 합리적이에요.
IAM 권한 — Lock 장애의 가장 조용한 원인
State 파일 읽기/쓰기 권한만 주고 Lock 테이블 권한을 빼먹는 경우가 의외로 많습니다. 최소한 dynamodb:GetItem, PutItem, DeleteItem은 필요하고, 디버깅을 위해 Scan, DescribeTable 정도는 열어두는 게 좋아요. 그리고 KMS로 State 파일을 암호화했다면 Lock 테이블이 아니라 S3 쪽 키 정책이지만, 같이 묶어서 점검해야 빠뜨리지 않습니다.
💡 DynamoDB Lock 꿀팁
- TTL 설정을 활용하면 비정상 종료된 Lock을 자동으로 청소할 수 있습니다. ExpiresAt 같은 숫자 속성을 추가하고 TTL 활성화 후 Lambda로 주기적 �신하면 좀비 Lock을 원천 차단해요.
- PITR(Point-in-Time Recovery)을 켜두면 테이블이 꼬였을 때 35일 이내 어느 시점으로든 복원 가능하므로 재해 복구 비용이 크게 줄어듭니다.
Lock 장애 5가지 유형과 증상 진단법
Lock 장애는 증상이 비슷해 보여도 원인이 다섯 갈래로 나뉩니다. 원인을 모르면 force-unlock을 무작정 눌러 State가 더 꼬이게 되거든요. 그래서 먼저 진단부터 해야 합니다.
유형 1. 좀비 Lock — 작업자가 이미 종료됐는데 Lock이 남는 경우
CI 러너가 갑자기 죽거나 로컬에서 Ctrl+C로 종료했을 때 발생해요. DynamoDB에는 Lock이 그대로 있고, LockInfo의 LockID만 들고 있어서 force-unlock으로 해결합니다. 다만 ID를 모르면 못 풀기 때문에, 평소엔 terraform output나 로그에서 ID를 따로 기록해 두는 습관이 중요합니다.
유형 2. 권한 부족 — AccessDenied가 뜨는데 메시지가 모호한 경우
신입 입사 후 권한을 부여했는데 terraform plan이 Lock 획득 단계에서 멈추는 경우예요. 메시지가 “Error acquiring the state lock”만 나오고 끝나기 때문에 권한 문제라고는 생각 못 하는 사람이 많습니다. CloudTrail을 켜두면 이 시점에 dynamodb:PutItem이 AccessDenied로 떨어진 게 잡혀요.
유형 3. 테이블 자체가 없거나 이름이 다른 경우
Backend 설정은 멀쩡한데 Lock 테이블을 새 계정/리전에서 다시 만들어야 할 때가 있죠. dynamodb describe-table으로 먼저 존재 여부를 확인하고, LockID Partition Key가 정확히 있는지 봐야 해요. 테이블이 없으면 Terraform이 자동으로 만들어주지 않으므로 사전 생성이 필수입니다.
유형 4. 네트워크/VPC 엔드포인트 문제 — 사설망 전용 환경
VPC 내부에서만 작동하는 사설 모듈 레지스트리나 사설 Git 서버를 쓰는 환경에서, DynamoDB VPC 엔드포인트가 누락되면 Lock 시도 자체가 타임아웃됩니다. 이때는 aws dynamodb describe-table을 동일 서브넷에서 실행해보고 응답 시간을 재보면 바로 진단돼요.
유형 5. State 파일 자체의 충돌 — 진짜 무서운 케이스
Lock은 풀렸는데 terraform plan에서 실제 리소스가 State와 다르다고 경고하는 경우예요. 이건 이미 누군가 우회로(콘솔 직접 변경, 다른 도구 사용 등)로 인프라를 건드린 상태라, Lock만 풀어서는 끝나지 않습니다. 이 케이스는 뒤에서 다루는 State 복구 절차로 이어져야 해요.
⚠️ Lock 장애 진단 주의
- 에러 로그만 보고 추측하지 마세요. DynamoDB Lock 장애는 메시지가 거의 동일하게 나오기 때문에, CloudTrail + DescribeTable로 직접 확인하는 게 정답입니다.
- force-unlock 전에 LockID 확인은 필수입니다. 잘못된 ID로 해제하면 진짜 작업 중인 사람의 Lock을 풀어 State 충돌을 만들 수 있어요.
실전 장애 사례와 전문가 해결 노하우
실제로 제가 겪었던 대표 사례 하나를 풀어볼게요. 어느 날 GitHub Actions로 운영 배포를 돌리던 중, 갑자기 모든 PR의 apply가 “Error acquiring the state lock”을 뱉기 시작했습니다. 재실행해도 똑같고, 로컬에서도 같은 메시지였어요. 처음엔 DynamoDB 장애인가 싶어서 AWS Status Page부터 봤지만 모든 서비스 정상이라고 나오더라고요.
1단계 — 진단: Lock 테이블 상태 직접 확인
aws dynamodb scan –table-name tf-locks –region ap-northeast-2을 실행했더니 평소와 달리 항목이 두 개나 잡혀 있었어요. 하나는 어제 밤 배포가 끝나고 정리 안 된 좀비 Lock, 다른 하나는 GitHub Actions의 OIDC 토큰이 새로 발급되면서 남긴 흔적이었습니다. 즉, 동시에 두 개의 Lock이 공존한 게 아니라, 좀비 Lock이 해제되지 않은 채로 새 세션이 Lock을 또 잡으려다 실패한 거예요.
2단계 — 해결: force-unlock + Lock 테이블 정리
좀비 Lock의 LockID를 복사한 뒤 terraform force-unlock <LOCK_ID>을 실행했습니다. 이때 메시지에 “Only force-unlock if you’re certain”이라는 경고가 떠요. 이유는 잘못 풀면 State가 꼬이기 때문이고, 그래서 평소엔 LockID를 PR 본문이나 Slack 로그에 남기는 룰을 운영합니다. 그리고 DynamoDB 테이블의 불필요한 항목은 DeleteItem으로 직접 청소했어요.
3단계 — 재발 방지: TTL + 알림 자동화
이후로는 Lock 테이블에 ExpiresAt(Number) 속성을 추가하고 TTL을 30분으로 설정했어요. 정상적인 apply는 보통 5~10분 내 끝나기 때문에, 30분 넘게 남은 Lock은 무조건 좀비로 간주하는 정책입니다. 그리고 CloudWatch 알람으로 Lock 항목 수 > 1일 때 Slack으로 알림을 받게 했더니, 같은 장애가 한 번도 재발하지 않더라고요.
⚠️ 장애 해결 시 주의
- force-unlock은 정말 누구도 작업 중이 아닐 때만 실행해야 합니다. 동료에게 Slack으로 한 번 더 확인하는 1분이 추후 3시간 복구를 막아줘요.
- 해제 후 곧바로 apply 하지 마세요. DynamoDB는 eventual consistency라서 다른 리전에서 보면 아직 Lock이 보이는 경우가 있거든요. 10초 정도 기다렸다가 진행합니다.
다른 Lock 방식 비교 — S3+Local, Consul, GCS와 무엇이 다른가
DynamoDB Lock만 있는 게 아닙니다. Terraform은 Backend에 따라 다양한 Lock 방식을 지원해요. AWS 환경에서도 S3+Local, Consul, GCS Lock을 선택할 수 있고, 멀티 클라우드 환경이라면 이 차이가 운영 난이도를 크게 좌우합니다. 그래서 아래 표로 한 번에 비교해 봤어요.
Lock 방식별 특성 비교표
S3 + Local File의 함정
S3에 State를 두고 Lock은 NFS 같은 공유 파일에 두는 방식이 가이드에 가끔 나오는데, 실무에선 거의 권장하지 않습니다. NFS 자체가 단일 장애점이 되는 경우가 많고, NFS가 응답 없으면 모든 apply가 멈춰요. 또 같은 시점에 두 명이 Lock 파일을 touch하면 race condition이 생겨 한 명이 자기 Lock을 풀지 못하기도 하거든요. 결국 Lock 전용 서비스를 따로 두는 게 훨씬 안전합니다.
Consul은 강력하지만 운영비가 크다
Consul의 분산 합의 기반 Lock은 견고합니다. 다만 서버 3대 이상을 띄우고 Raft quorum을 유지해야 하기 때문에, AWS 환경에서 굳이 그 비용을 들일 이유는 적어요. 이미 Consul을 서비스 디스커버리로 운영 중이라면 도입 가치가 있고, 그렇지 않다면 DynamoDB가 운영비 대비 정답입니다.
💡 Lock 방식 선택 꿀팁
- AWS에 머무는 한 DynamoDB Lock이 운영난이도 대비 최고입니다. 글로벌 테이블을 켜두면 멀티 리전 Lock 충돌까지 거의 해결돼요.
- 멀티 클라우드를 쓴다면 Cloud Storage + GCS Lock 또는 Consul을 검토하되, 단일 클라우드에선 절대 권장하지 않습니다.
State 복구와 재해 복구 절차 한 번에 끝내기
Lock을 풀었다고 끝이 아닙니다. Lock을 풀었는데도 plan이 비정상이거나, State 파일 자체가 손상됐다면 별도 복구 절차가 필요해요. 다섯 단계로 정리할게요.
1단계 — 반드시 백업부터
terraform state pull > backup.tfstate으로 현재 State를 받아두세요. S3 Backend라면 aws s3 cp s3://버킷/state.tfstate ./backup.tfstate로도 받을 수 있어요. 복구 시도는 거의 항상 가역적이지만, 만에 하나를 위해 백업은 필수입니다.
2단계 — 진단: State와 실제 인프라가 어디서 갈라졌는지 찾기
terraform plan -refresh-only를 실행하면 실제 AWS 자원과 State를 비교해서 변경 사항을 보여줘요. 여기서 큰 폭의 변경이 보이면 이미 누군가 우회로 인프라를 건드린 거고, 그 범위를 파악해야 합니다. 진단 없이 복구부터 하면 문제를 더 키울 수 있어요.
3단계 — 부분 복구: terraform import / state rm
특정 리소스만 어긋났다면 terraform import로 State에 다시 들이고, 반대로 State에는 있는데 실제로 없는 리소스라면 terraform state rm으로 정리해요. 이 두 명령을 적절히 �으면 전체 복구 없이도 대부분 복원됩니다.
4단계 — 전체 복구: state push 후 검증
State 파일 자체가 망가졌다면 마지막 수단으로 terraform state push를 씁니다. 단, 이 명령은 매우 위험해서 같은 코드를 그대로 배포한 신규 환경에서 받은 State로만 푸시해야 안전해요. 그리고 푸시 후엔 terraform plan을 돌려서 변경 사항이 0건인지 반드시 확인합니다.
5단계 — 재발 방지: 백업 자동화와 모니터링
S3 버전 관리를 켜두면 State 파일도 자동 버전 관리가 돼요. EventBridge + Lambda로 매일 State를 별도 버킷에 복사하고, CloudWatch 알람으로 State 파일 변경 이벤트를 Slack에 흘려보내면 사고를 빠르게 감지할 수 있습니다. 예방 모니터링이 결국 Lock 장애를 줄이는 가장 확실한 방법이에요.
자주 묻는 질문
Q. force-unlock을 아무 때나 해도 안전한가요?
A. 아닙니다. 누군가 실제로 작업 중이라면 State가 꼬일 수 있어요. 반드시 LockID의 Operation과 시간을 확인하고, 팀원에게 한 번 더 확인한 뒤 실행하셔야 합니다.
Q. DynamoDB Lock 테이블을 실수로 지웠다면 어떻게 복구하나요?
A. PITR(Point-in-Time Recovery)을 켜뒀다면 가장 가까운 시점으로 복원 가능합니다. 그렇지 않다면 테이블을 새로 만들고, 기존 State 파일이 멀쩡하면 그대로 진행할 수 있어요.
Q. Lock 테이블은 State 파일과 같은 리전에 둬야 하나요?
A. 같은 리전이 가장 안전하고, 멀티 리전이라면 글로벌 테이블을 권장합니다. 리전이 다르면 지연 시간 때문에 force-unlock이 자주 발생할 수 있어요.
Q. State 파일이 손상됐다고 판단되는 기준은 무엇인가요?
A. terraform plan에서 모든 리소스가 새로 만들어지려 하거나, JSON 파싱 에러가 발생하면 손상을 의심해야 합니다. 손상 판단 전엔 반드시 terraform state pull로 백업부터 받으세요.
Q. Consul Lock과 DynamoDB Lock 중 무엇을 먼저 시도해볼 만한가요?
A. AWS 단일 환경이면 DynamoDB가 운영 부담이 가장 적어요. Consul은 이미 서비스 디스커버리로 운영 중이라면 도입 가치가 있고, 그렇지 않다면 새로 띄울 이유가 거의 없습니다.
Terraform State Lock 장애는 한 번 겪으면 다시는 겪기 싫은 종류의 이슈입니다. 하지만 위에서 정리한 진단 순서와 해결 절차를 팀 위키에 한 번만 올려두면, 새벽에 장애가 터져도 30분 안에 해결할 수 있어요. 그리고 무엇보다 LockID를 로그에 남기는 습관, TTL 자동 청소, CloudTrail 기반 알람 세 가지는 사전에 꼭 세팅해 두시길 권합니다. 인프라를 코드로 관리하는 조직이라면, Lock 관리만큼은 사람이 매번 손으로 하지 말고 시스템에 맡기는 게 정답이에요.
면책조항
본 글에서 다루는 명령어와 절차는 일반적인 운영 가이드이며, 실제 환경에 적용하기 전에는 반드시 테스트 환경에서 충분히 검증하시길 권합니다. AWS 서비스 및 Terraform 버전 변경에 따라 동작이 달라질 수 있으며, 본 글 작성자의 통제 범위 밖에서 발생하는 장애나 데이터 손실에 대해 책임지지 않습니다. force-unlock, state push 등 가역성이 낮은 명령은 실행 전 백업과 동료 확인이 필수입니다.