IT/IaC

terraform plan -refresh=false, CI 시간 절반으로 줄인 한 줄

gfrog 2026. 6. 4. 21:14
반응형

이거 모르는 분 꽤 많더라. 우리 팀 모듈 plan에 8분 넘게 걸리던 게 4분으로 줄었다. 코드 한 줄도 안 고쳤다.

뭐가 문제였냐면

terraform plan 돌리면 기본적으로 state에 있는 모든 리소스를 클라우드 API에 한 번씩 조회한다(refresh). 리소스 200개짜리 모듈이면 200번 API 콜이 도는데, AWS provider 같이 throttling 걸리면 더 느려진다.

CI에서 PR마다 plan을 돌리는 입장에서는, 매번 fresh한 state가 필요한 것도 아닌데 이 refresh 때문에 시간을 다 까먹는다. 특히 우리는 monorepo에 모듈 30개 있어서 PR 하나 올리면 plan job 30개가 동시에 돌고, 그러면 AWS API throttling까지 같이 터진다.

그래서 이렇게 했다

terraform plan -refresh=false -out=tfplan

-refresh=false는 state를 신뢰하고 refresh를 스킵한다. CI에서는 일반적으로 state가 최근 apply 이후 그대로 유지되니까, drift만 걱정 안 하면 안전하다.

GitHub Actions step으로 쓰면 이런 식:

- name: Terraform Plan
  run: |
    terraform plan \
      -refresh=false \
      -lock-timeout=60s \
      -out=tfplan \
      -no-color
  working-directory: ${{ matrix.module }}

근데 drift는?

이게 좀 애매한데. PR 단계 plan에서 refresh 안 하니까 외부에서 손댄 리소스가 있어도 못 잡는다. 우리 팀은 이렇게 갈랐다.

PR 시점의 plan은 -refresh=false로 빠르게. apply 직전 plan(메인 머지 후)은 refresh 켜고 정상 실행. 그리고 매일 새벽 한 번 terraform plan -refresh-only만 별도 cron job으로 돌려서 drift 감지하고 슬랙으로 쏜다.

terraform plan -refresh-only -detailed-exitcode
# exit code 2 = drift 있음

-refresh-only는 state만 업데이트하고 리소스 변경은 제안하지 않는 모드다. 1.1부터 standalone terraform refresh를 대체하고 들어왔는데, 의외로 안 쓰는 팀이 많다.

실제 효과

우리 monorepo 기준으로:

단계 이전 이후
PR plan (모듈 30개 병렬) 8~12분 3~5분
AWS API throttle 에러 주 2-3건 0건
Apply 시점 plan 변화 없음 변화 없음

CI 비용도 줄었다. GitHub Actions 분당 과금 생각하면 한 달에 꽤 차이 난다.

주의할 것

다 좋은데 함정도 있다. -refresh=false로 plan 떠서 본 결과가 실제 인프라와 다를 수 있다는 점. 누가 콘솔에서 보안 그룹 룰 하나 지웠는데 plan에는 안 보일 수 있다. 그래서 apply 직전 plan은 꼭 refresh를 켜라.

그리고 처음 도입할 때 한 가지 더 — 기존에 state가 오래된 모듈이면 일단 한 번은 refresh 켜서 plan 떠보고 drift를 정리한 다음에 -refresh=false로 전환하는 게 안전하다. drift 쌓여있는 상태에서 바로 끄면 나중에 봐도 뭐가 진짜 변경인지 구분 안 된다.

혹시 더 좋은 패턴 쓰시는 분 있으면 댓글로 알려주세요.

반응형