Terraform "Error acquiring the state lock" 해결법: force-unlock부터 DynamoDB 락 삭제까지
지금 파이프라인이 멈춰 있고 1초가 급하다면? 개념 설명 건너뛰고 4. 안전한 해제 절차로 바로 내려가세요. 다만 force-unlock을 치기 전에 2번 판독표 한 줄만 보고 가는 걸 강력히 권합니다. 잘못 풀면 state가 깨집니다.
terraform apply를 돌렸는데 빨간 글씨로 Error acquiring the state lock이 떴다면, 누군가(혹은 다른 CI 잡, 혹은 30분 전에 Ctrl+C로 죽인 나 자신)가 같은 state에 락을 걸어둔 상태입니다. 무작정 force-unlock을 치는 게 가장 흔한 실수예요. 떠 있는 락이 진짜 살아있는 작업의 락이면 강제 해제 순간 두 프로세스가 동시에 state를 쓰면서 인프라가 꼬입니다.
이 글은 에러 로그를 5초 만에 판독 → 원인 분기 → 안전 해제 → 재발 방지까지, 복붙 가능한 명령과 표만으로 끝냅니다.
1. 에러 원문 로그 판독표 — 5초 진단
Terraform이 던지는 락 에러는 보통 이렇게 생겼습니다.
Error: Error acquiring the state lock
Error message: ConditionalCheckFailedException: The conditional
request failed
Lock Info:
ID: a1b2c3d4-5e6f-7890-abcd-ef1234567890
Path: my-bucket/env/prod/terraform.tfstate
Operation: OperationTypeApply
Who: user@hostname
Version: 1.7.5
Created: 2026-06-14 09:12:33.123456 +0000 UTC
Info:이 블록을 필드별로 분해해 읽으면 답이 거의 보입니다.
| 필드 | 값 예시 | 무엇을 알려주나 / 판단 기준 |
|---|---|---|
| ID | a1b2c3d4-...567890 | force-unlock에 그대로 넣을 락 ID. 이 값이 명령의 인자가 됩니다. |
| Path | my-bucket/env/prod/terraform.tfstate | 락이 걸린 S3 키 경로. 어느 환경(prod/stg)이 막혔는지 확인. DynamoDB 직접 조회 시 LockID로도 사용. |
| Operation | OperationTypeApply | apply 도중인지 plan 도중인지. apply에서 끊겼다면 state 일부가 이미 갱신됐을 수 있어 더 신중하게. |
| Who | user@hostname | 누가/어느 CI 러너가 잡았나. 본인 호스트명이면 내 이전 실행, runner-xxx면 CI 잡. 모르는 동료면 먼저 물어보세요. |
| Created | 2026-06-14 09:12:33 UTC | 언제 잡혔나. 지금 시각과 차이가 크면(예: 30분 전) 비정상 종료로 남은 유령 락일 확률이 높습니다. |
| Version | 1.7.5 | 락을 건 Terraform 버전. 팀 내 버전 불일치 디버깅용 참고값. |
핵심은 Who + Created 조합입니다. "내 호스트명 + 한참 전 시각"이면 거의 유령 락이고, "다른 CI 러너 + 방금 전 시각"이면 실제 실행 중인 작업이니 건드리지 말고 기다려야 합니다.
2. 원인별 분기 진단 — 내 락은 유령인가, 살아있나
판독표로 좁혔다면 아래 5가지 중 하나에 해당합니다.
| 원인 | 대표 증상 | 1차 확인 | 조치 |
|---|---|---|---|
| 이전 실행 비정상 종료(Ctrl+C, OOM kill) | Who가 본인, Created가 오래됨 | Created 시각이 현재와 크게 차이 | force-unlock 안전하게 가능 |
| CI 동시 실행 충돌 | Who가 CI 러너, 다른 잡이 동시 실행 중 | 파이프라인에서 같은 워크플로 동시 실행 여부 | 잡 종료 대기 후 동시성 제어 도입 |
| 네트워크 끊김 | apply 중 연결 끊긴 뒤 멈춤 | 러너/로컬 로그에 timeout·connection reset | 러너 죽었음 확인 후 락 해제 + 재시도 |
| DynamoDB 권한 부족 | AccessDeniedException 동반 | IAM 정책 점검 | dynamodb:GetItem/PutItem/DeleteItem 권한 부여 |
| S3 backend 설정 오류 | dynamodb_table 누락·오타 | backend 블록 검증 | 설정 수정 후 terraform init -reconfigure |
현장 경험 한 줄: 제가 운영하던 팀에서 락 에러의 80%는 첫 번째와 두 번째였습니다. 새벽에 OOM으로 죽은 잡이 남긴 유령 락, 그리고 같은 main 브랜치 머지가 연달아 트리거되며 두 apply가 겹친 경우죠. 그래서 CI 동시성 제어 한 줄(5번)이 사실상 가장 가성비 좋은 근본 대책이었습니다.
3. 살아있는 락인지 먼저 확인하기
force-unlock 전에 Who의 러너/사람이 실제로 돌고 있는지 반드시 교차 확인하세요.
- CI 러너라면: GitHub Actions / GitLab에서 해당 잡이 아직
running인지 확인. running이면 절대 풀지 말고 끝나길 대기. - 본인 로컬이라면: 다른 터미널에 살아있는
terraform프로세스가 있는지 확인.
ps aux | grep terraform | grep -v grep프로세스가 없고 CI 잡도 모두 종료 상태라면, 그 락은 유령입니다. 안심하고 4번으로.
4. 안전한 해제 절차 (복붙용)
4-1. 1순위: terraform force-unlock
판독표에서 본 ID 값을 그대로 넣습니다.
# 반드시 다른 사람/CI가 실행 중이 아님을 확인한 뒤!
terraform force-unlock a1b2c3d4-5e6f-7890-abcd-ef1234567890
# 자동화/비대화형(CI) 환경에서 확인 프롬프트 없이
terraform force-unlock -force a1b2c3d4-5e6f-7890-abcd-ef1234567890⚠️ 위험성 경고 실제 실행 중인 작업의 락을 강제 해제하면 두 프로세스가 동시에 state를 쓰면서 state 파일이 손상되거나, 리소스가 중복 생성/삭제될 수 있습니다. force-unlock은 "그 작업이 확실히 죽었다"는 확신이 있을 때만 사용하세요. apply 도중 끊긴 락이라면 해제 후 반드시
terraform plan으로 실제 상태와의 드리프트부터 점검합니다.
4-2. 최후 수단: DynamoDB 락 테이블 직접 삭제
force-unlock이 ID 불일치나 backend 깨짐으로 안 먹힐 때, DynamoDB 항목을 직접 건드립니다.
# 현재 락 항목 조회 (LockID = "<bucket>/<key>" 형식)
aws dynamodb get-item \
--table-name terraform-locks \
--key '{"LockID":{"S":"my-bucket/env/prod/terraform.tfstate"}}'
# 락 항목 강제 삭제 (force-unlock이 안 먹힐 때 최후 수단)
aws dynamodb delete-item \
--table-name terraform-locks \
--key '{"LockID":{"S":"my-bucket/env/prod/terraform.tfstate"}}'-md5 접미사 주의: DynamoDB에는 보통 두 종류의 항목이 들어 있습니다.
my-bucket/env/prod/terraform.tfstate→ 실제 락 항목. 삭제 대상은 이것.my-bucket/env/prod/terraform.tfstate-md5→ state 파일의 체크섬(다이제스트) 항목. 무결성 검증용이라 삭제하면 안 됩니다. 이걸 지우면 다음 실행에서 state 검증 경고가 뜰 수 있어요.
즉 락만 풀고 싶다면 -md5가 붙지 않은 LockID만 delete-item 하세요.
5. 재발 방지 체크리스트
한 번 겪었으면 다시는 안 겪게 막는 게 진짜 실력입니다.
5-1. lock-timeout으로 즉시 실패 대신 대기
# 락이 잡혀 있으면 바로 실패하지 말고 120초까지 재시도하며 대기
terraform apply -lock-timeout=120s짧게 겹치는 동시 실행은 이 한 줄로 자연스럽게 줄을 서게 됩니다.
5-2. backend 설정 표준화
terraform {
backend "s3" {
bucket = "my-bucket"
key = "env/prod/terraform.tfstate"
region = "ap-northeast-2"
dynamodb_table = "terraform-locks"
encrypt = true
}
}dynamodb_table을 빼먹으면 애초에 락 자체가 안 걸려 동시 쓰기 사고가 납니다. 환경별 key는 반드시 분리하세요.
5-3. CI 동시성 제어 — 가장 효과적인 근본 대책
GitHub Actions — 같은 워크플로의 중복 실행을 직렬화:
concurrency:
group: terraform-prod
cancel-in-progress: false # 진행 중 잡을 죽이지 말고 줄 세우기GitLab CI — resource_group으로 동일 리소스 잡 직렬화:
deploy_prod:
stage: deploy
resource_group: terraform-prod
script:
- terraform apply -auto-approve -lock-timeout=120s이 두 줄이면 "main 연속 머지로 apply 두 개가 겹쳐 락 충돌" 시나리오가 사라집니다.
5-4. 참고: DynamoDB 없는 S3 native locking
Terraform 1.10+ 부터는 use_lockfile = true 옵션으로 DynamoDB 테이블 없이 S3 자체에 락 파일을 두는 방식이 가능해졌습니다.
terraform {
backend "s3" {
bucket = "my-bucket"
key = "env/prod/terraform.tfstate"
region = "ap-northeast-2"
use_lockfile = true # DynamoDB 불필요
encrypt = true
}
}| 구분 | DynamoDB 방식 | S3 native(use_lockfile) |
|---|---|---|
| 추가 리소스 | DynamoDB 테이블 필요 | S3 버킷만으로 가능 |
| 적용 버전 | 모든 버전 | Terraform 1.10+ |
| 락 해제 | force-unlock / delete-item | force-unlock / S3 .tflock 객체 삭제 |
📌 OpenTofu 사용자도 위
terraform force-unlock→tofu force-unlock으로 바꾸면 동일하게 적용됩니다. DynamoDB/S3 CLI 명령은 그대로예요.
자주 묻는 질문 (FAQ)
Q. force-unlock을 했는데도 또 "Error acquiring the state lock"이 떠요.
A. 두 가지를 의심하세요. ① 다른 CI 잡이 여전히 실행 중이라 락을 재생성하고 있는 경우 — 잡을 먼저 멈추세요. ② backend ID 불일치로 force-unlock이 실제 항목을 못 지운 경우 — 4-2의 aws dynamodb get-item으로 LockID를 직접 확인하고 delete-item 하세요.
Q. DynamoDB에서 -md5가 붙은 항목도 같이 지워야 하나요?
A. 아니요. -md5는 state 무결성 체크섬이라 지우면 안 됩니다. 락 해제는 -md5가 붙지 않은 LockID 항목만 삭제하면 됩니다.
Q. apply 도중 끊겨서 락을 풀었는데, 인프라 상태가 불안합니다. 뭘 먼저 해야 하나요?
A. 해제 직후 곧바로 terraform plan을 돌려 실제 리소스와 state의 드리프트를 확인하세요. apply가 절반만 반영됐을 수 있으므로, plan 결과를 검토한 뒤에만 재apply 하는 것이 안전합니다.
이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.
댓글
불러오는 중...