Terraform 실전 · 라이프사이클과 드리프트 · 실습
라이프사이클로 통제하고 드리프트 되돌리기
목표
lifecycle 네 가지 인자로 교체·삭제·무시를 통제하고, 코드 밖에서 생긴 변경을 감지해 두 가지 방향으로 대응한 뒤 그 판정을 스크립트로 자동화합니다.
왜 중요한가
인프라 코드가 어긋나는 방식은 늘 같습니다 — 급한 일이 생겨 누군가 콘솔에서 직접 고치고, 그 변경이 코드로 돌아오지 않는 것입니다. 그 상태로 며칠이 지나면 전혀 다른 이유로 실행된 apply 가 새벽의 응급 조치를 조용히 되돌립니다. 그래서 드리프트 대응에서 진짜 어려운 부분은 명령어가 아니라 방향을 정하는 판단입니다. 응급 조치가 잘못이었다면 코드 기준으로 되돌리고, 옳은 판단이었다면 코드를 실물에 맞춰 고쳐야 합니다. 이 판단 없이 자동 복구를 돌리는 것이 가장 위험합니다. lifecycle 블록은 이 흐름에 미리 개입하는 장치입니다. 특히 ignore_changes 는 오토스케일러처럼 의도적으로 코드 밖에서 변하는 값을 감지 대상에서 빼서 거짓 경보를 줄이고, prevent_destroy 는 지워지면 안 되는 자원에 브레이크를 겁니다. 마지막으로 판정은 사람 눈이 아니라 -detailed-exitcode 로 해야 크론과 CI 에 얹을 수 있습니다.
단계
1. /root/tf/lifecycle/main.tf 에 local_file 리소스 endpoint 를 선언하세요. 파일 경로는 /root/tf/lifecycle/out/endpoint.txt, 내용은 줄바꿈 없는 한 줄 문자열입니다. 이 리소스의 lifecycle 블록에 create_before_destroy = true 를 넣고 tofu init 후 적용하세요.
2. local_file 리소스 statefile_guard(파일 /root/tf/lifecycle/out/guard.txt)를 추가하고 lifecycle 에 prevent_destroy = true 를 넣어 적용하세요. 그다음 그 리소스만 대상으로 삭제를 시도하고(tofu destroy -target=local_file.statefile_guard) 출력을 표준 오류까지 포함해 /root/tf/lifecycle/out/prevent.txt 로 저장하세요. 거부 메시지가 남아야 하고, 상태의 statefile_guard 와 guard.txt 는 그대로 살아 있어야 합니다.
3. local_file 리소스 하나를 파일 /root/tf/lifecycle/out/managed-note.txt 로 만들되 내용을 ignored-value 로 두고 적용하세요. 그다음 lifecycle 에 ignore_changes = [content] 를 넣고 코드의 내용 값만 다른 문자열로 바꾼 뒤 tofu plan 출력을 /root/tf/lifecycle/out/ignore-plan.txt 로 저장하세요. 계획은 No changes 여야 하고 실제 파일 내용은 여전히 ignored-value 여야 합니다.
4. null_resource 리소스 하나를 triggers 로 버전 값을 물고 있게 만들고, local_file 리소스 checksum(파일 /root/tf/lifecycle/out/checksum.txt)에 replace_triggered_by = [null_resource.<이름>] 을 거세요. checksum 자신의 인자는 그대로 둔 채 버전 값만 바꾸고 tofu plan -out=<계획파일> 로 저장한 뒤 tofu show -json <계획파일> 결과를 /root/tf/lifecycle/out/trigger-plan.json 으로 저장하세요. JSON 의 resource_changes 에서 local_file.checksum 의 동작이 두 개(삭제와 생성)여야 합니다. 확인 후 적용하세요.
5. 코드를 거치지 않고 /root/tf/lifecycle/out/endpoint.txt 를 직접 고쳐 내용에 hand-edited 가 들어가게 하세요. 그다음 tofu plan -refresh-only -detailed-exitcode -out=<계획파일> 을 실행해 종료 코드만 /root/tf/lifecycle/out/drift-exit.txt 에 적고(2 여야 합니다), tofu show -json <계획파일> 결과를 /root/tf/lifecycle/out/drift.json 으로 저장하세요. JSON 의 resource_drift 에 endpoint 가 들어 있어야 합니다. 이 계획은 적용하지 마세요.
6. tofu apply 로 실물을 코드 기준으로 되돌리고 출력을 /root/tf/lifecycle/out/remediate.txt 로 저장하세요. Apply complete 문구가 남아야 하고, endpoint.txt 에서 hand-edited 가 사라지고 파일 내용이 상태에 기록된 content 값과 정확히 같아야 합니다.
7. local_file 리소스 quota(파일 /root/tf/lifecycle/out/quota.txt)를 내용 256 으로 적용해 두세요. 이번에는 파일을 손으로 512 로 고친 뒤 되돌리지 말고 main.tf 의 값을 512 로 바꿔 코드에 흡수하고 적용하세요. 그다음 tofu plan 출력을 /root/tf/lifecycle/out/accept-plan.txt 로 저장하면 No changes 여야 하고, quota.txt 와 main.tf 모두 512 를 담고 있어야 합니다.
8. /root/tf/lifecycle/drift-report.sh 를 만들고 실행 권한을 주세요. 이 스크립트는 -detailed-exitcode 를 쓰는 계획 명령으로 드리프트를 판정하고, 결과를 /root/tf/lifecycle/out/drift-report.json 에 exit_code, drift, checked_at, addresses 네 필드로 남겨야 합니다. 실습을 마무리한 시점에는 드리프트가 없어야 하므로 exit_code 는 0, drift 는 false 여야 합니다.
참고
- 이 실습의 상태 파일은
/root/tf/lifecycle/terraform.tfstate입니다. 드리프트 판단은 이 상태와 실물을 비교해 나옵니다. -detailed-exitcode는 변경 없음이면 0, 오류면 1, 변경 있음이면 2 입니다.drift필드는 이 값이 2 일 때만true여야 합니다.tofu show -json <계획파일>은 계획을 기계가 읽을 수 있는 형태로 뱉습니다.resource_changes는 앞으로 할 일,resource_drift는 코드 밖에서 이미 벌어진 일입니다.local_file의 내용은 줄바꿈 없이 한 줄로 두세요. 6번은 파일 내용과 상태에 기록된 값을 글자 단위로 비교합니다.- 흔한 실수 1: 8번 스크립트에
set -e를 넣는 것. 종료 코드 2 에서 스크립트가 그대로 죽어 보고서가 만들어지지 않습니다. - 흔한 실수 2: 3번에서
ignore_changes를 먼저 넣고 적용하는 것. 무시할 값이 상태에 들어간 뒤에 코드를 바꿔야 효과가 보입니다. - 흔한 실수 3: 5번에서 코드를 고쳐 놓고 드리프트라고 부르는 것. 드리프트는 코드를 거치지 않은 변경만을 말합니다.
단계 8개
- create_before_destroy 걸기
- prevent_destroy 로 삭제 막기
- ignore_changes 로 특정 속성 무시하기
- 다른 자원 변경으로 재생성 유발하기
- 코드 밖 변경을 드리프트로 감지하기
- 코드 기준으로 실물 되돌리기
- 드리프트를 코드에 흡수하기
- 드리프트 감지 스크립트와 보고서 만들기