2025년 Terraform에서 현재 구성으로 옮기는 순서

현재 다시 선택할 제품 설정과 당시 설계 결함을 나누고, 되돌릴 수 있는 이전 경로를 고르는 기준

2025년 구성에는 metadata 계약, import 확인, 리소스 소유권, 비밀값 관리에서 각각 결함이 있었다. 현재 다시 선택할 제품 설정과 당시에도 할 수 있었던 설계 수정을 구분해야 이전 순서를 정할 수 있다.

아래 제품 정보와 가격은 2026년 8월 28일에 확인했다. 요금은 자주 바뀌므로 실제 결정 전에 계산기로 다시 확인해야 한다.

현재 기준으로 다시 고를 제품 설정

구형 선점형 VM 대신 Spot VM을 선택한다. 2025년 구성은 preemptible = true와 automatic_restart = false만 지정했다. 이 옵션은 당시에도 지원됐지만 24시간 최대 실행 시간이 있는 구형 선점형 VM이다. 현재 구성은 provisioning_model = "SPOT"과 instance_termination_action을 사용한다. 종료 동작을 STOP으로 두면 인스턴스가 TERMINATED 상태로 남고 연결된 영구 디스크를 다시 사용할 수 있다. 실행 시간을 별도로 제한하지 않은 Spot VM에는 최소나 최대 실행 시간 제한이 없다.

새 함수는 Cloud Run function 배포 경로를 사용한다. 2025년 구성은 1세대 google_cloudfunctions_function 위에 있었다. 2세대는 이제 Cloud Run functions라고 부른다. 기존 2세대 구성을 호환하는 google_cloudfunctions2_function도 남아 있지만, Google Cloud 문서는 새 Terraform 배포에 함수 컨테이너를 빌드해 google_cloud_run_v2_service로 배포하는 경로를 안내한다.

Provider 메이저가 올랐다. 서버 루트는 ~> 5.0으로 묶여 있었고 나머지 두 루트는 >= 5.0.0이라 lock에 6.x가 들어가 있다. 지금 최신은 7.x다. 루트마다 출발점이 다르고, 메이저 업그레이드는 속성 이름과 기본값이 바뀌므로 한 번에 건너뛰지 않는다.

Cloud Shell에서 Terraform이 빠졌다. 2026년 6월 릴리스 노트 기준으로 기본 이미지에 CLI가 없다. 실행 환경을 준비하는 단계가 하나 늘었다.

네트워크 이그레스를 따로 계산해야 한다. 2026년 8월 공개 가격표에서 서울 리전에서 한국 사용자에게 나가는 인터넷 트래픽의 첫 1 TiB 구간은 GiB당 0.19달러다. 대상 목적지와 사용량 구간에 따라 단가가 달라지므로 실제 사용 조건으로 다시 계산해야 한다. VM 요금만 계산하면 이 비용이 빠진다.

판단이 아쉬웠던 지점

다음 항목은 제품 변경과 무관하며 당시에도 다르게 구성할 수 있었다.

원격 backend는 2025년에도 있었다. 작업 디렉터리의 local state에는 여러 실행자가 공유할 원격 locking과 접근 제어가 없었고, 백업 사본이 Git에 커밋됐다. apply -auto-approve 배치에는 계획을 저장하고 읽는 단계도 없었고, import가 어디까지 됐는지는 apply가 멈춘 뒤에 알았다.

디렉터리는 나눴지만 어느 state가 무엇을 책임지는지 정하지 않아 API가 두 곳에 겹쳤다. 비밀값을 변수 기본값에 넣은 것은 시점과 무관하게 피할 수 있었다. 읽지 않는 metadata 키와 실행되지 않는 복원 분기는 부팅을 한 번만 처음부터 확인했어도 드러났다.

이전 경로는 두 가지다

기존 서버를 살린 채 옮기는 방법은 크게 둘이다. 접속 주소를 유지해야 하는지, state를 직접 옮길 수 있는지에 따라 갈린다.

경로 A: 제자리에서 옮기기

기존 리소스와 state를 유지하면서 구성만 현재 기준으로 끌어올린다.

  1. 되돌릴 준비. 월드를 백업하고 복원 시험까지 마친다. 이 단계를 건너뛰면 뒤의 어느 단계에서 실패해도 월드를 되돌릴 수 없다.
  2. state를 원격으로 옮긴다. backend를 추가하고 terraform init -migrate-state를 실행한다. .terraform.lock.hcl을 버전 관리에 넣는다.
  3. 비밀값을 먼저 무효화한다. 폐기와 재발급이 코드 정리보다 앞이다. 그다음 변수 기본값과 스크립트의 폴백 상수를 제거한다.
  4. Provider를 한 메이저씩 올린다. 각 단계에서 plan을 저장해 읽고, 예상하지 않은 교체가 있으면 멈춘다.
  5. 소유권을 정리한다. 겹친 API를 한 루트로 모은다. terraform state rm과 import를 쓰되 한 번에 하나씩 하고 매번 plan으로 확인한다.
  6. 선점형 설정을 현재 표현으로 바꾼다. 이 변경이 속성 수정인지 인스턴스 교체인지 반드시 plan에서 확인한다. 교체라면 부팅 디스크와 월드를 어떻게 지킬지 먼저 정하고 진행한다.
  7. 함수를 Cloud Run function으로 옮긴다. 새 서비스와 트리거를 확인한 뒤 옛 1세대 함수를 지운다.

각 단계의 되돌릴 지점은 다르다. 2단계를 되돌리려면 검증해 둔 state 사본을 보존하고 terraform init -migrate-state로 local backend에 다시 이전해야 한다. state 파일을 수동으로 복사하지 않는다. 5단계와 6단계는 되돌리기 어렵다. state를 잘못 건드리면 코드와 실제 리소스의 연결을 잃고, 인스턴스가 교체되면 디스크가 사라질 수 있다.

경로 B: 새로 만들고 데이터만 옮기기

현재 기준의 구성으로 새 프로젝트나 새 이름 공간에 서버를 만들고, 월드만 가져온다. 최종 백업을 시작할 때부터 옛 서버의 월드에는 새 쓰기가 생기지 않게 한다.

  1. 구축 트랙 절차대로 새 서버를 만든다.
  2. 접속자를 내보내고 옛 서버를 정상 종료한 뒤 최종 백업을 받아 검증한다.
  3. 새 서버에 월드를 가져온다.
  4. 옛 서버는 중지한 채 새 서버 접속을 확인하고 플레이어에게 새 주소를 알린다.
  5. 새 백업과 플레이 기록을 확인하며 며칠 지켜본 뒤 옛 서버를 정리한다.

새 서버에서 플레이하기 전에 되돌린다면 새 서버를 중지한 뒤 옛 서버를 다시 켠다. 새 월드에 변경이 생긴 뒤에는 옛 서버를 곧바로 켜면 안 된다. 새 서버를 중지해 백업한 뒤 그 변경을 옛 서버로 가져올지, 새 서버에서 생긴 변경을 버리고 최종 이전 시점으로 돌아갈지 먼저 정한다. 두 월드를 동시에 쓰지 않는 동안 옛 서버의 state를 손대거나 Provider 업그레이드 경로를 밟을 필요는 없다.

대신 접속 주소가 바뀌고, 옮기는 동안 새 VM과 두 환경의 디스크·고정 IP·Storage 비용이 겹칠 수 있다.

이전 경로 선택 기준

개인이 운영하는 서버 한 대라면 경로 B를 권한다. 경로 A의 어려운 단계들이 지키려는 것은 고정 IP와 리소스 이름이다. 친구 몇 명에게 새 주소를 알리는 비용보다 state를 잘못 만질 위험이 더 크다. 새 서버에서 플레이하기 전에는 옛 서버를 다시 켜는 되돌리기 지점도 남는다. 플레이가 시작된 뒤의 변경은 앞 절처럼 별도로 옮기거나 포기할 범위를 정해야 한다.

경로 A가 맞는 경우도 있다. 주소가 여러 곳에 등록돼 바꾸기 어렵거나, 데이터가 커서 이전 시간이 길거나, 조직 정책상 프로젝트를 새로 만들 수 없을 때다. 그때는 위 순서를 지키되 5단계와 6단계 전에 반드시 백업을 다시 받는다.

당시 원형과 지금 예제의 대응

항목2025년 구성구축 트랙 예제
state작업 디렉터리의 로컬 파일GCS backend, 버전 관리
배포apply -auto-approve저장한 plan을 검토 후 적용
선점형 표현레거시 preemptibleprovisioning_model = "SPOT", 종료 시 STOP
관리 접속root SSH와 작업 트리에 둔 키 파일OS Login과 IAM
서버 파일 배포metadata에 스크립트·유닛·앱 ZIPURL과 SHA-256으로 JAR 검증
비밀값변수 기본값서버에 비밀값을 두지 않음
백업 보존버킷 lifecycle과 정리 함수 양쪽버킷 lifecycle 한 곳
복구 함수1세대 함수, Monitoring 알림 경유Cloud Run functions, 로그 sink 경유
함수 권한프로젝트 역할최소 권한 custom role

왼쪽 열은 당시 저장소와 state에서 확인한 구성이고, 오른쪽 열은 그 결함을 줄인 공개 예제다.

검사 절차를 추가한 이유

계약의 양쪽을 맞춰 보지 않았고, import가 어디까지 됐는지 읽지 않았고, 소유권을 적어 두지 않았고, 비밀값이 어디까지 갔는지 세지 않았다. 네 가지 모두 확인하는 단계가 절차에 없었기 때문에 생겼다.

구축 트랙의 예제가 검사 스크립트와 컨테이너 시험을 달고 있는 이유가 이것이다. 그 검사들은 구현과 기대가 어긋나는 지점을 반복해서 확인하게 한다. 실제로 이 연재를 준비하며 코드를 다시 읽다가 예제의 백업 검증 스크립트에서 결함을 하나 찾았다. 컨테이너 시험은 그 결함을 잡지 못하고 통과하고 있었다. 가짜 Java가 작업 디렉터리를 확인하지 않았기 때문이다. 시험을 실제에 가깝게 고치자 그때야 같은 결함이 시험에서도 재현됐다.

이 경험 뒤에는 구현과 함께 확인 절차를 만들고, 그 절차가 실제 실패 조건을 재현하는지 검토하는 것을 작업 범위에 포함한다.

참고

Comments

댓글

    이미지 확대