Controlling With Lifecycle and Reverting Drift
한국어 원문으로 표시합니다.
목표
lifecycle 네 가지 인자로 교체·삭제·무시를 통제하고, 코드 밖에서 생긴 변경을 감지해 두 가지 방향으로 대응한 뒤 그 판정을 스크립트로 자동화합니다.
왜 중요한가
인프라 코드가 어긋나는 방식은 늘 같습니다 — 급한 일이 생겨 누군가 콘솔에서 직접 고치고, 그 변경이 코드로 돌아오지 않는 것입니다. 그 상태로 며칠이 지나면 전혀 다른 이유로 실행된 apply 가 새벽의 응급 조치를 조용히 되돌립니다. 그래서 드리프트 대응에서 진짜 어려운 부분은 명령어가 아니라 방향을 정하는 판단입니다. 응급 조치가 잘못이었다면 코드 기준으로 되돌리고, 옳은 판단이었다면 코드를 실물에 맞춰 고쳐야 합니다. 이 판단 없이 자동 복구를 돌리는 것이 가장 위험합니다. lifecycle 블록은 이 흐름에 미리 개입하는 장치입니다. 특히 ignore_changes 는 오토스케일러처럼 의도적으로 코드 밖에서 변하는 값을 감지 대상에서 빼서 거짓 경보를 줄이고, prevent_destroy 는 지워지면 안 되는 자원에 브레이크를 겁니다. 마지막으로 판정은 사람 눈이 아니라 -detailed-exitcode 로 해야 크론과 CI 에 얹을 수 있습니다.
단계
/root/tf/lifecycle/main.tf에local_file리소스endpoint를 선언하세요. 파일 경로는/root/tf/lifecycle/out/endpoint.txt, 내용은 줄바꿈 없는 한 줄 문자열입니다. 이 리소스의lifecycle블록에create_before_destroy = true를 넣고tofu init후 적용하세요.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는 그대로 살아 있어야 합니다.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여야 합니다.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의 동작이 두 개(삭제와 생성)여야 합니다. 확인 후 적용하세요.- 코드를 거치지 않고
/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.txt와main.tf모두512를 담고 있어야 합니다./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번에서 코드를 고쳐 놓고 드리프트라고 부르는 것. 드리프트는 코드를 거치지 않은 변경만을 말합니다.
create_before_destroy 걸기
/root/tf/lifecycle/main.tf 에 local_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)를 추가하고 lifecycle 에 prevent_destroy = true 를 넣어 적용하세요. 그다음 그 리소스만 대상으로 삭제를 시도하고(tofu destroy -target=local_file.statefile_guard) 출력을 표준 오류까지 포함해 /root/tf/lifecycle/out/prevent.txt 로 저장하세요. 거부 메시지가 남아야 하고, 상태의 statefile_guard 와 guard.txt 는 그대로 살아 있어야 합니다.
이 인자는 계획 단계에서 오류를 냅니다. 대상 하나만 지정해 삭제를 시도하고 그 출력을 통째로 저장하세요.
ignore_changes 로 특정 속성 무시하기
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 여야 합니다.
먼저 적용해 값을 상태에 넣은 다음 코드 값을 바꿔야 효과가 보입니다. 실물 파일의 내용은 최초 적용값 그대로여야 합니다.
다른 자원 변경으로 재생성 유발하기
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.txt 와 main.tf 모두 512 를 담고 있어야 합니다.
이번에는 반대 방향입니다. 손으로 바꾼 값이 옳다고 판단했다면 코드를 그 값으로 고칩니다.
드리프트 감지 스크립트와 보고서 만들기
/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 여야 합니다.
판정은 사람 눈이 아니라 종료 코드로 합니다. set -e 가 종료 코드 2 에서 스크립트를 죽이지 않도록 주의하세요.