IT/IaC

DynamoDB 락 테이블을 지우던 새벽에 대하여

gfrog 2026. 7. 31. 03:16

새벽 2시에 알림이 울렸다. 배포 파이프라인이 Error acquiring the state lock을 뱉고 멈춰 있었다. 락 ID를 들고 DynamoDB 콘솔에 들어가 강제로 항목을 지우고, 파이프라인을 재실행. 익숙한 절차였다. 근데 그날따라 이 절차가 어이없게 느껴졌다.

우리 팀은 Terraform 1.11 무렵부터 S3 backend + DynamoDB 락을 써 왔다. 몇 년 잘 돌던 조합이다. 그런데 올봄에 팀 내부 논의 끝에 DynamoDB 락 테이블을 정리하기로 했다. 이유는 두 가지였다. 하나는 Terraform과 OpenTofu 양쪽 다 S3 네이티브 락을 밀고 있고 DynamoDB 방식이 사실상 deprecated가 됐다는 것. 다른 하나는 우리 조직에 IaC state가 40개 넘게 흩어져 있는데, 그때마다 DynamoDB 테이블을 하나씩 붙여 두는 것이 관리 부담이 됐다는 것이다.

use_lockfile = true 하나로 끝나는가

문서만 보면 참 쉬워 보였다.

terraform {
  backend "s3" {
    bucket         = "kurly-tfstate-prod"
    key            = "network/vpc/terraform.tfstate"
    region         = "ap-northeast-2"
    dynamodb_table = "terraform-locks"
    use_lockfile   = true
  }
}

use_lockfile = true를 붙이면 S3 오브젝트 락과 DynamoDB 락을 동시에 잡는다. 이 상태로 한동안 굴리다가, 언젠가 dynamodb_table 라인만 지워버리면 마이그레이션 완료. 그런 그림이었다.

여기까지는 계획대로 됐다. 문제는 DynamoDB 테이블을 실제로 지우기로 한 그 다음 단계였다.

몇 개의 state가 아직 옛날 버전이었는가

우리 팀은 Atlantis로 PR 기반 apply를 돌린다. Atlantis 이미지 안의 Terraform 버전은 1.13. 근데 use_lockfile은 Terraform 1.10 이상에서 지원되고, S3 락은 그것대로 백엔드 설정을 감지한다. 여기서 나는 한 가지를 놓쳤다. 팀 내부의 유틸리티 저장소 몇 개가 아직 1.9로 고정돼 있었다는 것이다. 이런 저장소는 use_lockfile 옵션을 만나면 그냥 파싱 에러로 죽는다.

배포 스크립트 로그를 되짚어 보다가 Error: Unsupported argument 라인을 마주쳤을 때 좀 멘탈이 흔들렸다. 프로덕션 스택은 이미 마이그레이션이 끝났는데, 그 뒤에 딸린 부수 저장소들이 apply를 못 하고 있는 상황.

해결은 단순했다. Atlantis 이미지를 통일하고, .terraform-version을 각 저장소 루트에 두고, tfenv로 강제. 그게 다다. 그런데 이걸 사전에 확인 안 하고 use_lockfile부터 뿌린 게 정말 부끄러웠다. 40개짜리 저장소를 손으로 뒤지는 새벽 시간이 아깝기도 하고.

그래서 DynamoDB는 언제 지웠냐면

정확히는 두 주 정도 병행 운영했다. 그 사이 팀에서 배포한 각 state의 use_lockfile 락 파일이 S3에 실제로 남는지, 배포 도중 락이 잘 걸렸다가 잘 해제되는지를 확인했다. 두 주 정도 지나서 로그를 훑어보니 DynamoDB 항목이 걸리는 건 자동화 실패 케이스 두 건이 전부였다. 이 정도면 S3 락만으로 살 만하다 싶어서, 팀 슬랙에 공지하고 DynamoDB 테이블을 삭제.

삭제 직후에 backend 블록의 dynamodb_table 라인을 다 걷어냈다. 이 작업은 그냥 sed로 처리했다.

find . -name "backend.tf" -exec \
  sed -i '' '/dynamodb_table[[:space:]]*=/d' {} \;

Terraform init을 다시 돌리면 backend 설정이 바뀌었다며 마이그레이션을 묻는다. -migrate-state를 붙여서 넘긴다. state 파일 위치가 안 바뀌었으므로 실제 옮겨지는 데이터는 없다. 그냥 metadata만 정리되는 셈이다.

놓치기 쉬운 것들

한 가지 나중에 알게 된 사실. S3 락 파일은 state 파일 옆에 terraform.tfstate.tflock 형태로 생긴다. 파이프라인이 갑자기 죽으면 이 파일이 남는다. DynamoDB 시절에는 강제 삭제할 항목이 하나였는데, 이제는 S3 오브젝트를 지워야 한다. tofu force-unlock terraform force-unlock이 그 일을 대신해 준다. 근데 사용법이 살짝 다르다. 이걸 wiki에 정리해서 팀에 공유했다.

또 한 가지. 우리 조직은 IAM 정책을 tightly scoped로 관리하는데, DynamoDB 락 시절의 IAM 정책에는 여전히 dynamodb:PutItem이 남아 있었다. 이건 즉시 문제는 안 되지만 관성적으로 붙어 있는 권한이니 이번 기회에 걷어냈다. IAM 정책도 결국은 코드고, 시간이 지나면 죽은 권한이 쌓인다.

지나고 보니

솔직히 이 마이그레이션은 배포 안정성 자체를 크게 개선하진 않았다. S3 락이 특별히 더 빠르거나 안정적이라고 체감할 정도는 아니다. 다만 관리해야 할 인프라 요소가 하나 줄었다는 게 크다. DynamoDB 테이블 40여 개, 그것에 딸린 CloudWatch 알람, IAM 정책, 백업 정책. 이런 것들이 다 자잘한 유지비였다.

앞으로 새 state를 만들 때 backend 블록이 한 줄 짧아진 것도 좋다. 이거 은근히 뿌듯하다.

혹시 아직 DynamoDB 락 쓰고 계신 분, 급하지는 않지만 6개월 안에는 정리하는 걸 추천드린다. 나처럼 Terraform 버전 통일부터 시작하시길.