Terraform 실전 · workspace 와 디렉터리 분리 · 이론
workspace 는 환경 분리 도구가 아니다
한 줄 요약
workspace 는 하나의 백엔드·하나의 자격증명 안에서 상태만 여러 벌 갖는 장치다. 환경이 갈라지는 지점이 권한이라면 workspace 로는 나눌 수 없다.
왜 이게 필요했나 — 그리고 왜 오해받나
환경을 나누는 일은 거의 언제나 이렇게 시작한다. dev 에서 만든 스택을 prod 에도 올려야 하는데, 코드를 통째로 복사하기는 싫다. 이때 workspace new prod 는 완벽한 답처럼 보인다. 코드는 한 벌이고, 명령 한 줄로 갈아타고, 프로바이더 캐시도 다시 받지 않는다.
문제는 이 도구가 해결하는 것이 무엇인지에 있다. workspace 가 갈라 주는 것은 상태(state)뿐이다. 백엔드는 그대로 하나고, 그 백엔드를 여는 자격증명도 하나고, 코드도 하나다. 그래서 dev 를 만질 권한만 줘야 하는 사람에게 workspace 를 쓰게 하면, 그 사람은 prod 상태가 들어 있는 같은 저장소를 여는 열쇠를 이미 쥐고 있는 것이다.
공식 문서도 같은 말을 한다. OpenTofu 의 Workspaces 문서는 workspace 가 "시스템 분해나, 별도의 자격증명과 접근 제어가 필요한 배포에는 적절하지 않다" 고 적고, 더 큰 시스템에서는 아키텍처 경계에 맞춰 설정 자체를 나누라고 권한다. 즉 workspace 는 환경 분리 도구가 아니라 *같은 환경의 임시 사본*을 만드는 도구다. 문서가 드는 대표 예도 기능 브랜치용 임시 복제본이다.
어떻게 동작하나
local 백엔드에서 실제로 무슨 일이 일어나는지는 파일 배치가 다 말해 준다.
tfa-ws/├── terraform.tfstate ← default workspace├── terraform.tfstate.d/│ ├── dev/terraform.tfstate│ └── prod/terraform.tfstate└── .terraform/ ├── providers/ ← 캐시는 한 벌뿐이다 └── environment ← 지금 선택된 이름이 여기에만 있다세 가지를 읽어야 한다.
- 기본 workspace 만 경로가 다르다.
default의 상태는 예전 그대로 작업 디렉터리에 있고, 나머지는terraform.tfstate.d/<이름>/아래로 간다. 원격 백엔드는 각자 다른 규칙으로 접두어를 붙인다. - 선택은 로컬 상태다. 지금 어느 workspace 인지는
.terraform/environment라는 파일 한 줄에만 적혀 있다. 이 파일은 커밋되지 않고, 다른 사람의 작업 디렉터리와 공유되지도 않는다. "어느 환경에 명령을 내리고 있는가" 가 코드나 리뷰에 남지 않는다는 뜻이다. - 설정에서는 이름만 보인다.
terraform.workspace로 이름을 읽어 값을 갈아 끼울 수 있다. 크기를 맵으로 두고lookup하는 방식이 흔하다. 편리하지만, 맵에 없는 이름을 선택하면 기본값으로 조용히 떨어진다.
디렉터리 분리는 정확히 반대 성질을 갖는다. 환경마다 디렉터리가 따로라 상태도 따로, init 도 따로, 프로바이더 캐시도 따로다. 문서가 인정하는 대로 디스크와 대역폭을 더 쓰고 설정 갱신도 각각 해야 한다. 대신 백엔드 설정을 환경마다 다르게 줄 수 있고, 저장소 권한으로 "prod 디렉터리는 누가 바꿀 수 있는가" 를 코드 리뷰 규칙으로 만들 수 있다.
현장에서 만나는 모습
가장 흔한 사고는 문법이 아니라 선택이다. 터미널에는 prod 라고 아무 데도 쓰여 있지 않고, 프롬프트에도 안 뜨고, 명령어에도 안 들어간다. 어제 prod 를 보다가 그대로 두고 퇴근한 디렉터리에서 오늘 아침 destroy 를 치면 그것으로 끝이다. 실습 5단계에서 이 사고를 일부러 낸다.
두 번째는 권한이 이미 갈라져 있는데 workspace 로 나눈 팀이다. 감사에서 "dev 담당자가 prod 상태를 읽을 수 있는가" 를 물으면 답은 언제나 그렇다이다. 백엔드가 하나라서다. 이 지적은 코드를 고쳐 막을 수 없고, 디렉터리(또는 별도 설정)로 옮기는 이전 작업이 된다.
세 번째는 워크스페이스 이름이 코드의 조건문으로 새는 것이다. terraform.workspace == "prod" ? ... : ... 이 서너 군데를 넘어가면, 그 코드는 이미 환경마다 다른 두 코드다. 그때가 디렉터리로 나눌 시점이다.
막는 방법은 단순하다. 쓰기 명령 앞에 문지기 한 줄을 세워 기대한 workspace 가 아니면 멈추게 한다. CI 에서는 환경 이름을 파이프라인 변수로 받아 같은 검사를 한다. 사람의 기억 대신 종료 코드가 판단하게 만드는 것이다. 종료 코드를 둘이 아니라 셋으로 나누는 것도 실무에서 값어치가 있다 — "기대와 다르다" 와 "스크립트를 잘못 불렀다" 를 파이프라인이 구분해야 재시도할지 사람을 부를지 정할 수 있기 때문이다.
정리하면 선택 기준은 이렇게 된다. 상태만 나누면 되는가, 아니면 자격증명·백엔드·리뷰 권한까지 나눠야 하는가. 앞이면 workspace 가 가장 싼 답이고, 뒤면 workspace 는 답이 아니다. 둘을 섞어 쓰는 것도 흔하다 — prod 와 비운영을 디렉터리로 크게 가르고, 비운영 안에서 개발자별 임시 사본을 workspace 로 두는 식이다.
다음 실습에서 할 것
/root/tfa-ws 에서 workspace 세 개를 만들어 상태 파일이 어디에 생기는지 직접 열어 보고, 환경마다 다른 값을 맵으로 주고, prod 를 선택한 채 destroy 를 돌려 사고를 낸 뒤 복구합니다. 그다음 같은 결과를 디렉터리 분리로 다시 만들고, 상태 파일 수와 프로바이더 캐시 수를 세어 두 방식을 표로 견줍니다.