Terraform으로 실습 자원 만들기
100분 안팎
학습 목표
설정·계획·적용·상태 파일의 관계를 관찰합니다.
개념
손으로 만든 실행 환경을 코드로 옮깁니다
m04에서 검증한 이미지를 받아도 매번 네트워크 이름과 볼륨 경로를 기억해 입력하면 재현성이 떨어집니다. IaC는 원하는 자원 구성을 코드로 적고 현재 자원과 비교하여 변경을 계획하는 접근입니다. 이번 실습은 OpenTofu가 Terraform 형식의 HCL을 읽고 Docker provider로 로컬 자원을 관리합니다. 레슨 제목의 Terraform은 설정 생태계를 가리키며 명령은 tofu로 통일합니다. 유료 클라우드 계정 없이 전용 네트워크·서비스·볼륨을 실제로 만들도록 구성합니다.
앞 모듈의 계약부터 읽습니다
m04 artifacts에는 release.json, test-summary.json, notice-image.tar, checksums.json이 있습니다. release의 digestKind는 local-image-id이며 registryDigest는 null입니다. sha256 문자열이 있다고 레지스트리에서 내려받을 수 있는 digest라고 부르면 안 됩니다. prepare.py는 app/ci.py의 verify 함수를 재사용해 체크섬과 테스트 요약을 확인하고 tar를 docker load로 불러옵니다. 이어 Docker가 보고한 이미지 ID와 명세의 값을 비교하여 확인한 이미지를 입력으로 연결합니다.
새 입력을 임의 태그로 대체하지 않습니다
image_id에는 검증된 sha256 이미지 ID가 들어갑니다. 최신 태그로 바꾸면 CI가 검사한 파일과 실행 파일이 달라질 수 있습니다. 아카이브의 플랫폼도 기록한 OS와 아키텍처에 맞는지 확인합니다. 이 검사는 개인 CI 산출물의 변조 탐지이며 발신자 인증이나 서명 검증을 제공하지 않습니다. 출처를 모르는 파일과 함께 받은 체크섬을 믿어도 안전하다는 의미는 아닙니다. 환경 파일의 비밀은 HCL이나 tfvars에 추가하지 않고 다음 모듈에서 별도 주입합니다.
HCL 블록을 읽는 순서
terraform 블록은 CLI와 provider의 요구 버전을 선언합니다. provider 블록은 Docker 연결을 구성합니다. variable 블록은 외부 입력의 타입과 검증 조건을 정의합니다. resource는 관리할 자원이며 docker_network.notice의 앞부분은 종류, 뒷부분은 설정 안에서 부르는 이름입니다. Docker 화면에 나타나는 실제 이름은 name 속성입니다. output은 사용자가 확인할 결과를 노출합니다. 이름이 비슷해도 자원 주소와 원격 자원 ID는 다른 값이므로 오류 위치를 읽을 때 구분합니다.
참조로 생성 의존성을 표현합니다
컨테이너의 networks_advanced.name은 docker_network.notice.name을 참조하고 volumes.volume_name은 docker_volume.data.name을 참조합니다. 엔진은 이 참조 관계로 네트워크와 볼륨을 준비한 뒤 컨테이너를 만들도록 의존성을 계산합니다. 자원 선언을 파일 위쪽에 배치했다는 이유만으로 생성 순서가 결정되는 것은 아닙니다. 동일한 문자열을 직접 복사하면 관계가 코드에서 숨을 수 있습니다. 참조 가능한 값은 자원 참조로 연결하여 이름 변경에도 관계가 유지되도록 합니다.
상태는 설정과 실제 자원의 연결 기록입니다
설정은 원하는 모습이고 상태 파일은 관리 주소와 Docker 자원을 연결하는 기록입니다. 상태를 지우면 실제 자원이 자동 삭제되는 것이 아니라 관리 연결을 잃을 수 있습니다. 남아 있는 동일 이름의 자원 때문에 재생성이 실패할 수도 있습니다. terraform.tfstate를 버전 관리에서 제외하고 접근 권한을 제한합니다. 상태와 저장 plan에는 입력 값이 포함될 수 있으므로 민감한 파일로 취급합니다. 파일명을 보고 지워도 되는 캐시라고 판단하지 않습니다.
init과 lock이 고정하는 것을 구분합니다
tofu init은 공급자를 설치하고 의존성 lock을 만듭니다. main.tf의 ~> 4.6 제약은 선택할 버전을 정하며 lock의 해시는 내려받은 공급자 패키지 검증에 사용됩니다. 작성 환경에서는 tofu를 실행할 수 없어 실제 해시를 생성한 척 제공하지 않습니다. README의 외부 확인 절차로 lock을 생성·보존하고 다른 플랫폼의 checksum도 추가합니다. 다음 초기화는 -lockfile=readonly로 확인합니다. 버전 선언만 있는 현재 자료를 완전한 공급자 잠금 완료라고 보고하지 않습니다.
plan과 apply의 경계를 유지합니다
tofu plan -out=initial.tfplan은 적용할 계획을 저장하지만 자원을 생성하지 않습니다. tofu show -json으로 만든 자료를 review.py가 검사합니다. 이번 자동 정책은 예상한 세 자원의 create와 no-op만 허용합니다. 삭제·교체·수정은 사람의 검토 대상으로 차단합니다. 승인한 저장 plan을 tofu apply initial.tfplan으로 적용합니다. 새로 plan을 만들고 바로 apply하면 이전에 읽었던 계획과 다른 변경이 포함될 수 있습니다. 계획 이후 환경이 바뀌면 저장 계획도 다시 검토합니다.
호스트 공개 범위를 좁힙니다
컨테이너는 0.0.0.0:8080에서 listen하고 호스트 포트는 127.0.0.1:18085에만 게시합니다. 컨테이너 내부의 listen 주소와 호스트의 게시 주소는 서로 다른 경계입니다. 호스트 게시 주소를 0.0.0.0으로 바꾸면 외부 인터페이스에서도 접근 가능한 범위를 만들 수 있습니다. starter는 그 결함을 포함하며 static-check가 거부합니다. 수정한 뒤 Docker inspect에서도 HostIp를 확인합니다. 설정 파일만 맞았다고 실제 실행 경계가 같다고 단정하지 않습니다.
볼륨의 소유권과 생존 기간
m04 Dockerfile은 /data를 10001 사용자에게 쓰기 가능한 경로로 준비합니다. 새 named volume은 기본 복사 동작을 통해 이미지의 경로 내용을 받을 수 있지만 기존 볼륨은 예전 권한을 유지할 수 있습니다. permission denied가 나면 /data와 실제 볼륨 소유권을 조사합니다. root 실행으로 즉시 바꾸는 대신 데이터 쓰기 조건을 확인합니다. volume에는 prevent_destroy를 두어 계획 단계에서 우발적인 삭제를 막습니다. 컨테이너가 교체되어도 같은 볼륨을 재사용하는 설계를 설명합니다.
완료는 생성 목록 이상의 증거입니다
check.sh는 HCL validate 후 계획을 검사하고 apply한 뒤 /health와 /notices를 요청합니다. 실제 이미지 ID·네트워크·마운트·포트 게시를 inspect로 확인합니다. 개인 서비스 컨테이너만 재시작하고 안내판 내용이 유지되는지 비교합니다. 이어 두 번째 plan의 종료 상태 0으로 수렴을 확인합니다. 이 명령들은 작성 환경에서 실행하지 않았으므로 아래 단계의 output은 비워 둡니다. 외부 검증 담당자는 evidence/http.json과 second-plan.json을 보존하고 실제 성공 여부를 기록합니다.
실패를 다음 실행 전에 분류합니다
checksum mismatch는 IaC 문법 문제가 아니라 입력 산출물의 무결성 문제입니다. project validation 오류는 개인 자원 이름 계약 위반입니다. port is already allocated는 호스트 포트 충돌을 조사합니다. provider 설치 오류는 init 단계 로그를 보고 공급자 접근과 버전을 확인합니다. 각 실패를 피하려고 검사를 삭제하면 이후 단계가 잘못된 가정 위에서 실행됩니다. 서비스 자원은 다음 모듈로 이어가며 전역 prune을 사용하지 않습니다. 데이터 보존과 승인된 정리 절차는 후속 모듈에서 완성합니다.
리소스 속성은 Docker provider 4.6 문서를 기준으로 검토했습니다.
따라하기
앞 산출물 연결
미션 zip 루트에서 m04 artifacts 절대 경로와 개인 이름을 지정합니다. 이미지 tar를 새로 빌드하지 않고 검증하여 불러옵니다.
python3 prepare.py /절대경로/m04/artifacts bc-yourname공급자 준비
외부 실행 환경에서 초기화하고 실제 lock을 생성·보존합니다. 이어 fmt와 validate가 성공하는지 확인합니다. 이 단계 출력은 작성 환경에서 확인하지 않았습니다.
tofu init -input=false
tofu providers lock -platform=darwin_arm64 -platform=linux_amd64
tofu fmt -check
tofu validate계획 저장과 검토
JSON actions가 create 또는 no-op이고 대상이 세 주소인지 확인합니다. 허용된 저장 계획만 적용합니다. starter의 host ip를 loopback으로 고친 뒤 진행합니다.
tofu plan -input=false -out=initial.tfplan
tofu show -json initial.tfplan > plan-initial.json
python3 -B review.py plan-initial.json실제 결과 검사
bash check.sh는 검토한 계획 적용, HTTP, 전용 자원 inspect, 재시작 보존과 두 번째 plan을 확인합니다. evidence/ 파일을 열어 명세와 실제 이미지 ID를 대조합니다. 앞 단계 산출물 준비부터 한 번에 실행하는 bash prepare-and-check.sh도 같은 검사를 거칩니다.
bash check.sh확인 문제
실습
main.tf의 호스트 게시 IP를 loopback으로 고칩니다. m04 artifacts를 prepare.py로 가져온 뒤 check.sh로 실제 자원 생성과 요청·재시작 보존·수렴을 확인합니다. AWS 설계는 network-design.md와 허용 표로 제출합니다. 단계별로 따라 하지 않고 한 번에 확인하려면 bash prepare-and-check.sh를 실행합니다. m04 산출물이 없으면 app/에서 CI를 돌려 만들고, prepare.py와 lock 생성, check.sh까지 이어서 수행합니다. 명령 앞의 BOOTCAMP_AUTOCLEAN=1은 자동 검증기가 끝난 뒤 자원을 지우는 용도이며 학습자는 붙이지 않습니다. OpenTofu가 Docker 소켓을 못 찾으면 DOCKER_HOST를 docker context의 주소로 지정합니다.
실행 명령
BOOTCAMP_AUTOCLEAN=1 bash prepare-and-check.sh
기대 결과
PASS apply, HTTP, persistence, second plan and state protection
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- VPC·서브넷·보안그룹의 역할을 설명합니다.