Terraform 실전 · 상태 조작과 import · 실습
상태 옮기고 지우고 가져오기
목표
이미 적용된 자원의 주소를 안전하게 옮기고, 관리 밖에 있던 자원을 상태에 편입시키고, 그 결과 코드와 상태가 어긋나지 않음을 종료 코드로 증명합니다.
왜 중요한가
상태 파일은 도구의 기억입니다. 코드는 "무엇이 있어야 하는가"를, 실물은 "무엇이 있는가"를 말하고, 상태는 "내가 마지막으로 알던 것"을 말합니다. 리팩터링은 이 셋 중 코드만 바꾸는 일이라, 상태를 함께 옮기지 않으면 도구는 옛 자원이 사라지고 새 자원이 생겼다고 판단해 파괴와 생성을 계획합니다. 그래서 이름 변경 같은 사소한 작업이 운영에서는 위험한 작업이 됩니다. 반대로 state rm 은 이름과 달리 실물을 지우지 않습니다 — 장부에서만 지우는 관리 포기 선언이고, 코드에 선언이 남아 있으면 다음 계획이 그 자원을 다시 만들려 듭니다. 이 비대칭을 정확히 알고 있어야 상태 수술이 무섭지 않습니다. 그리고 모든 수술 전에는 사본을 뜨세요. 되돌릴 수 있는 작업과 되돌릴 수 없는 작업의 차이는 그 파일 하나입니다.
단계
1. /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개 이상이어야 합니다.
2. 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 속성 줄이 보여야 합니다.
3. 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 여야 합니다.
4. /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 여야 합니다.
5. 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 의 차이를 한 줄로 적으세요.
6. main.tf 에 resource "terraform_data" "adopted" 를 빈 껍데기로 선언하고, 그 위에 import 블록을 두어 to = terraform_data.adopted, id = "labhub-adopted" 를 지정한 뒤 적용하세요. 상태의 terraform_data.adopted 의 id 속성이 labhub-adopted 여야 합니다.
7. 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 여야 합니다.
8. 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 를 넣을 자리를 코드에서 찾습니다.
단계 8개
- 수술 전 상태 사본 확보하기
- 상태 목록과 개별 리소스 들여다보기
- state mv 로 이름만 바꾸기
- 리소스를 모듈 안으로 옮기기
- state rm 이 실물을 지우지 않음을 증명하기
- import 블록으로 자원 편입하기
- 명령줄 import 로 편입하기
- 수술 후 일치 여부를 종료 코드로 증명하기