Terraform/OpenTofu 기초 · 리소스와 의존성 · 이론
의존성 그래프와 상태 파일의 속살
한 줄 요약
Terraform 은 파일에 적힌 순서가 아니라 참조가 만든 그래프의 순서로 움직인다. 그 그래프는 상태 파일의 dependencies 에 화석처럼 남아, 코드에서 리소스가 사라진 뒤에도 삭제 순서를 알려 준다.
왜 이게 필요했나
셸 스크립트에서는 순서를 사람이 정한다. 리소스가 다섯 개일 때는 위에서 아래로 읽으면 되지만, 서른 개가 되면 새 리소스를 끼워 넣을 때마다 "이건 어디 뒤에 와야 하지"를 매번 사람이 다시 판단해야 한다. 게다가 서로 무관한 리소스도 한 줄로 세워 만들게 되므로 만드는 데 걸리는 시간이 그대로 합산된다.
선언형 도구는 이 문제를 뒤집는다. 사람은 관계만 적고 순서는 적지 않는다. A 를 만들 때 B 의 값을 쓰면 그것 자체가 "B 가 먼저"라는 선언이다. 도구는 이 관계를 모아 방향 그래프를 만들고 위상 정렬해서, 서로 무관한 것들은 동시에 처리한다. 삭제할 때는 같은 그래프를 거꾸로 탄다.
어떻게 동작하나
의존성을 만드는 방법은 두 가지다.
| 방식 | 어떻게 생기나 | 언제 쓰나 |
| --- | --- | --- |
| 암묵적 의존성 | 다른 리소스의 속성을 식으로 참조하면 자동으로 | 기본. 값이 실제로 필요할 때 |
| 명시적 의존성 | depends_on = [주소] 를 직접 적어서 | 참조는 없는데 순서는 있어야 할 때 |
대부분은 암묵적으로 충분하다. depends_on 이 필요한 경우는 값으로는 드러나지 않는 부수 효과가 있을 때다. 예를 들어 IAM 정책이 붙기 전에는 리소스 생성이 실패하는데 그 정책의 값을 코드에서 쓰지는 않는 경우다.
depends_on 을 습관처럼 붙이면 그래프가 굵어진다. 그러면 두 가지가 손해다. 첫째, 동시에 처리할 수 있었던 것들이 줄을 서서 실행 시간이 늘어난다. 둘째, 앞 리소스가 재생성될 때 뒤 리소스까지 함께 재생성되는 범위가 넓어진다.
상태 파일 쪽도 함께 보자. terraform state list 는 등록된 주소만 한 줄씩 뱉는 가장 빠른 확인 수단이다. 기계로 다뤄야 할 때는 terraform show -json 을 쓴다. 이 출력의 리소스 목록은 values.root_module.resources 아래에 있고 각 항목에 address 가 붙어 있어 jq 로 다루기 좋다. 상태 파일 자체를 직접 읽으면 serial, lineage, version, resources 를 볼 수 있다.
.terraform.lock.hcl 도 이 시점에 읽어 둘 가치가 있다. 안에는 provider "registry.opentofu.org/hashicorp/local" 같은 블록과 확정된 version, 그리고 h1: 또는 zh: 로 시작하는 해시가 들어 있다. 버전은 "무엇을 쓰기로 했는지", 해시는 "실제로 받은 물건이 그것이 맞는지"를 뜻한다.
현장에서 만나는 모습
첫째, depends_on 도미노. 순서 문제가 한 번 나면 사람들은 여기저기 depends_on 을 붙이기 시작한다. 몇 달 뒤 데이터베이스 파라미터 하나를 고쳤을 뿐인데 계획에 "재생성 12건"이 뜬다. 그래프를 굵게 만든 대가는 항상 나중에 청구된다.
둘째, 삭제 순서는 코드가 아니라 상태가 안다. 코드에서 리소스를 지우면 그 리소스의 관계는 코드에서 사라진다. 그런데도 도구가 올바른 역순으로 지울 수 있는 것은 상태의 dependencies 에 관계가 남아 있기 때문이다. 상태를 손으로 편집하는 일이 위험한 이유 중 하나다.
셋째, 드리프트는 특별한 기능이 아니다. 관리 중인 리소스를 코드 밖에서 고치면, 다음 plan 이 조회 단계에서 그 차이를 발견하고 되돌리려 든다. 장애 대응 중 콘솔에서 급히 고친 설정이 다음 배포에서 조용히 사라지는 사고가 여기서 나온다. 급한 변경은 반드시 코드로 되돌려 놔야 한다.
다음 실습에서 할 것
/root/tf/state 에 리소스를 쌓으면서 참조만으로 의존성이 생기는 것을 상태에서 확인하고, 참조가 없는 자리에는 depends_on 을 붙여 본다. 상태 주소 목록과 JSON 표현을 뽑고, serial·lineage·리소스 개수를 담은 메타데이터 파일을 만든다. 그다음 관리 중인 파일을 손으로 지워 드리프트가 계획에 잡히는 것을 보고, 잠금 파일에서 프로바이더 버전을 읽는다. 마지막에는 3단계 의존 사슬을 만들어 순서를 기록한다.