Terraform/OpenTofu 기초 · init 과 의존성 잠금 파일 · 실습
잠금 파일 한 줄을 고쳤더니 init 이 멈췄다
목표
의존성 잠금 파일을 직접 만들고, 제약을 붙여 좁히고, 지웠다 다시 만들고, 해시를 깨뜨렸다 되살립니다. init 이 매번 무엇을 확인하는지, 왜 이 파일만 커밋하고 설치 디렉터리는 커밋하지 않는지를 손으로 확인합니다.
왜 중요한가
잠금 파일은 읽는 파일이 아니라 팀의 계약서입니다. 어떤 버전을 골랐는지(version), 무엇을 허용했는지(constraints), 그 꾸러미가 그때 그것과 같은지(hashes) 세 가지가 여기 적히고, init 은 매번 이 셋을 확인합니다. 계약서가 없으면 어떤 명령도 계획을 세우지 못하고, 체크섬이 어긋나면 설치가 그 자리에서 막힙니다. 이 파일을 커밋하지 않는 팀은 사람마다 다른 버전을 받고, 그래서 '내 노트북에서는 되는데' 가 프로바이더 단위로 생깁니다. 반대로 설치 디렉터리는 커밋하지 않습니다 — 플랫폼마다 다른 바이너리라 저장소가 부풀고, init 이 언제든 다시 만들어 주기 때문입니다.
단계
1. /root/tfb-lock/app/main.tf 에 local·random 두 프로바이더를 버전 제약 없이 선언하고, random_pet.tag(length 2)와 그 id 를 note.txt 에 쓰는 local_file.note 를 두세요. init 한 뒤 생긴 .terraform.lock.hcl 을 읽어 /root/tfb-lock/lock-read.txt 에 local_version=<값>, random_version=<값>, local_hashes=<local 블록의 h1 해시 줄 수> 세 줄을 적으세요.
2. /root/tfb-lock/pinned/main.tf 를 새로 만들되 이번에는 처음부터 local 에 version = "2.9.0" 을 적고 random 은 제약 없이 두세요. local_file.fixed 로 fixed.txt 하나를 선언하고 init 하세요. 그다음 잠금 파일에서 프로바이더별 constraints 값을 읽어 /root/tfb-lock/constraints.txt 에 local=<값> 과 random=none 두 줄을 적으세요.
3. /root/tfb-lock/app/.terraform/providers/ 아래에서 실제로 설치된 프로바이더 꾸러미의 위치를 찾아 /root/tfb-lock/installed.txt 에 local=<버전>/<플랫폼> 과 random=<버전>/<플랫폼> 두 줄을 적으세요.
4. /root/tfb-lock/fresh/ 를 만들고 /root/tfb-lock/app/main.tf 만 복사하세요(잠금 파일과 .terraform/ 은 가져오지 않습니다). init 없이 tofu plan 을 돌려 오류 출력을 /root/tfb-lock/no-init.txt 에 저장하세요. fresh 에서는 init 을 하지 마세요.
5. /root/tfb-lock/badpin/main.tf 에 local 만 쓰되 version = "2.5.0" 으로 못 박고 local_file.pinned 하나를 두세요. init 을 돌려 실패 출력을 /root/tfb-lock/pin-fail.txt 에 저장한 뒤, 제약을 2.9.0 으로 고쳐 init 이 성공하게 만드세요.
6. /root/tfb-lock/app/main.tf 의 local 에 이제서야 version = "2.9.0" 을 붙이고 다시 init 하세요. 잠금 파일의 local 블록에 constraints 줄이 생겼는지 확인해 /root/tfb-lock/stale-lock.txt 의 첫 줄에 after_init=<그 값, 없으면 none> 을 적으세요. 그다음 .terraform.lock.hcl 을 지우고 init 대신 tofu providers lock -fs-mirror=/opt/tofu-mirror -platform=linux_amd64 로 다시 만든 뒤, 그 출력을 /root/tfb-lock/relock.txt 에 저장하고 stale-lock.txt 둘째 줄에 after_relock=<지금 값> 을 적으세요.
7. /root/tfb-lock/app/.terraform.lock.hcl 의 local 해시 한 줄을 아무 값으로 바꾸고 .terraform/ 을 지운 뒤 init 하세요. 체크섬 오류 출력을 /root/tfb-lock/tamper.txt 에 저장합니다. 그다음 잠금 파일을 지우고 다시 init 해서 제대로 된 해시로 되돌리세요.
8. /root/tfb-lock/audit.sh 를 만드세요. 첫 인자로 받은 디렉터리의 .terraform.lock.hcl 을 읽어 constraints 줄이 없는 provider 블록의 이름을 한 줄씩 찍고, 하나라도 있으면 종료 코드 1, 없으면 0 으로 끝나야 합니다. /root/tfb-lock/app 에 돌린 결과를 /root/tfb-lock/audit-app.txt 에, /root/tfb-lock/badpin 에 돌린 결과를 /root/tfb-lock/audit-pin.txt 에 저장하고 각 파일의 마지막 줄에 exit=<종료 코드> 를 덧붙이세요.
참고
- 파드의 프로바이더는 /opt/tofu-mirror 파일 시스템 미러에서만 옵니다. 미러에는 프로바이더마다 버전이 하나씩이고 플랫폼은 linux_amd64 뿐입니다.
- 그래서 다른 플랫폼의 해시를 레지스트리에서 받아 오는 방식은 이 파드에서 쓸 수 없습니다. 대신 미러를 가리키는
-fs-mirror를 씁니다. - 흔한 실수: 4단계의 fresh 디렉터리에서 오류를 없애려고 init 을 돌려 버리는 것. 이 단계는 init 하지 않은 상태의 오류를 남기는 것이 과제입니다.
- 흔한 실수: 7단계에서 잘못된 해시를 그대로 두고 -upgrade 만 붙이는 것. 버전 선택이 그대로라 같은 체크섬 검사에 다시 걸립니다.
- 6단계의 핵심은 init 이 '고칠 이유가 없으면 잠금 파일을 쓰지 않는다' 는 것입니다. -upgrade 로도 constraints 줄은 생기지 않습니다.
- [Dependency Lock File](https://opentofu.org/docs/language/files/dependency-lock/) · [tofu init](https://opentofu.org/docs/cli/commands/init/) · [tofu providers lock](https://opentofu.org/docs/cli/commands/providers/lock/) · [tofu providers](https://opentofu.org/docs/cli/commands/providers/) · [CLI 설정 파일](https://opentofu.org/docs/cli/config/config-file/)
단계 8개
- 첫 init 이 만든 계약서를 읽는다
- 제약을 적고 시작하면 계약서에 허용 범위가 함께 남는다
- 잠금 파일이 가리키는 실물이 어디 있는지 찾는다
- 계약서가 없으면 계획조차 못 세운다
- 미러에 없는 버전을 못 박으면 init 이 거절한다
- 뒤늦게 붙인 제약은 계약서에 저절로 반영되지 않는다
- 해시를 손대면 설치가 막힌다
- 제약 없는 프로바이더를 찾아내는 점검을 만든다