LabHub
学习 学习路径 课程

Terraform 实战

用生命周期管住,并把漂移撤回来

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

lifecycle 네 가지 인자로 교체·삭제·무시를 통제하고, 코드 밖에서 생긴 변경을 감지해 두 가지 방향으로 대응한 뒤 그 판정을 스크립트로 자동화합니다.

왜 중요한가

인프라 코드가 어긋나는 방식은 늘 같습니다 — 급한 일이 생겨 누군가 콘솔에서 직접 고치고, 그 변경이 코드로 돌아오지 않는 것입니다. 그 상태로 며칠이 지나면 전혀 다른 이유로 실행된 apply 가 새벽의 응급 조치를 조용히 되돌립니다. 그래서 드리프트 대응에서 진짜 어려운 부분은 명령어가 아니라 방향을 정하는 판단입니다. 응급 조치가 잘못이었다면 코드 기준으로 되돌리고, 옳은 판단이었다면 코드를 실물에 맞춰 고쳐야 합니다. 이 판단 없이 자동 복구를 돌리는 것이 가장 위험합니다. lifecycle 블록은 이 흐름에 미리 개입하는 장치입니다. 특히 ignore_changes 는 오토스케일러처럼 의도적으로 코드 밖에서 변하는 값을 감지 대상에서 빼서 거짓 경보를 줄이고, prevent_destroy 는 지워지면 안 되는 자원에 브레이크를 겁니다. 마지막으로 판정은 사람 눈이 아니라 -detailed-exitcode 로 해야 크론과 CI 에 얹을 수 있습니다.

단계

  1. /root/tf/lifecycle/main.tflocal_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)를 추가하고 lifecycleprevent_destroy = true 를 넣어 적용하세요. 그다음 그 리소스만 대상으로 삭제를 시도하고(tofu destroy -target=local_file.statefile_guard) 출력을 표준 오류까지 포함해 /root/tf/lifecycle/out/prevent.txt 로 저장하세요. 거부 메시지가 남아야 하고, 상태의 statefile_guardguard.txt 는 그대로 살아 있어야 합니다.
  3. local_file 리소스 하나를 파일 /root/tf/lifecycle/out/managed-note.txt 로 만들되 내용을 ignored-value 로 두고 적용하세요. 그다음 lifecycleignore_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.txtmain.tf 모두 512 를 담고 있어야 합니다.
  8. /root/tf/lifecycle/drift-report.sh 를 만들고 실행 권한을 주세요. 이 스크립트는 -detailed-exitcode 를 쓰는 계획 명령으로 드리프트를 판정하고, 결과를 /root/tf/lifecycle/out/drift-report.jsonexit_code, drift, checked_at, addresses 네 필드로 남겨야 합니다. 실습을 마무리한 시점에는 드리프트가 없어야 하므로 exit_code0, driftfalse 여야 합니다.

참고

create_before_destroy 걸기

/root/tf/lifecycle/main.tflocal_file 리소스 endpoint 를 선언하세요. 파일 경로는 /root/tf/lifecycle/out/endpoint.txt, 내용은 줄바꿈 없는 한 줄 문자열입니다. 이 리소스의 lifecycle 블록에 create_before_destroy = true 를 넣고 tofu init 후 적용하세요.

lifecycle 블록은 리소스 안에 들어갑니다. 교체가 필요할 때 새 것을 먼저 만들게 하는 인자를 찾으세요.

prevent_destroy 로 삭제 막기

local_file 리소스 statefile_guard(파일 /root/tf/lifecycle/out/guard.txt)를 추가하고 lifecycleprevent_destroy = true 를 넣어 적용하세요. 그다음 그 리소스만 대상으로 삭제를 시도하고(tofu destroy -target=local_file.statefile_guard) 출력을 표준 오류까지 포함해 /root/tf/lifecycle/out/prevent.txt 로 저장하세요. 거부 메시지가 남아야 하고, 상태의 statefile_guardguard.txt 는 그대로 살아 있어야 합니다.

이 인자는 계획 단계에서 오류를 냅니다. 대상 하나만 지정해 삭제를 시도하고 그 출력을 통째로 저장하세요.

ignore_changes 로 특정 속성 무시하기

local_file 리소스 하나를 파일 /root/tf/lifecycle/out/managed-note.txt 로 만들되 내용을 ignored-value 로 두고 적용하세요. 그다음 lifecycleignore_changes = [content] 를 넣고 코드의 내용 값만 다른 문자열로 바꾼 뒤 tofu plan 출력을 /root/tf/lifecycle/out/ignore-plan.txt 로 저장하세요. 계획은 No changes 여야 하고 실제 파일 내용은 여전히 ignored-value 여야 합니다.

먼저 적용해 값을 상태에 넣은 다음 코드 값을 바꿔야 효과가 보입니다. 실물 파일의 내용은 최초 적용값 그대로여야 합니다.

다른 자원 변경으로 재생성 유발하기

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 의 동작이 두 개(삭제와 생성)여야 합니다. 확인 후 적용하세요.

자기 인자는 그대로인데 남의 변경 때문에 교체되는 것이 핵심입니다. 계획을 파일로 저장한 뒤 JSON 으로 변환하세요.

코드 밖 변경을 드리프트로 감지하기

코드를 거치지 않고 /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 가 들어 있어야 합니다. 이 계획은 적용하지 마세요.

코드를 거치지 않고 실물을 직접 고쳐야 드리프트입니다. 새로고침 전용 계획과 상세 종료 코드를 함께 쓰세요.

코드 기준으로 실물 되돌리기

tofu apply 로 실물을 코드 기준으로 되돌리고 출력을 /root/tf/lifecycle/out/remediate.txt 로 저장하세요. Apply complete 문구가 남아야 하고, endpoint.txt 에서 hand-edited 가 사라지고 파일 내용이 상태에 기록된 content 값과 정확히 같아야 합니다.

대응 방향 중 첫 번째입니다. 되돌린 뒤에는 상태에 기록된 값과 실물 내용이 같아야 합니다.

드리프트를 코드에 흡수하기

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.txtmain.tf 모두 512 를 담고 있어야 합니다.

이번에는 반대 방향입니다. 손으로 바꾼 값이 옳다고 판단했다면 코드를 그 값으로 고칩니다.

드리프트 감지 스크립트와 보고서 만들기

/root/tf/lifecycle/drift-report.sh 를 만들고 실행 권한을 주세요. 이 스크립트는 -detailed-exitcode 를 쓰는 계획 명령으로 드리프트를 판정하고, 결과를 /root/tf/lifecycle/out/drift-report.jsonexit_code, drift, checked_at, addresses 네 필드로 남겨야 합니다. 실습을 마무리한 시점에는 드리프트가 없어야 하므로 exit_code0, driftfalse 여야 합니다.

판정은 사람 눈이 아니라 종료 코드로 합니다. set -e 가 종료 코드 2 에서 스크립트를 죽이지 않도록 주의하세요.