terraform state lock 에러 해결: 원인 확인과 force-unlock 판단 기준

4 분 소요

terraform plan이나 apply를 실행했는데 다음 에러가 나옵니다.

에러 메시지
Error: Error acquiring the state lock

Error message: operation error S3: PutObject, ...
Lock Info:
  ID:        b2f1c4d8-...
  Path:      myapp-tfstate/myapp/prod/terraform.tfstate
  Who:       bob@bobs-laptop
  Created:   2026-09-17 02:14:33 UTC

결론을 앞에 두겠습니다. 이 에러의 대부분은 고장이 아니라 정상 동작이고, 첫 대응은 명령이 아니라 기다림입니다. 잠금은 두 실행이 같은 state를 동시에 고치는 사고를 막으려고 테라폼이 일부러 걸어 둔 것이기 때문입니다. 문제는 잠금이 남아서는 안 되는 상황에 남아 있는 소수의 경우이고, 이 글은 그 둘을 구분하는 절차입니다.

Lock Info를 먼저 읽습니다 #

에러 본문의 Lock Info에 판단 재료가 다 있습니다.

  • Who: 잠금을 건 실행 주체입니다. 동료의 계정이면 그 사람의 apply가 진행 중일 가능성이 높고, CI 러너 이름이면 파이프라인이 돌고 있는 것입니다.
  • Created: 잠금이 걸린 시각(UTC)입니다. 몇 분 전이면 진행 중일 확률이 높고, 몇 시간·며칠 전이면 잔존 잠금을 의심할 수 있습니다.
  • ID: 잠금 식별자입니다. 해제 명령에 필요하니 복사해 둡니다.

원인은 셋 중 하나입니다 #

  1. 다른 사람·CI가 실행 중(정상): 가장 흔한 경우입니다. apply는 리소스에 따라 수십 분씩 걸릴 수 있으므로(RDS 생성 등), Created가 최근이라면 끝나기를 기다리는 것이 정답입니다.
  2. 실행이 비정상 종료되어 잠금만 남음: 터미널 강제 종료, 노트북 절전, CI 러너 강제 취소가 원인입니다. 실행은 죽었는데 잠금 해제 단계까지 가지 못한 경우로, 이때만 수동 해제가 필요합니다.
  3. 권한 문제로 잠금 확인 자체가 실패: Lock Info 없이 액세스 거부 계열 메시지가 나오면 잠금이 아니라 자격 증명·IAM 문제입니다. 이 글의 범위 밖이므로 state 버킷 접근 권한을 먼저 확인합니다.

해제 판단 절차 #

순서대로 확인하고, 전부 통과했을 때만 해제합니다.

  1. Who에게 직접 확인합니다. 실행 중이라는 답이 오면 기다립니다. CI 러너라면 해당 파이프라인의 실행 현황 화면을 확인합니다.
  2. Created가 충분히 오래됐는지 봅니다. 팀의 가장 긴 apply 시간(보통 수십 분)보다 오래된 잠금이면 잔존일 가능성이 높습니다.
  3. 둘 다 확인됐으면 잠금 ID로 해제합니다.
잠금 해제
terraform force-unlock b2f1c4d8-...

ID는 Lock Info의 값을 그대로 씁니다. 해제 후 plan을 한 번 돌려 state가 온전한지 확인하면 끝입니다.

force-unlock을 함부로 쓰면 안 되는 이유 #

에러를 검색하면 “force-unlock 하면 됩니다"라는 답이 많이 나오는데, 확인 절차 없이 쓰는 것은 위험합니다. 진행 중인 apply의 잠금을 풀고 내 apply를 시작하면, 두 실행이 같은 state를 동시에 쓰는 상황이 됩니다. 잠금이 막으려던 바로 그 사고입니다. 한쪽의 기록이 덮어써지면 state와 실제 인프라가 어긋나고, 이 복구는 잠금 대기와 비교할 수 없이 고된 작업입니다. force-unlock은 “실행자가 없음을 확인한 뒤의 마지막 수단"이라는 위치를 지켜야 합니다.

DynamoDB 잠금 방식이라면 #

S3 백엔드의 잠금은 현행 표준인 use_lockfile = true(state 옆의 .tflock 객체) 외에, 예전 표준인 DynamoDB 테이블 방식(dynamodb_table 인수)이 아직 많이 남아 있습니다. 동작과 판단 절차는 같고, 잔존 잠금의 실체가 다를 뿐입니다. DynamoDB 방식에서는 지정한 테이블의 LockID 항목이 잠금이며, force-unlock이 실패하는 예외 상황에서는 콘솔에서 해당 항목을 직접 확인할 수 있습니다. 두 방식의 차이와 마이그레이션은 테라폼 기초 강좌 #9에서 다뤘습니다.

재발 줄이기 #

잔존 잠금의 단골 원인은 사람의 강제 종료와 CI의 동시 실행입니다. 사람 쪽은 apply를 중간에 끊지 않는 습관(끊어야 한다면 테라폼이 정리할 시간을 주는 Ctrl+C 한 번까지만), CI 쪽은 같은 state를 만지는 잡을 직렬화하는 concurrency 설정이 답입니다. CI 워크플로 구성은 테라폼 운영 강좌 #1에 정리해 두었습니다.

정리 #

  • state lock 에러는 대부분 정상 신호입니다. 첫 대응은 force-unlock이 아니라 Lock Info 확인과 대기입니다
  • Who에게 확인하고, Created가 팀의 최장 apply 시간보다 오래됐을 때만 잔존 잠금으로 판단합니다
  • 해제는 terraform force-unlock <잠금ID>이며, 진행 중 실행의 잠금을 풀면 state 동시 쓰기 사고가 됩니다
  • DynamoDB 방식은 잠금의 실체가 테이블 항목이라는 점만 다르고 판단 절차는 같습니다
  • 재발 방지는 apply를 끊지 않는 습관과 CI의 concurrency 직렬화입니다
X