把状态挪走、删掉、再导进来
한국어 원문으로 표시합니다.
목표
이미 적용된 자원의 주소를 안전하게 옮기고, 관리 밖에 있던 자원을 상태에 편입시키고, 그 결과 코드와 상태가 어긋나지 않음을 종료 코드로 증명합니다.
왜 중요한가
상태 파일은 도구의 기억입니다. 코드는 "무엇이 있어야 하는가"를, 실물은 "무엇이 있는가"를 말하고, 상태는 "내가 마지막으로 알던 것"을 말합니다. 리팩터링은 이 셋 중 코드만 바꾸는 일이라, 상태를 함께 옮기지 않으면 도구는 옛 자원이 사라지고 새 자원이 생겼다고 판단해 파괴와 생성을 계획합니다. 그래서 이름 변경 같은 사소한 작업이 운영에서는 위험한 작업이 됩니다. 반대로 state rm 은 이름과 달리 실물을 지우지 않습니다 — 장부에서만 지우는 관리 포기 선언이고, 코드에 선언이 남아 있으면 다음 계획이 그 자원을 다시 만들려 듭니다. 이 비대칭을 정확히 알고 있어야 상태 수술이 무섭지 않습니다. 그리고 모든 수술 전에는 사본을 뜨세요. 되돌릴 수 있는 작업과 되돌릴 수 없는 작업의 차이는 그 파일 하나입니다.
단계
/root/tf/surgery/main.tf에 세 리소스를 선언하고tofu init후 적용하세요 —local_file.legacy(파일/root/tf/surgery/files/legacy.txt),local_file.notes(파일/root/tf/surgery/files/notes.txt),random_pet.orphan. 그다음 상태 파일을/root/tf/surgery/backup/terraform.tfstate.bak로 복사하세요. 사본은 올바른 JSON 이어야 하고lineage가 현재 상태와 같아야 하며 리소스가 3개 이상이어야 합니다.tofu state list결과를/root/tf/surgery/out/state-list.txt로(3줄 이상),tofu state show local_file.legacy결과를/root/tf/surgery/out/show-legacy.txt로 저장하세요. 두 번째 파일에는id속성 줄이 보여야 합니다.tofu state mv local_file.legacy local_file.renamed로 상태의 주소를 바꾸고, 같은 작업 안에서 코드의 리소스 이름도renamed로 고치세요. 옛local_file.legacy블록은 주석 처리가 아니라 완전히 지워야 하며, 작업 디렉터리의 어떤.tf파일에도 남아 있으면 안 됩니다. 상태만 옮기고 코드를 그대로 두면 다음 계획이 옛 이름의 자원을 다시 만들려 듭니다.tofu plan출력을/root/tf/surgery/out/plan-after-rename.txt로 저장하면No changes여야 합니다./root/tf/surgery/modules/archive/디렉터리에 모듈을 만들어 같은local_file리소스를renamed라는 이름으로 선언하세요(파일 경로·내용 등 인자 값이 옮기기 전과 완전히 동일해야 합니다). 루트에module "archive"블록을 추가하고tofu init을 돌린 뒤tofu state mv local_file.renamed module.archive.local_file.renamed를 실행하고, 루트main.tf에서는 그 리소스 블록을 지우세요(여기서도 주석 처리가 아니라 삭제입니다 — 리소스 선언은 이제 모듈 파일에만 있어야 합니다).tofu plan출력을/root/tf/surgery/out/plan-after-module.txt로 저장하면No changes여야 합니다.tofu state rm random_pet.orphan을 실행한 뒤tofu plan출력을/root/tf/surgery/out/plan-after-rm.txt로 저장하세요. 계획에는random_pet.orphan을 다시 만들겠다는 내용(1 to add)이 있어야 합니다. 확인했으면main.tf에서도 그 리소스 블록을 지우고,/root/tf/surgery/out/rm-note.txt에state rm과destroy의 차이를 한 줄로 적으세요.main.tf에resource "terraform_data" "adopted"를 빈 껍데기로 선언하고, 그 위에import블록을 두어to = terraform_data.adopted,id = "labhub-adopted"를 지정한 뒤 적용하세요. 상태의terraform_data.adopted의id속성이labhub-adopted여야 합니다.main.tf에resource "terraform_data" "legacy_job"을 선언하고, 이번에는 명령줄로tofu import terraform_data.legacy_job labhub-legacy를 실행하세요. 출력을/root/tf/surgery/out/import.txt로 저장하면Import successful문구가 보여야 하고, 상태의terraform_data.legacy_job의id는labhub-legacy여야 합니다.tofu plan -detailed-exitcode를 실행하고 종료 코드만/root/tf/surgery/out/final-exit.txt에 적으세요(0이어야 합니다). 그리고/root/tf/surgery/out/inventory.json을 만들어addresses에 현재 상태의 모든 주소를 배열로 담고backup_taken에true를 넣으세요. 주소 개수는 상태의 인스턴스 수와 정확히 같아야 하며, 목록에는module.archive.로 시작하는 주소와 가져온adopted주소가 포함돼야 합니다.
참고
- 이 실습의 상태 파일은
/root/tf/surgery/terraform.tfstate입니다. 수술 전 사본은 반드시 따로 남기세요. tofu state mv는 옛 주소와 새 주소를 인자로 받습니다. 모듈로 옮길 때는 새 주소 앞에module.<이름>.을 붙입니다.- 모듈을 새로 추가하면
state mv전에 반드시tofu init을 돌리세요. 설치되지 않은 모듈 주소로는 옮길 수 없습니다. -detailed-exitcode는 변경 없음이면 0, 오류면 1, 변경 있음이면 2 를 냅니다. 종료 코드를 잡으려면 명령 바로 다음에$?를 읽어야 합니다.state mv와 코드 수정은 한 쌍입니다. 둘 중 하나만 하면 다음 계획이 곧바로 어긋납니다 — 상태만 옮기면 옛 이름으로 자원을 다시 만들려 들고, 코드만 고치면 파괴와 생성이 나란히 뜹니다. 그래서 실무에서는 둘을 한 번에 표현하는moved블록을 선호합니다.- 흔한 실수 1:
state rm을 삭제 명령으로 오해하는 것. 장부에서만 지우므로 코드 블록도 함께 정리해야 계획이 조용해집니다. - 흔한 실수 2: 가져올 리소스 블록 없이 import 를 시도하는 것. 도구는 그 ID 를 넣을 자리를 코드에서 찾습니다.
수술 전 상태 사본 확보하기
/root/tf/surgery/main.tf 에 세 리소스를 선언하고 tofu init 후 적용하세요 — local_file.legacy(파일 /root/tf/surgery/files/legacy.txt), local_file.notes(파일 /root/tf/surgery/files/notes.txt), random_pet.orphan. 그다음 상태 파일을 /root/tf/surgery/backup/terraform.tfstate.bak 로 복사하세요. 사본은 올바른 JSON 이어야 하고 lineage 가 현재 상태와 같아야 하며 리소스가 3개 이상이어야 합니다.
상태를 만지는 모든 작업 전에 파일을 복사합니다. 사본이 같은 상태의 것인지 확인하려면 계보 값을 비교하세요.
상태 목록과 개별 리소스 들여다보기
tofu state list 결과를 /root/tf/surgery/out/state-list.txt 로(3줄 이상), tofu state show local_file.legacy 결과를 /root/tf/surgery/out/show-legacy.txt 로 저장하세요. 두 번째 파일에는 id 속성 줄이 보여야 합니다.
목록 명령과 특정 주소의 속성을 통째로 보여 주는 명령은 다릅니다. 후자에는 id 속성이 보여야 합니다.
state mv 로 이름만 바꾸기
tofu state mv local_file.legacy local_file.renamed 로 상태의 주소를 바꾸고, 같은 작업 안에서 코드의 리소스 이름도 renamed 로 고치세요. 옛 local_file.legacy 블록은 주석 처리가 아니라 완전히 지워야 하며, 작업 디렉터리의 어떤 .tf 파일에도 남아 있으면 안 됩니다. 상태만 옮기고 코드를 그대로 두면 다음 계획이 옛 이름의 자원을 다시 만들려 듭니다. tofu plan 출력을 /root/tf/surgery/out/plan-after-rename.txt 로 저장하면 No changes 여야 합니다.
코드와 상태가 같은 이름을 가리켜야 계획이 빕니다. 상태만 옮기면 다음 계획이 옛 이름으로 다시 만들려 들고, 코드만 고치면 파괴와 생성이 나란히 뜹니다. 옛 이름의 블록은 주석이 아니라 삭제입니다.
리소스를 모듈 안으로 옮기기
/root/tf/surgery/modules/archive/ 디렉터리에 모듈을 만들어 같은 local_file 리소스를 renamed 라는 이름으로 선언하세요(파일 경로·내용 등 인자 값이 옮기기 전과 완전히 동일해야 합니다). 루트에 module "archive" 블록을 추가하고 tofu init 을 돌린 뒤 tofu state mv local_file.renamed module.archive.local_file.renamed 를 실행하고, 루트 main.tf 에서는 그 리소스 블록을 지우세요(여기서도 주석 처리가 아니라 삭제입니다 — 리소스 선언은 이제 모듈 파일에만 있어야 합니다). tofu plan 출력을 /root/tf/surgery/out/plan-after-module.txt 로 저장하면 No changes 여야 합니다.
모듈 블록을 먼저 만들고 init 을 돌린 뒤 옮깁니다. 모듈 안 리소스의 인자 값이 옮기기 전과 완전히 같아야 계획이 빕니다.
state rm 이 실물을 지우지 않음을 증명하기
tofu state rm random_pet.orphan 을 실행한 뒤 tofu plan 출력을 /root/tf/surgery/out/plan-after-rm.txt 로 저장하세요. 계획에는 random_pet.orphan 을 다시 만들겠다는 내용(1 to add)이 있어야 합니다. 확인했으면 main.tf 에서도 그 리소스 블록을 지우고, /root/tf/surgery/out/rm-note.txt 에 state rm 과 destroy 의 차이를 한 줄로 적으세요.
장부에서만 지우는 명령입니다. 코드에 선언이 남아 있으면 다음 계획이 무엇을 하려 드는지 보세요.
import 블록으로 자원 편입하기
main.tf 에 resource "terraform_data" "adopted" 를 빈 껍데기로 선언하고, 그 위에 import 블록을 두어 to = terraform_data.adopted, id = "labhub-adopted" 를 지정한 뒤 적용하세요. 상태의 terraform_data.adopted 의 id 속성이 labhub-adopted 여야 합니다.
가져올 자리(리소스 블록)를 먼저 만들어야 합니다. 블록에는 어디로(to) 무엇을(id) 가져올지 적습니다.
명령줄 import 로 편입하기
main.tf 에 resource "terraform_data" "legacy_job" 을 선언하고, 이번에는 명령줄로 tofu import terraform_data.legacy_job labhub-legacy 를 실행하세요. 출력을 /root/tf/surgery/out/import.txt 로 저장하면 Import successful 문구가 보여야 하고, 상태의 terraform_data.legacy_job 의 id 는 labhub-legacy 여야 합니다.
같은 일을 즉시 실행하는 방식입니다. 출력에 성공 문구가 남으니 파일로 저장하세요.
수술 후 일치 여부를 종료 코드로 증명하기
tofu plan -detailed-exitcode 를 실행하고 종료 코드만 /root/tf/surgery/out/final-exit.txt 에 적으세요(0 이어야 합니다). 그리고 /root/tf/surgery/out/inventory.json 을 만들어 addresses 에 현재 상태의 모든 주소를 배열로 담고 backup_taken 에 true 를 넣으세요. 주소 개수는 상태의 인스턴스 수와 정확히 같아야 하며, 목록에는 module.archive. 로 시작하는 주소와 가져온 adopted 주소가 포함돼야 합니다.
사람이 눈으로 읽는 판정은 자동화가 아닙니다. 계획의 종료 코드로 판정하고, 상태의 주소를 한 곳에 모아 개수를 맞춰 보세요.