Terraform과 NKS로 NCP 운영 계정을 이전한 과정
NCP 운영 계정을 옮기며 Terraform·NKS와 배포 경로를 재구성한 과정을 설명합니다.
목차
들어가며
운영 중인 서비스를 다른 클라우드 계정으로 옮기려면 무엇을 확인해야 할까요? 새 계정에 서버를 만드는 것 외에도, 사용자가 쌓은 데이터와 로그인 세션을 옮기고 이후 배포와 모니터링이 이어지는지 확인해야 합니다.
2026년 10월, 클라우드 크레딧을 활용하기 위해 NCP에서 운영하던 그룹웨어 WITIM을 새 NCP 계정으로 옮겼습니다. WITIM은 이미 NKS(Naver Kubernetes Service)에서 운영 중이었습니다. 이번 작업에서는 Terraform으로 새 계정의 인프라를 구성하고 NKS에 기존 서비스를 올린 뒤, 데이터와 배포 경로까지 전환했습니다. 실제 사용자 영향은 약 13분이었습니다.
처음에는 새 계정에 필요한 자원을 다시 만들면 이전을 시작할 수 있을 거라고 생각했습니다. 그런데 작업을 진행하던 담당자가 퇴사한 뒤 남겨진 인프라 문서와 이전 계획, PR을 살펴보니 문서와 실제 리소스가 맞지 않았습니다. 새 계정에 생성했던 리소스 일부는 이미 삭제돼 있었고, 기존 계정에는 VM 시절의 로드밸런서와 사용이 끝난 서비스가 남아 있었습니다. Terraform이 관리 중인 자원을 기록하는 state에도 실제로 존재하지 않는 리소스가 있었습니다.
이 상태에서 apply를 실행하면 어떤 자원을 복구하고 어떤 자원을 다시 만드는지 판단하기 어렵습니다. 기존 자원이 의도치 않게 교체될 수 있고, 더 이상 쓰지 않는 서비스까지 새 계정에 만들 수도 있었습니다.
그래서 먼저 문서·PR·Git 이력과 NCP 콘솔·API·Kubernetes·Terraform state를 대조했습니다. 확인한 현재 상태를 기준으로 이전 범위를 정한 뒤, 재구성·데이터 이전·리허설·운영 전환 순서로 진행했습니다. 이 글에서는 그 과정에서 어떤 자원을 옮기기로 했고, 새 계정에서 계속 운영하기 위해 무엇을 다시 연결했는지 설명하겠습니다.
1. 실제 운영 환경을 확인해 이전 대상을 정했습니다
WITIM의 운영 방식은 몇 달 사이에 바뀌었습니다. 4월에는 VM과 오토스케일링 그룹을 사용했고, 6월 말부터는 NKS와 ArgoCD로 배포했습니다. 하지만 VM 시절의 로드밸런서는 여전히 남아 있었고, 사용이 사실상 끝난 별도 서비스도 노드풀을 차지하고 있었습니다.
운영 문서도 작성 시점에 따라 설명하는 구조가 달랐습니다. 4월 문서에는 VM이 직접 컨테이너 이미지를 받아 실행하는 흐름이, 이후 문서에는 ArgoCD가 Git에 기록된 이미지 태그를 읽어 NKS에 배포하는 흐름이 남아 있었습니다. 각 문서는 당시의 구조를 설명하고 있었으므로, 10월에도 어느 설명이 유효한지 확인해야 했습니다.
Git 이력으로 구조가 바뀐 시점을 확인하고, 실제 서비스가 연결된 로드밸런서와 NKS 워크로드를 대조했습니다. 각 서비스에 트래픽이 들어오는지, 연결된 노드가 있는지, 지금도 배포 대상인지 살펴봤습니다. 리소스가 남아 있다는 이유만으로 이전 대상에 포함하지는 않았습니다.
이를 기준으로 이전 범위를 다음과 같이 정리했습니다.
| 구분 | 결정 | 이유 |
|---|---|---|
| WITIM 본체·실시간 서버·로그인 서비스 | 이전 | 실제 사용자가 이용하는 서비스 |
| PostgreSQL·Redis | 이전 | 메시지, 근태, 세션과 사용량 기록 보존 필요 |
| ArgoCD·로그·알림·배포 파이프라인 | 이전 및 재구성 | 서비스가 실행되는 것과 계속 운영 가능한 것은 다름 |
| 예전 Orbit 서비스·시험용 관리자 앱 | 새 계정에는 만들지 않음 | 현재 운영 범위에서 제외할 수 있음 |
| VM 시절 로드밸런서 등 잔여 자원 | 이전 제외 | 더 이상 서비스 트래픽을 처리하지 않음 |
새 계정의 Terraform state에는 이미 삭제된 리소스 9개가 남아 있었습니다. 코드·state·실제 리소스가 서로 다르면 plan에 나온 생성·삭제·교체 목록을 해석하기도 어렵습니다. 실제로 존재하지 않는 항목을 state에서 정리한 뒤 계획을 다시 확인했습니다. state의 기록을 정리하는 작업과 실제 클라우드 자원을 삭제하는 작업은 분리해 처리했습니다.
이렇게 현재 운영에 필요한 자원만 새 계정에 만들기로 결정했습니다. 전체 이전은 아래 순서로 진행했습니다.
2. Terraform으로 인프라를 구성하고 변경 범위를 확인했습니다
10월 21일 새 계정에 네트워크, bastion, 관리형 PostgreSQL·Redis, NKS를 구성했습니다. NKS는 공통 운영 구성요소용 system 1대, 앱용 witim 2대, 실시간 서버용 centrifugo 2대로 시작했습니다. 총 5대이며, 노드풀 자동 확장도 설정했습니다.
노드풀을 나눌 때는 기존 환경에서 역할과 자원 이름이 맞지 않았던 부분을 정리했습니다. 기존에는 별도 서비스의 노드풀에 운영 도구가 함께 올라가 있었습니다. 새 환경에서는 ArgoCD와 로그·알림을 위한 공통 노드를 앱·실시간 서버의 노드풀과 분리했습니다. 어디에 무엇이 올라가는지 확인하기 쉽고, 이후 노드 수를 조정할 때 영향 범위를 예측할 수 있도록 구성한 것입니다.
Terraform과 ArgoCD의 역할도 구분했습니다. Terraform은 VPC·서브넷·bastion·관리형 DB·Redis·NKS처럼 계정에 속하는 기반 자원을 구성하는 데 사용했습니다. 클러스터 안에서 계속 변경되는 애플리케이션 매니페스트는 기존처럼 ArgoCD가 Git을 기준으로 동기화하도록 했습니다.
네트워크는 데이터 이전 방식까지 고려해 결정했습니다. 두 계정 사이에 VPC Peering을 추가하면 DB를 직접 복제하기는 편해집니다. 다만 이번에는 데이터가 작고, 이전이 끝나면 필요하지 않을 연결이었습니다. 따라서 계정 사이에 상시 접근 경로를 만들지 않고, 각 클러스터 내부의 임시 파드로 데이터를 옮기기로 했습니다.
Terraform 적용 중에는 NKS 노드풀의 taint 설정에서 API 오류가 발생했습니다. Terraform은 해당 리소스를 교체 대상으로 표시했지만, 정상 동작 중인 노드를 교체해야 하는지는 별도로 확인할 필요가 있었습니다. API 상태와 실제 리소스를 확인한 결과 재생성은 불필요했습니다. terraform untaint로 표시를 해제하고, plan에서 교체가 발생하지 않는지 다시 확인했습니다.
이 조치는 실제 상태를 확인한 뒤 선택했습니다. taint 오류에 일괄 적용할 수 있는 해결책은 아닙니다. 이번에는 terraform apply의 성공 여부와 함께 무엇을 새로 만들고, 어떤 자원은 교체하지 않아야 하는지 확인하는 데 Terraform을 활용했습니다.
3. 새 계정에서 배포가 이어지도록 경로를 연결했습니다
NKS와 DB를 준비한 다음에는 애플리케이션이 새 계정에서 배포되고 업데이트되는 경로를 연결했습니다. 각 단계는 다음과 같이 이어집니다.
GitHub Actions → 새 NCP Container Registry → Git 이미지 태그 갱신 → ArgoCD → 새 NKS먼저 기존 레지스트리의 이미지를 새 레지스트리로 옮겼습니다. 롤백에 쓸 이미지까지 포함해 다섯 개를 복사하고 해시를 대조했습니다. 전환 당일에 이미지를 새로 빌드하거나 옛 계정에서 가져오지 않도록 미리 준비한 것입니다.
이미지를 복사한 뒤에는 새 노드에서 실제로 pull할 수 있는지 확인했습니다. 계정을 옮기면 레지스트리 주소와 인증 키가 함께 바뀌므로, 이미지가 저장돼 있어도 노드에서 내려받지 못할 수 있기 때문입니다.
저장소 접근 권한도 확인해야 했습니다. 기존 ArgoCD는 퇴사자의 개인 GitHub 토큰을 사용하고 있었는데, 계정이 제거되면서 토큰이 무효화돼 배포와 롤백이 멈춰 있었습니다. 이때 곧바로 새 토큰을 넣으면 멈춰 있던 동기화가 재개되고, 그동안 쌓인 Git 변경이 한꺼번에 운영 환경에 반영될 수 있었습니다.
그래서 먼저 자동 동기화를 비활성화한 뒤, 개인 토큰을 조직 소유의 읽기 전용 GitHub App으로 교체했습니다. 앱의 저장소 접근과 동기화 상태를 확인한 다음 필요한 배포를 진행했습니다.
로그인 서비스에서는 재발급된 NCP 키가 이미지 pull Secret에 반영되지 않아 401 오류가 발생했습니다. Secret을 교체하고 다른 노드에서 이미지를 실제로 내려받는 것까지 확인했습니다. 이 과정에서 운영 파드의 재시작은 발생하지 않았습니다.
전환 후 운영 배포를 다시 실행했을 때는 GitHub의 main 브랜치 규칙이 이미지 태그 갱신 커밋을 막고 있었습니다. CI에서 이미지를 만드는 단계가 성공해도 Git에 새 태그를 쓰지 못하면 ArgoCD는 변경을 인식하지 못합니다. 이 때문에 빌드 완료 이후의 배포 흐름까지 확인해야 했습니다.
브랜치 보호 규칙은 유지하면서, 해당 저장소의 이미지 태그만 갱신할 수 있는 별도 GitHub App을 만들었습니다. 이 앱에만 예외를 허용한 뒤, 10월 23일 15시 32분 새 경로로 web과 admin 배포를 마쳤습니다.
배포에 앞서 운영 데이터 복사본으로 DB 마이그레이션도 재현했습니다. 그 과정에서 사전 적용해야 할 인덱스 11개가 빠져 있다는 것을 발견했습니다. 그대로 배포했다면 마이그레이션 사전 검사에서 멈췄을 상황이었습니다. 인덱스를 먼저 적용하고 유효성을 확인한 뒤, 마이그레이션 48개를 오류 없이 실행했습니다.
ArgoCD 동기화 때 web 파드가 일시적으로 1대까지 줄어드는 문제는 RespectIgnoreDifferences 설정을 수정해 해결했습니다. 새 서버에서 앱이 한 번 실행되는지 확인한 뒤에도, 다음 배포가 안전하게 이어지는지 별도로 검증해야 했습니다.
4. 전환 당일에는 데이터와 트래픽을 옮기는 데 집중했습니다
앱은 새 클러스터에 같은 이미지를 올려 준비할 수 있지만, DB는 두 계정에서 동시에 쓰기를 받아서는 안 됩니다. 메시지는 옛 DB에, 근태 기록은 새 DB에 남는 식으로 데이터가 나뉘는 상황을 피해야 했습니다.
PostgreSQL 논리 복제도 검토했습니다. 다만 덤프 크기가 약 46.5MB였고, 리허설에서 복원 시간이 짧게 나왔습니다. 이 규모에서는 복제 슬롯과 계정 간 네트워크를 추가로 관리하기보다 사전에 충분히 검증한 뒤 짧게 쓰기를 멈추는 방식이 적합하다고 판단했습니다. PostgreSQL도 17로 올리지 않고 기존과 같은 15.15로 유지해, 계정 이전과 DB 업그레이드를 함께 진행하지 않았습니다.
10월 22일 리허설에서는 운영 DB를 읽기 전용으로 덤프해 새 환경에 복원했습니다. 실제 데이터로 로그인·채팅·파일 기능을 확인했고, worker는 외부 통신을 차단한 상태에서 시험했습니다. 리허설에서 발견한 연결 설정 문제는 전환 전에 수정했습니다.
어느 시점까지 되돌릴 수 있는지도 미리 정했습니다. 새 환경에서 사용자 쓰기가 시작되기 전에는 기존 환경을 다시 켜서 복귀할 수 있습니다. 하지만 새 DB에 기록이 쌓인 뒤 주소만 옛 계정으로 되돌리면 새 기록이 빠질 수 있습니다. 그래서 데이터 대조와 새 앱 점검을 통과한 뒤 외부 진입점을 변경하도록 순서를 고정했습니다.
실제 전환은 다음 순서로 진행했습니다.
10월 22일 23:59 기존 환경 쓰기 중지10월 23일 00:01–00:05 PostgreSQL·Redis 최종 이전10월 23일 00:06 새 환경 앱 기동10월 23일 00:12 Cloudflare 진입점 5개 전환 완료| 확인 항목 | 결과 |
|---|---|
| PostgreSQL | 285개 테이블, 495,192행 일치 |
| Redis | 9,587개 키 일치 |
| 사용자 영향 | 약 13분 |
| 전환 후 1시간 웹 요청 | 5,518건 중 5xx 0건 |
| 같은 기간 worker 처리 | 1,926건 성공, 재시작 0회 |
전환 직후에는 웹뿐 아니라 데스크톱·모바일의 로그인 유지, 실시간 채팅, 파일, 푸시까지 확인했습니다. 로그인 세션을 유지하면서 데이터가 일치하는 것을 확인했지만, 이후에도 안정적으로 운영할 수 있는지 점검이 필요했습니다. 같은 날 운영 배포를 끝까지 실행하고, 모니터링 경보와 백업도 새 계정 기준으로 확인했습니다.
11월 2일에는 옛 계정의 로그를 보존하고 Loki 보관 기간을 30일에서 90일로 늘린 뒤, 옛 계정 리소스를 모두 정리했습니다. 이전 계정의 10월 인프라 사용액은 약 134만 원이었고, 정리 후에는 과금 자원이 남지 않도록 했습니다. 이 금액은 당시 옛 계정의 사용액이며, 그대로 절감한 순비용을 뜻하지는 않습니다.
마무리하며
이번 이전에서는 Terraform 코드를 작성하는 것 외에도 실제 운영 상태를 확인하는 데 시간이 필요했습니다. 문서와 다른 설정을 찾아내고, 유지할 자원과 정리할 자원을 고른 뒤, 배포 권한과 데이터 전환 순서를 다시 확인했습니다.
처음부터 기존 구성을 그대로 복제했다면 더 빨리 시작할 수는 있었을 것입니다. 하지만 필요 없는 자원과 이미 동작하지 않던 배포 경로까지 가져왔을 가능성이 있습니다. 사전 점검과 리허설을 거친 덕분에 전환 당일에는 데이터 복사와 트래픽 변경에 집중할 수 있었습니다.
새 계정에서 서비스를 실행하는 것에 더해, 이후의 배포·복구·관측까지 이어지는지를 성공 기준으로 삼았습니다. 계정 이전을 준비할 때 서버 목록과 함께 운영 경로도 확인해야 하는 이유입니다.
데이터 이전 방식을 비교하고 선택한 과정은 2편 · 무중단 대신 짧은 점검을 선택한 이유에서 설명합니다. 실제 데이터 복사와 운영 격리를 어떻게 검증했는지는 3편 · 운영 데이터로 리허설한 과정에서 이어서 살펴보겠습니다.
댓글