Terraform/OpenTofu Fundamentals
One Edited Line in the Lock File Stopped init
한국어 원문으로 표시합니다.
목표
의존성 잠금 파일을 직접 만들고, 제약을 붙여 좁히고, 지웠다 다시 만들고, 해시를 깨뜨렸다 되살립니다. init 이 매번 무엇을 확인하는지, 왜 이 파일만 커밋하고 설치 디렉터리는 커밋하지 않는지를 손으로 확인합니다.
왜 중요한가
잠금 파일은 읽는 파일이 아니라 팀의 계약서입니다. 어떤 버전을 골랐는지(version), 무엇을 허용했는지(constraints), 그 꾸러미가 그때 그것과 같은지(hashes) 세 가지가 여기 적히고, init 은 매번 이 셋을 확인합니다. 계약서가 없으면 어떤 명령도 계획을 세우지 못하고, 체크섬이 어긋나면 설치가 그 자리에서 막힙니다. 이 파일을 커밋하지 않는 팀은 사람마다 다른 버전을 받고, 그래서 '내 노트북에서는 되는데' 가 프로바이더 단위로 생깁니다. 반대로 설치 디렉터리는 커밋하지 않습니다 — 플랫폼마다 다른 바이너리라 저장소가 부풀고, init 이 언제든 다시 만들어 주기 때문입니다.
단계
/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 해시 줄 수>세 줄을 적으세요./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두 줄을 적으세요./root/tfb-lock/app/.terraform/providers/아래에서 실제로 설치된 프로바이더 꾸러미의 위치를 찾아/root/tfb-lock/installed.txt에local=<버전>/<플랫폼>과random=<버전>/<플랫폼>두 줄을 적으세요./root/tfb-lock/fresh/를 만들고/root/tfb-lock/app/main.tf만 복사하세요(잠금 파일과.terraform/은 가져오지 않습니다). init 없이tofu plan을 돌려 오류 출력을/root/tfb-lock/no-init.txt에 저장하세요. fresh 에서는 init 을 하지 마세요./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 이 성공하게 만드세요./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=<지금 값>을 적으세요./root/tfb-lock/app/.terraform.lock.hcl의 local 해시 한 줄을 아무 값으로 바꾸고.terraform/을 지운 뒤 init 하세요. 체크섬 오류 출력을/root/tfb-lock/tamper.txt에 저장합니다. 그다음 잠금 파일을 지우고 다시 init 해서 제대로 된 해시로 되돌리세요./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 · tofu init · tofu providers lock · tofu providers · CLI 설정 파일
첫 init 이 만든 계약서를 읽는다
/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 해시 줄 수> 세 줄을 적으세요.
잠금 파일은 HCL 이고 프로바이더마다 provider 블록이 하나씩 생깁니다. 블록 안에 version, 필요하면 constraints, 그리고 hashes 목록이 들어갑니다. apply 는 아직 하지 않아도 됩니다.
제약을 적고 시작하면 계약서에 허용 범위가 함께 남는다
/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 두 줄을 적으세요.
constraints 줄은 설정에 버전 제약을 적었을 때만 잠금 파일에 나타납니다. 제약을 안 적은 프로바이더의 블록에는 아예 그 줄이 없습니다. 무엇을 골랐는가(version)와 무엇을 허용했는가(constraints)는 다른 정보입니다.
잠금 파일이 가리키는 실물이 어디 있는지 찾는다
/root/tfb-lock/app/.terraform/providers/ 아래에서 실제로 설치된 프로바이더 꾸러미의 위치를 찾아 /root/tfb-lock/installed.txt 에 local=<버전>/<플랫폼> 과 random=<버전>/<플랫폼> 두 줄을 적으세요.
설치 경로는 레지스트리 주소·네임스페이스·이름·버전·플랫폼 순으로 깊어집니다. 이 디렉터리는 init 이 다시 만들어 주므로 커밋하지 않지만, 잠금 파일은 사람이 리뷰해야 하므로 커밋합니다.
계약서가 없으면 계획조차 못 세운다
/root/tfb-lock/fresh/ 를 만들고 /root/tfb-lock/app/main.tf 만 복사하세요(잠금 파일과 .terraform/ 은 가져오지 않습니다). init 없이 tofu plan 을 돌려 오류 출력을 /root/tfb-lock/no-init.txt 에 저장하세요. fresh 에서는 init 을 하지 마세요.
새로 받은 저장소에서 사람들이 가장 먼저 만나는 오류입니다. 메시지가 무엇을 하라고 말하는지, 왜 계획 단계에서 막는지 생각해 보세요. 프로바이더 없이는 계획을 세울 수가 없습니다.
미러에 없는 버전을 못 박으면 init 이 거절한다
/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 이 성공하게 만드세요.
이 파드의 프로바이더는 오프라인 미러에서만 옵니다. 미러에 없는 버전을 요구하면 도구는 고를 것이 없다고 말합니다. 실무에서도 사내 미러만 쓰는 조직에서 똑같은 메시지를 만납니다.
뒤늦게 붙인 제약은 계약서에 저절로 반영되지 않는다
/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=<지금 값> 을 적으세요.
init 은 이미 고른 선택이 여전히 제약에 맞으면 다시 고르지 않고, 그래서 잠금 파일을 새로 쓰지도 않습니다. 허용 범위를 계약서에 반영하려면 그 항목을 새로 만들게 해야 합니다. providers lock 은 꾸러미를 설치하지 않고 체크섬만 계산해 적습니다.
해시를 손대면 설치가 막힌다
/root/tfb-lock/app/.terraform.lock.hcl 의 local 해시 한 줄을 아무 값으로 바꾸고 .terraform/ 을 지운 뒤 init 하세요. 체크섬 오류 출력을 /root/tfb-lock/tamper.txt 에 저장합니다. 그다음 잠금 파일을 지우고 다시 init 해서 제대로 된 해시로 되돌리세요.
잠금 파일의 해시는 꾸러미가 그때 그것과 같은지를 확인하는 장치입니다. 손댄 해시로는 어떤 꾸러미도 맞지 않으므로 설치가 멈춥니다. 잘못된 해시를 그대로 둔 채 -upgrade 만 붙여서는 풀리지 않습니다.
제약 없는 프로바이더를 찾아내는 점검을 만든다
/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=<종료 코드> 를 덧붙이세요.
잠금 파일은 한 줄씩 읽으면 됩니다. provider 로 시작하는 줄이 새 블록의 시작이고, 그 뒤에 constraints 줄이 나오는지만 기억하면 됩니다. awk 의 END 블록에서 마지막 블록도 판정해야 한 개를 놓치지 않습니다.