LabHub
学习 学习路径 课程

Terraform 实战

没注意选中的是 prod,就执行了 destroy

在 LabHub 中继续学习

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

목표

workspace 로 환경을 나눌 때 상태가 실제로 어디에 생기는지 열어 보고, 선택 실수 사고를 직접 낸 뒤 문지기로 막고, 같은 것을 디렉터리 분리로 만들어 두 방식을 숫자로 견줍니다.

왜 중요한가

환경을 나누는 첫 도구로 workspace 를 집는 이유는 분명합니다 — 코드가 한 벌이고, 프로바이더 캐시도 한 벌이고, 명령 한 줄로 갈아탑니다. 그런데 그 장점이 그대로 위험입니다. 백엔드가 하나라는 말은 dev 를 쓰는 사람과 prod 를 쓰는 사람이 같은 자격증명으로 같은 저장소를 연다는 뜻이고, '지금 어느 환경인가' 는 화면이 아니라 작업 디렉터리 안 파일 하나에만 적혀 있습니다. 공식 문서가 workspace 를 시스템 분해나 자격증명이 갈라지는 배포에 쓰지 말라고 적은 이유가 이것입니다. 그래서 실무의 선택은 '둘 중 무엇이 옳은가' 가 아니라 '무엇을 무엇으로 나누는가' 입니다 — 같은 팀이 쓰는 임시 사본은 workspace 로, 권한이 갈라지는 경계는 디렉터리로 나눕니다.

단계

  1. /root/tfa-ws/main.tf 에 local 프로바이더와 local_file.env 를 두세요. 파일 이름은 out/<workspace 이름>.conf, 내용은 첫 줄 workspace=<workspace 이름>, 둘째 줄 replicas=<local.replicas> 입니다. /root/tfa-ws/sizes.tf 에는 localsreplicas = 1 만 둡니다. init 하고 apply 하세요.
  2. devprod workspace 를 만들고 각각에서 apply 하세요. 끝나면 /root/tfa-ws/out/ 에 세 파일이, /root/tfa-ws/terraform.tfstate.d/ 아래에 두 개의 상태 파일이 있어야 합니다.
  3. /root/tfa-ws/where.tsv 에 workspace 세 개를 이름순(default·dev·prod)으로 한 줄씩 적으세요. 탭으로 나눈 세 칸은 <workspace 이름>, /root/tfa-ws 기준 상태 파일 상대경로, 그 상태의 lineage 입니다.
  4. /root/tfa-ws/sizes.tf 를 고쳐 local.sizes 맵(default = 1, dev = 2, prod = 6)에서 지금 workspace 의 값을 lookup 으로 꺼내 local.replicas 로 쓰세요. 세 workspace 모두에서 다시 apply 하면 out/ 의 세 파일이 서로 다른 replicas 값을 갖습니다.
  5. prod 를 선택한 상태에서 tofu destroy -auto-approve 를 돌려 사고를 직접 내세요. 출력은 /root/tfa-ws/incident/prod-destroy.log 에, 그때 선택돼 있던 workspace 이름은 /root/tfa-ws/incident/selected.txt 에(.terraform/environment 를 그대로 복사) 남깁니다. 그다음 /root/tfa-ws/incident.mdselected:, command:, lost:, recovery: 네 줄을 적고, prod 를 다시 apply 해 복구하세요.
  6. /root/tfa-ws/guard.sh 를 만드세요. 인자로 받은 이름과 지금 선택된 workspace 가 같으면 확인 메시지를 내고 0 으로, 다르면 지금 선택된 이름을 보여 주며 1 로, 인자가 없으면 2 로 끝나야 합니다. workspace 를 바꾸지는 않습니다.
  7. /root/tfa-ws/modules/app/main.tfvar.env·var.replicas 를 받아 /root/tfa-ws/out-dir/<env>.conf 를 쓰는 모듈을 두고, /root/tfa-ws/envs/dev/root/tfa-ws/envs/prod 두 디렉터리에서 그 모듈을 각각 env="dev" replicas=2, env="prod" replicas=6 으로 부르세요. 두 디렉터리를 따로 init·apply 합니다. workspace 는 쓰지 않습니다.
  8. /root/tfa-ws/compare.tsv 에 아래 다섯 줄을 탭 두 칸으로 적으세요. 값은 지어내지 말고 지금 디스크에서 세어 적습니다. workspace_states = workspace 방식이 갖는 상태 파일 수, directory_states = envs/ 아래 상태 파일 수, workspace_plugin_dirs = workspace 방식의 .terraform 디렉터리 수, directory_plugin_dirs = envs/ 아래 .terraform 디렉터리 수, selected_marker = 지금 선택된 workspace 를 기억하는 파일의 상대경로.

참고

기본 workspace 의 상태는 어디에 있나

/root/tfa-ws/main.tf 에 local 프로바이더와 local_file.env 를 두세요. 파일 이름은 out/<workspace 이름>.conf, 내용은 첫 줄 workspace=<workspace 이름>, 둘째 줄 replicas=<local.replicas> 입니다. /root/tfa-ws/sizes.tf 에는 localsreplicas = 1 만 둡니다. init 하고 apply 하세요.

설정 안에서 지금 선택된 workspace 이름은 terraform.workspace 로 읽습니다(OpenTofu 에서도 이름은 terraform 그대로입니다). 기본 workspace 의 상태는 아직 특별한 디렉터리로 가지 않습니다 — apply 뒤 작업 디렉터리를 그냥 ls 해 보세요.

workspace 를 둘 더 만들면 상태가 어디로 가나

devprod workspace 를 만들고 각각에서 apply 하세요. 끝나면 /root/tfa-ws/out/ 에 세 파일이, /root/tfa-ws/terraform.tfstate.d/ 아래에 두 개의 상태 파일이 있어야 합니다.

tofu workspace new 는 만들면서 선택까지 합니다. 이미 있는 이름이면 오류가 나니 select 로 갈아타세요. 기본이 아닌 workspace 의 상태가 어느 경로에 생기는지 find 로 확인하고, 세 상태의 lineage 가 서로 다른지도 보세요.

어느 workspace 의 상태가 어느 파일인지 적는다

/root/tfa-ws/where.tsv 에 workspace 세 개를 이름순(default·dev·prod)으로 한 줄씩 적으세요. 탭으로 나눈 세 칸은 <workspace 이름>, /root/tfa-ws 기준 상태 파일 상대경로, 그 상태의 lineage 입니다.

상태 파일은 JSON 이라 lineage 는 jq 로 읽습니다. 기본 workspace 의 경로와 나머지 둘의 경로가 다르다는 것이 이 단계의 핵심입니다. 상대경로 앞에 ./ 를 붙이지 마세요.

같은 코드가 환경마다 다른 값을 갖게 한다

/root/tfa-ws/sizes.tf 를 고쳐 local.sizes 맵(default = 1, dev = 2, prod = 6)에서 지금 workspace 의 값을 lookup 으로 꺼내 local.replicas 로 쓰세요. 세 workspace 모두에서 다시 apply 하면 out/ 의 세 파일이 서로 다른 replicas 값을 갖습니다.

맵에 없는 이름이 선택됐을 때 무엇이 되어야 하는지 정하는 것이 lookup 의 세 번째 인자입니다. locals 안에서 다른 local 을 참조해도 됩니다. 값을 바꾼 뒤 workspace 마다 apply 를 한 번씩 돌려야 파일이 갱신됩니다.

선택을 잘못한 채 명령을 내린다

prod 를 선택한 상태에서 tofu destroy -auto-approve 를 돌려 사고를 직접 내세요. 출력은 /root/tfa-ws/incident/prod-destroy.log 에, 그때 선택돼 있던 workspace 이름은 /root/tfa-ws/incident/selected.txt 에(.terraform/environment 를 그대로 복사) 남깁니다. 그다음 /root/tfa-ws/incident.mdselected:, command:, lost:, recovery: 네 줄을 적고, prod 를 다시 apply 해 복구하세요.

workspace 는 현재 선택을 작업 디렉터리 안 파일 하나로 기억합니다 — .terraform/environment 를 열어 보세요. 사고는 '명령을 잘못 친 것' 이 아니라 '어디에 내리는지 화면 어디에도 안 보이는 것' 입니다. lost 줄에는 사라진 파일 경로를, recovery 줄에는 되살린 명령을 적습니다.

쓰기 명령 앞에 문지기를 세운다

/root/tfa-ws/guard.sh 를 만드세요. 인자로 받은 이름과 지금 선택된 workspace 가 같으면 확인 메시지를 내고 0 으로, 다르면 지금 선택된 이름을 보여 주며 1 로, 인자가 없으면 2 로 끝나야 합니다. workspace 를 바꾸지는 않습니다.

지금 선택된 이름은 tofu workspace show 한 줄로 나옵니다. 종료 코드를 셋으로 나누는 이유는 CI 에서 '틀렸다' 와 '잘못 불렀다' 를 구분해야 하기 때문입니다. 채점기가 이 스크립트를 직접 세 가지 방식으로 불러 봅니다.

같은 것을 디렉터리 분리로 만든다

/root/tfa-ws/modules/app/main.tfvar.env·var.replicas 를 받아 /root/tfa-ws/out-dir/<env>.conf 를 쓰는 모듈을 두고, /root/tfa-ws/envs/dev/root/tfa-ws/envs/prod 두 디렉터리에서 그 모듈을 각각 env="dev" replicas=2, env="prod" replicas=6 으로 부르세요. 두 디렉터리를 따로 init·apply 합니다. workspace 는 쓰지 않습니다.

모듈 파일의 내용 형식은 1단계와 똑같이 두 줄입니다(workspace= 와 replicas=). 같은 결과를 workspace 없이 만드는 것이 목적입니다. 두 디렉터리가 각각 자기 상태와 자기 프로바이더 캐시를 갖는다는 점을 apply 뒤에 확인하세요.

두 방식을 숫자로 견준다

/root/tfa-ws/compare.tsv 에 아래 다섯 줄을 탭 두 칸으로 적으세요. 값은 지어내지 말고 지금 디스크에서 세어 적습니다. workspace_states = workspace 방식이 갖는 상태 파일 수, directory_states = envs/ 아래 상태 파일 수, workspace_plugin_dirs = workspace 방식의 .terraform 디렉터리 수, directory_plugin_dirs = envs/ 아래 .terraform 디렉터리 수, selected_marker = 지금 선택된 workspace 를 기억하는 파일의 상대경로.

네 숫자는 find 로 셉니다(envs/ 아래 것은 workspace 쪽 수에 넣지 않습니다). 마지막 줄은 5단계에서 복사했던 그 파일입니다. 이 다섯 줄이 '왜 workspace 는 자격증명 경계를 못 만드나' 의 답입니다 — 백엔드도 캐시도 한 벌이기 때문입니다.