Terraform 실전 · 상태 조작과 import · 이론
상태 수술 — 실물을 건드리지 않고 기억만 고치기
한 줄 요약
state mv 와 state rm 은 인프라를 바꾸지 않는다. 도구의 기억만 바꾼다. 이 한 문장을 오해하면 사고가 나고, 이해하면 리팩터링이 안전해진다.
왜 이게 필요했나
인프라 코드는 반드시 리팩터링을 겪는다. 리소스 이름이 마음에 들지 않아서, 리소스 몇 개를 모듈로 묶고 싶어서, 상태 파일이 너무 커져서 나누고 싶어서. 그런데 코드에서 이름을 바꾸는 순간 도구는 옛 이름의 자원이 사라졌고 새 이름의 자원이 생겼다고 판단한다. 계획에는 파괴와 생성이 나란히 뜬다. 실제 운영 DB 였다면 그 계획을 승인하는 순간 끝이다.
반대 방향의 문제도 있다. 장애 대응 중에 누군가 콘솔에서 보안 그룹을 만들었다. 실물은 존재하는데 코드와 상태에는 없다. 다음 apply 는 그 자원을 모르므로 건드리지 않지만, 아무도 관리하지 않는 자원이 하나 늘어난 것이다. 이 자원을 코드 관리 아래로 데려오는 것이 import 다.
이 두 방향 — 주소를 옮기는 일과 바깥의 것을 데려오는 일 — 이 상태 수술의 전부다.
어떻게 동작하나
상태 파일은 "내가 만든 자원의 주소와 마지막으로 알던 속성"을 적은 장부다. 수술 도구는 네 가지다.
| 명령 | 하는 일 | 실물에 대한 영향 |
| --- | --- | --- |
| state list | 주소 목록 출력 | 없음 |
| state show <주소> | 그 주소의 속성 전체 출력 | 없음 |
| state mv A B | 장부에서 A 항목을 B 이름으로 옮김 | 없음 |
| state rm A | 장부에서 A 항목을 지움 | 없음 (실물은 그대로 남는다) |
| import | 실물을 장부에 등록 | 없음 |
가장 자주 오해되는 것이 state rm 이다. 이것은 삭제 명령이 아니라 관리 포기 선언이다. 실물은 그대로 남고 도구만 잊는다. 그래서 state rm 뒤에 코드에서도 해당 블록을 지우지 않으면, 다음 계획은 "그 자원이 없네, 새로 만들자"고 말한다. 실제 클라우드에서는 이름이 겹치는 자원을 또 만들려다 실패하거나, 최악의 경우 중복 자원이 생긴다. 실물까지 지우고 싶다면 destroy 를 써야 한다.
state mv 와 같은 일을 코드로 하는 것이 moved 블록이다. 차이는 기록에 있다. state mv 는 누군가의 터미널에서 한 번 실행되고 사라지지만, moved 는 코드에 남아 팀원이 plan 만 돌려도 같은 이주가 자동으로 일어난다. 그래서 혼자 쓰는 상태에는 state mv, 여럿이 쓰는 코드에는 moved 가 기본이다.
import 도 두 가지 형태가 있다. 명령줄 tofu import <주소> <ID> 는 즉시 실행되고 상태를 바꾼다. import {} 블록은 코드에 선언해 두고 계획으로 먼저 확인한 뒤 apply 로 반영한다. 후자는 리뷰가 가능하고 이력이 남기 때문에 협업에서는 블록 방식이 낫다. 둘 다 가져올 자리(리소스 블록)를 미리 만들어 둬야 한다는 점은 같다. 빈 껍데기가 없으면 도구는 그 ID 를 어디에 넣어야 할지 모른다.
현장에서 만나는 모습
첫째, 수술 전 사본은 협상 대상이 아니다. 상태를 만지는 모든 명령 전에 파일을 복사해 둔다. 원격 백엔드라면 state pull 로 받아 둔다. 잘못 옮긴 주소는 되돌리기 어렵지만, 사본이 있으면 그냥 되돌리면 된다. 사본이 진짜인지 확인하는 방법은 lineage 값 비교다 — 같은 상태의 계보를 가진 파일이어야 한다.
둘째, 상태 손상은 대개 중단된 apply 에서 온다. 네트워크가 끊긴 채 apply 가 죽으면 파일이 깨진다. 원격 백엔드의 버전 관리로 이전 버전을 복원하고 apply -refresh-only 로 실물과 다시 맞추는 것이 표준 복구 절차다. 로컬 파일 하나로 운영하는 구성이 위험한 진짜 이유가 이것이다.
셋째, import 후 첫 계획은 반드시 읽는다. 가져온 자원의 실제 속성이 코드와 다르면 계획은 그 차이를 없애려 든다. 콘솔에서 만든 자원의 세부 설정을 코드에 옮겨 적기 전에 apply 하면, 데려오자마자 설정이 날아간다. import 직후에는 prevent_destroy 를 임시로 걸어 두는 것도 흔한 방어책이다.
넷째, 상태를 옮기는 작업은 잠금과 함께 생각한다. 여럿이 쓰는 원격 상태에서 수술 중에 남이 apply 를 돌리면 결과를 예측할 수 없다. 작업 시간을 공지하고, 가능하면 파이프라인을 잠깐 멈춘다.
다음 실습에서 할 것
먼저 사본을 뜨고 상태를 들여다본 다음, state mv 로 리소스 이름을 바꾸고 계획이 비는 것을 확인한다. 같은 명령으로 리소스를 모듈 안으로 옮기고, state rm 이 실물을 지우지 않는다는 사실을 계획으로 증명한다. 그다음 import {} 블록과 tofu import 명령 두 가지 방법으로 바깥의 자원을 상태에 편입시키고, 마지막에 -detailed-exitcode 로 코드와 상태가 완전히 일치함을 종료 코드로 증명한다.