LabHub
学习 学习路径 课程

基础设施即代码

挪走状态文件后,同样的服务器变成了两台

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

상태 파일의 구조를 열어 보고, 상태를 잃었을 때의 중복 생성·lineage 안전장치·import 복구·동시 적용 잠금·상태 분할·커밋 규칙을 OpenTofu 로 하나씩 일으켜 확인합니다.

왜 중요한가

상태는 코드와 실제 인프라를 잇는 유일한 기록이라, 사고는 대개 코드가 아니라 상태에서 납니다. 상태가 사라지면 도구는 이미 있는 것을 모르고 또 만들고, 엉뚱한 상태로 덮어쓰면 남의 기록을 지웁니다. 복구는 import 로 하나씩 되돌리는 길뿐이라 버전 관리된 백엔드가 기본이 됩니다. 잠금이 있어야 두 적용이 겹치지 않고, 수명이 다른 층을 한 상태에 두지 않아야 매일 바뀌는 쪽 때문에 거의 안 바뀌는 쪽이 위험해지지 않습니다.

단계

  1. /root/iac-state/main.tfrandom_id.server(byte_length 4)와, out/server-<그 hex>.txtserver <hex> 한 줄을 쓰는 local_file.inventory 를 두고 init·apply 하세요. 그다음 상태 파일을 /root/iac-state/backup/terraform.tfstate 로 복사하고, 그 사본에서 읽은 값으로 /root/iac-state/anatomy.txtlineage=<값>, serial=<값>, resources=<리소스 블록 수> 세 줄을 적으세요.
  2. /root/iac-state/lost/ 를 만들고 terraform.tfstateterraform.tfstate.backup(있으면)을 그곳으로 옮기세요. tofu plan 출력을 /root/iac-state/lost-plan.txt 에 저장한 뒤 apply 하세요. 채점기는 옮긴 상태와 새 상태가 서로 다른 lineage 이고, out/ 에 서버 파일이 두 개 생겼는지 봅니다.
  3. tofu state push lost/terraform.tfstate 로 옛 상태를 되돌려 놓으려 해 보세요. 출력(오류 포함)을 /root/iac-state/push.txt 에 저장합니다. -force 는 쓰지 않습니다. 채점기는 지금 상태의 lineage 가 옛 상태와 여전히 다른지도 봅니다.
  4. /root/iac-state/rebuild/ 에 같은 main.tf 를 복사하고, import 블록으로 옛 상태(lost/terraform.tfstate)의 random_id.server 를 가져오세요. import 식별자는 옛 상태의 그 리소스 속성에서 찾습니다. rebuild 에서 init·apply 한 뒤 계획이 깨끗해야 합니다. 채점기는 rebuild 상태의 hex 가 옛 서버의 hex 와 같은지 봅니다.
  5. /root/iac-state/lockdemo/main.tf 에 local-exec 로 15초 자는 terraform_data.slow 를 두고 init 하세요. apply 를 백그라운드로 시작하고, 끝나기 전에 같은 디렉터리에서 tofu plan 을 돌려 출력을 /root/iac-state/lock.txt 에 저장하세요. 이어서 tofu plan -lock-timeout=60s 를 돌려 잠금이 풀릴 때까지 기다린 뒤의 출력을 /root/iac-state/lockwait.txt 에 저장합니다. 채점기는 임시 디렉터리에서 같은 상황을 직접 만들어도 확인합니다.
  6. /root/iac-state/network/random_id.vpc(byte_length 3)와 출력 vpc_id = "vpc-<hex>" 를, /root/iac-state/app/ 에는 terraform_remote_state 데이터 소스(local 백엔드, ../network/terraform.tfstate)로 그 출력을 읽어 out/app.confvpc=<vpc_id> 한 줄을 쓰는 local_file.app 을 두세요. network 를 먼저, app 을 나중에 init·apply 합니다. 두 디렉터리 모두 계획이 깨끗해야 합니다.
  7. /root/iac-state/.gitignore 를 만드세요. git check-ignore 기준으로 terraform.tfstate, terraform.tfstate.backup, lost/terraform.tfstate, .terraform/(그 안의 파일), change.tfplan 같은 계획 파일은 무시되고, .terraform.lock.hclmain.tf, app/main.tf 는 무시되지 않아야 합니다. 채점기는 임시 git 저장소에 이 .gitignore 를 두고 확인합니다.

참고

상태 파일을 열어 본다

/root/iac-state/main.tfrandom_id.server(byte_length 4)와, out/server-<그 hex>.txtserver <hex> 한 줄을 쓰는 local_file.inventory 를 두고 init·apply 하세요. 그다음 상태 파일을 /root/iac-state/backup/terraform.tfstate 로 복사하고, 그 사본에서 읽은 값으로 /root/iac-state/anatomy.txtlineage=<값>, serial=<값>, resources=<리소스 블록 수> 세 줄을 적으세요.

상태 파일은 JSON 입니다. lineage 는 이 상태가 처음 만들어질 때 붙는 고유 번호이고, serial 은 쓸 때마다 올라갑니다. jq 로 .lineage, .serial, .resources | length 를 보세요.

상태를 잃었더니 같은 서버가 하나 더 생겼다

/root/iac-state/lost/ 를 만들고 terraform.tfstateterraform.tfstate.backup(있으면)을 그곳으로 옮기세요. tofu plan 출력을 /root/iac-state/lost-plan.txt 에 저장한 뒤 apply 하세요. 채점기는 옮긴 상태와 새 상태가 서로 다른 lineage 이고, out/ 에 서버 파일이 두 개 생겼는지 봅니다.

도구는 상태에 없는 리소스를 '아직 만들지 않은 것' 으로 봅니다. 이름이 무작위로 정해지는 리소스라 충돌 없이 조용히 하나가 더 생깁니다. 계획 출력의 Plan 줄을 읽으세요.

옛 상태를 덮어쓰려 했더니 거절됐다

tofu state push lost/terraform.tfstate 로 옛 상태를 되돌려 놓으려 해 보세요. 출력(오류 포함)을 /root/iac-state/push.txt 에 저장합니다. -force 는 쓰지 않습니다. 채점기는 지금 상태의 lineage 가 옛 상태와 여전히 다른지도 봅니다.

lineage 가 다른 두 상태는 '같은 인프라의 다른 시점' 이 아니라 '서로 다른 두 기록' 입니다. 도구가 이것을 막아 주는 이유를 생각해 보세요. -force 는 이 안전장치를 끄는 옵션입니다.

잃은 서버를 import 로 되찾는다

/root/iac-state/rebuild/ 에 같은 main.tf 를 복사하고, import 블록으로 옛 상태(lost/terraform.tfstate)의 random_id.server 를 가져오세요. import 식별자는 옛 상태의 그 리소스 속성에서 찾습니다. rebuild 에서 init·apply 한 뒤 계획이 깨끗해야 합니다. 채점기는 rebuild 상태의 hex 가 옛 서버의 hex 와 같은지 봅니다.

random_id 는 import 를 지원하고, 식별자로 b64_url 값을 받습니다(프로바이더 문서). import 블록의 to 와 id 두 가지만 쓰면 되고, 계획에 'will be imported' 가 보여야 합니다. 새로 만들어지는 random_id 가 있으면 틀린 것입니다.

적용하는 동안 다른 계획은 잠금에 막힌다

/root/iac-state/lockdemo/main.tf 에 local-exec 로 15초 자는 terraform_data.slow 를 두고 init 하세요. apply 를 백그라운드로 시작하고, 끝나기 전에 같은 디렉터리에서 tofu plan 을 돌려 출력을 /root/iac-state/lock.txt 에 저장하세요. 이어서 tofu plan -lock-timeout=60s 를 돌려 잠금이 풀릴 때까지 기다린 뒤의 출력을 /root/iac-state/lockwait.txt 에 저장합니다. 채점기는 임시 디렉터리에서 같은 상황을 직접 만들어도 확인합니다.

잠금은 상태를 쓸 수 있는 모든 명령이 자동으로 잡습니다. 기본으로는 기다리지 않고 바로 실패하며, -lock-timeout 을 주면 그 시간만큼 기다립니다. 백그라운드 작업은 & 로 띄우고 wait 로 끝을 기다립니다.

수명이 다른 층은 상태를 나누고 출력으로만 잇는다

/root/iac-state/network/random_id.vpc(byte_length 3)와 출력 vpc_id = "vpc-<hex>" 를, /root/iac-state/app/ 에는 terraform_remote_state 데이터 소스(local 백엔드, ../network/terraform.tfstate)로 그 출력을 읽어 out/app.confvpc=<vpc_id> 한 줄을 쓰는 local_file.app 을 두세요. network 를 먼저, app 을 나중에 init·apply 합니다. 두 디렉터리 모두 계획이 깨끗해야 합니다.

app 상태에는 네트워크 리소스가 들어가지 않고 데이터 소스만 들어갑니다. 그래서 app 을 매일 바꿔도 네트워크는 잠기거나 계획에 끼지 않습니다. 대신 app 은 network 의 출력 이름에 의존하게 됩니다.

상태와 계획은 커밋하지 않고, 잠금 파일은 커밋한다

/root/iac-state/.gitignore 를 만드세요. git check-ignore 기준으로 terraform.tfstate, terraform.tfstate.backup, lost/terraform.tfstate, .terraform/(그 안의 파일), change.tfplan 같은 계획 파일은 무시되고, .terraform.lock.hclmain.tf, app/main.tf 는 무시되지 않아야 합니다. 채점기는 임시 git 저장소에 이 .gitignore 를 두고 확인합니다.

상태와 저장한 계획에는 값이 평문으로 들어갑니다(다음 실습에서 직접 봅니다). 잠금 파일은 프로바이더 버전과 해시를 고정하므로 코드와 함께 리뷰돼야 합니다. *.tfstate.* 같은 패턴이 무엇까지 잡는지 확인하세요.