terraform plan을 실행했는데 "Error acquiring the state lock"이 뜨면서 아무 작업도 진행되지 않습니다. 팀원이 작업 중인 것도 아닌데 lock이 걸려 있습니다. CI/CD 파이프라인이 중간에 실패하면서 lock이 해제되지 않은 경우, 이 상황이 자주 발생합니다.
핵심 요약
| 구분 | 내용 |
|---|---|
| 에러 메시지 | Error acquiring the state lock |
| 원인 | 이전 Terraform 실행이 비정상 종료되면서 DynamoDB Lock이 해제되지 않음 |
| 즉시 해결 | terraform force-unlock <LOCK_ID> |
| 근본 원인 | CI/CD timeout, 수동 중단(Ctrl+C), 네트워크 단절, 프로세스 강제 종료 |
| 재발 방지 | CI/CD timeout 설정, Graceful shutdown, Lock 모니터링 알람 |
1. 에러 메시지 원문
│ Error: Error acquiring the state lock
│
│ Error message: ConditionalCheckFailedException: The conditional request failed
│ Lock Info:
│ ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
│ Path: my-terraform-state/prod/terraform.tfstate
│ Operation: OperationTypePlan
│ Who: runner@github-actions-12345
│ Version: 1.8.2
│ Created: 2026-06-04 09:15:32.123456 +0000 UTC
│ Info:
│
│ Terraform acquires a state lock to protect the state from being written
│ by multiple users at the same time. Please resolve the lock issue listed
│ above before retrying this operation.
│
│ To "unlock" the state, use the force-unlock command:
│ terraform force-unlock a1b2c3d4-e5f6-7890-abcd-ef1234567890
이 에러는 Terraform이 State에 대한 Lock을 획득하려 했으나, 이미 다른 프로세스가 Lock을 보유하고 있어 실패했다는 의미입니다.
2. State Lock 동작 원리
Terraform은 plan, apply, destroy 등 State를 읽거나 쓰는 모든 작업 전에 Lock을 획득합니다. 작업이 완료되면 Lock을 해제합니다.
S3 Backend + DynamoDB 조합에서의 Lock 흐름:
terraform plan실행- DynamoDB 테이블에 Lock 레코드 생성 (Conditional Put)
- S3에서 State 파일 읽기
- Plan 계산
- DynamoDB Lock 레코드 삭제
문제는 3~4단계에서 프로세스가 비정상 종료되면 5단계(Lock 해제)가 실행되지 않는다는 것입니다.
3. 원인별 분석
3-1. CI/CD 파이프라인 timeout
가장 흔한 원인입니다.
시나리오: GitHub Actions에서 terraform apply를 실행 중입니다. 대규모 인프라 변경으로 15분이 걸리는데, Job timeout이 10분으로 설정되어 있습니다. 10분 시점에 Runner가 강제 종료되면서 Lock이 남습니다.
# GitHub Actions — timeout으로 인한 Lock 잔류 원인
jobs:
terraform:
runs-on: ubuntu-latest
timeout-minutes: 10 # 이 시간 초과 시 프로세스 강제 종료
steps:
- run: terraform apply -auto-approve
# 대규모 변경 시 10분 초과 → Lock 미해제
3-2. 수동 중단 (Ctrl+C)
로컬에서 terraform apply 실행 중 Ctrl+C를 두 번 누르면 Graceful shutdown이 아닌 즉시 종료가 됩니다.
- Ctrl+C 1회: Terraform이 현재 리소스 작업을 완료한 후 종료 (Lock 정상 해제)
- Ctrl+C 2회: 즉시 종료 (Lock 미해제)
3-3. 네트워크 단절
terraform apply 실행 중 VPN 연결이 끊기거나 네트워크 장애가 발생하면, DynamoDB에 Lock 해제 요청을 보내지 못합니다.
3-4. 동시 실행
팀원 A가 terraform plan을 실행 중인 상태에서 팀원 B가 같은 State에 대해 terraform apply를 실행하는 경우입니다. 이 경우는 정상적인 보호 메커니즘이므로 A의 작업이 완료될 때까지 기다리면 됩니다.
4. 해결 방법
4-1. 먼저 확인: 실제로 누군가 작업 중인가?
Lock을 강제 해제하기 전에 반드시 확인해야 합니다.
# Lock 정보에서 Who 필드 확인
# Who: runner@github-actions-12345 → CI/CD에서 실행된 작업
# Who: username@hostname → 특정 팀원이 로컬에서 실행 중
# DynamoDB에서 Lock 레코드 직접 확인
aws dynamodb get-item \
--table-name terraform-state-lock \
--key '{"LockID": {"S": "my-terraform-state/prod/terraform.tfstate"}}' \
--query 'Item.Info.S' \
--output text
확인 결과에 따른 판단:
| 상황 | 조치 |
|---|---|
| 팀원이 현재 작업 중 | 기다림 (정상 Lock) |
| CI/CD Job이 이미 실패/종료됨 | 강제 해제 가능 |
| Lock 생성 시간이 수 시간 전 | 높은 확률로 orphaned Lock → 강제 해제 |
| 본인이 Ctrl+C로 종료한 직후 | 강제 해제 가능 |
4-2. 강제 해제 (force-unlock)
Lock이 orphaned 상태임을 확인한 후 강제 해제합니다.
# Lock ID는 에러 메시지에 표시됨
terraform force-unlock a1b2c3d4-e5f6-7890-abcd-ef1234567890
실행하면 확인 프롬프트가 표시됩니다:
Do you really want to force-unlock?
Terraform will remove the lock on the remote state.
This will allow local Terraform commands to modify this state, even though it
may still be in use. Only 'yes' will be accepted to confirm.
Enter a value: yes
yes를 입력하면 DynamoDB에서 Lock 레코드가 삭제됩니다.
force-unlock은 실제로 작업 중인 프로세스가 없을 때만 사용해야 합니다. 다른 프로세스가 State를 수정하는 중에 Lock을 해제하면 State 파일이 손상될 수 있습니다.
4-3. DynamoDB에서 직접 삭제 (비상 상황)
드문 경우지만, terraform force-unlock이 실패할 수 있습니다. 이때는 DynamoDB에서 Lock 레코드를 직접 삭제합니다.
# DynamoDB Lock 레코드 직접 삭제
aws dynamodb delete-item \
--table-name terraform-state-lock \
--key '{"LockID": {"S": "my-terraform-state/prod/terraform.tfstate"}}'
이 방법은 Terraform CLI를 통하지 않으므로 더 위험합니다. State 파일의 무결성을 반드시 확인해야 합니다.
# Lock 해제 후 State 무결성 확인
terraform plan
# "No changes" 또는 예상된 변경만 표시되면 정상
5. 실무 시나리오: CI/CD에서 반복 발생
상황: GitHub Actions에서 Terraform을 운영하는 팀입니다. 주 2~3회 terraform apply Job이 timeout으로 실패하면서 Lock이 남습니다. 매번 수동으로 force-unlock을 실행하고 있습니다.
근본 원인 분석:
- Infrastructure 규모가 커지면서
apply시간이 증가 - Job timeout은 초기 설정(10분) 그대로 유지
- timeout 시 Runner가 SIGKILL로 프로세스를 종료하므로 Graceful shutdown 불가
해결 전략:
# 1. Job timeout 충분히 확보
jobs:
terraform:
runs-on: ubuntu-latest
timeout-minutes: 30 # apply 예상 시간 + 여유분
steps:
# 2. apply 전에 orphaned lock 자동 확인/해제
- name: Check and clear stale locks
run: |
LOCK_INFO=$(aws dynamodb get-item \
--table-name terraform-state-lock \
--key '{"LockID": {"S": "my-terraform-state/prod/terraform.tfstate"}}' \
--query 'Item.Info.S' \
--output text 2>/dev/null)
if [ "$LOCK_INFO" != "None" ] && [ -n "$LOCK_INFO" ]; then
LOCK_CREATED=$(echo "$LOCK_INFO" | jq -r '.Created')
LOCK_AGE_MINUTES=$(( ($(date +%s) - $(date -d "$LOCK_CREATED" +%s)) / 60 ))
if [ $LOCK_AGE_MINUTES -gt 30 ]; then
echo "Stale lock detected (${LOCK_AGE_MINUTES}min old). Force unlocking..."
LOCK_ID=$(echo "$LOCK_INFO" | jq -r '.ID')
terraform force-unlock -force "$LOCK_ID"
else
echo "Active lock detected (${LOCK_AGE_MINUTES}min old). Waiting..."
exit 1
fi
fi
# 3. apply 실행
- name: Terraform Apply
run: terraform apply -auto-approve
# 4. 실패 시에도 lock 해제 시도
- name: Cleanup lock on failure
if: failure()
run: |
terraform force-unlock -force $(terraform show -json 2>/dev/null | jq -r '.lock_id // empty') || true
6. 재발 방지 전략
6-1. CI/CD timeout 설정
# GitHub Actions
timeout-minutes: 30
# GitLab CI
job:
timeout: 30m
# Jenkins
timeout(time: 30, unit: 'MINUTES') {
sh 'terraform apply -auto-approve'
}
apply 시간은 인프라 규모에 따라 달라집니다. 모니터링을 통해 평균 소요 시간을 파악하고, 2배 정도의 여유를 두는 것이 일반적입니다.
6-2. State 분리로 Lock 범위 축소
하나의 State에 모든 리소스를 넣으면 apply 시간이 길어지고 Lock 충돌 확률도 높아집니다.
# 단일 State (비권장 — 대규모 환경)
infrastructure/
main.tf # VPC + EKS + RDS + ... 모든 리소스
terraform.tfstate # Lock 시간: 15분+
# 분리된 State (권장)
infrastructure/
network/ # VPC, Subnet, NAT → Lock 시간: 2분
compute/ # EKS, ASG → Lock 시간: 5분
database/ # RDS, ElastiCache → Lock 시간: 3분
State를 분리하면: - 각 apply의 실행 시간이 짧아져 timeout 위험이 줄어듦 - Lock 범위가 좁아져 팀원 간 충돌 가능성이 감소 - 한 영역의 Lock 문제가 다른 영역에 영향을 주지 않음
6-3. Lock 모니터링 알람
DynamoDB에 Lock 레코드가 일정 시간 이상 존재하면 알람을 보냅니다.
# CloudWatch 알람으로 stale lock 감지
# DynamoDB Streams + Lambda 조합
# Lock 생성 후 30분 이상 해제되지 않으면 Slack 알림
6-4. Ctrl+C 사용 규칙 (로컬 작업)
팀 내 규칙으로 공유합니다:
| 상황 | 올바른 대응 |
|---|---|
| apply 중 중단하고 싶을 때 | Ctrl+C 1회만 누르고 대기 |
| plan 중 중단하고 싶을 때 | Ctrl+C 1회 (즉시 종료됨) |
| 이미 Ctrl+C 2회 눌렀을 때 | force-unlock 실행 |
7. State Lock Backend별 차이
State Lock은 Backend 유형에 따라 메커니즘이 다릅니다.
| Backend | Lock 메커니즘 | Lock 저장 위치 |
|---|---|---|
| S3 + DynamoDB | DynamoDB Conditional Put | DynamoDB 테이블 |
| Azure Blob Storage | Blob Lease | Azure Storage Account |
| GCS (Google Cloud Storage) | Object Lock | GCS 버킷 내 .tflock 파일 |
| Terraform Cloud | 내장 Lock | Terraform Cloud 서버 |
| Consul | KV Store Lock | Consul 클러스터 |
Backend별 force-unlock 동작은 동일합니다. Terraform CLI가 Backend 유형에 맞게 Lock 해제를 처리합니다.
Azure Blob Storage Backend의 경우 Blob Lease가 만료 시간(보통 60초)을 가집니다. 프로세스가 비정상 종료되면 Lease가 자동 만료되므로, DynamoDB처럼 orphaned lock이 영구 지속되는 문제가 덜 발생합니다.
8. 정리
Error acquiring the state lock은 이전 실행이 비정상 종료되면서 Lock이 해제되지 않았을 때 발생합니다.- 강제 해제 전에 반드시 실제 작업 중인 프로세스가 없는지 확인합니다.
terraform force-unlock <LOCK_ID>로 해제하며, 해제 후terraform plan으로 State 무결성을 검증합니다.- CI/CD에서 반복 발생하면 timeout 설정, State 분리, Lock 모니터링으로 근본 원인을 해결합니다.
- Ctrl+C는 1회만 눌러 Graceful shutdown을 유도하는 것이 안전합니다.
관련 글
- Terraform State란 무엇인가: 상태 관리의 개념과 실무 운영 전략
- Terraform S3 Backend와 State Lock 구성하기: 팀 협업을 위한 원격 상태 관리
- Terraform Module을 사용하는 이유: 코드 재사용, 표준화, 운영 효율성
참고 문서
'Troubleshooting' 카테고리의 다른 글
| EKS Pod가 외부 인터넷에 접근하지 못하는 경우 (0) | 2026.06.06 |
|---|---|
| Kubernetes CrashLoopBackOff 원인과 해결 방법 (0) | 2026.06.05 |
| S3 AccessDenied 원인과 해결 방법: Bucket Policy, IAM, KMS, VPC Endpoint까지 (0) | 2026.06.01 |
| Kubernetes ImagePullBackOff 원인과 해결 방법 (0) | 2026.06.01 |
| ALB 502 Bad Gateway 원인 분석: Target Group, Health Check, 타임아웃까지 (0) | 2026.05.31 |