삼태연구소
SAMTAELABS삼태연구소
가이드2026년 4월 19일·8분 읽기

월 130만원 절감한 프로덕션 서버 무중단 이전: DigitalOcean → Hetzner 실전 가이드 (isayeter.com)

서버마이그레이션HetznerDigitalOceanMySQL마이그레이션무중단배포전용서버mydumper인프라비용절감프로덕션이전Nginx
월 130만원 절감한 프로덕션 서버 무중단 이전: DigitalOcean → Hetzner 실전 가이드
목차(4)

한줄 요약

월 $1,432 DigitalOcean 서버를 $233 Hetzner 전용 서버로 무중단 이전해 연간 $14,388을 절감한 실전 마이그레이션 사례다.

어떤 상황에서 필요한가?

클라우드 인프라 비용이 점점 감당하기 어려워지는 시점이 온다. 트래픽이 예측 가능하고, 오토스케일링이나 매니지드 서비스를 적극적으로 활용하지 않는 steady-state 워크로드라면, 퍼블릭 클라우드보다 전용 서버(dedicated server)가 훨씬 합리적인 선택일 수 있다.

이 사례의 출발점도 그랬다. DigitalOcean에서 192GB RAM, 32 vCPU, 600GB SSD 구성으로 월 $1,432를 지불하고 있었다. Hetzner의 AX162-R은 AMD EPYC 9454P(48코어/96스레드), 256GB DDR5, 1.92TB NVMe Gen4 RAID1 구성을 월 $233에 제공한다. 스펙은 모든 항목에서 우월하고 가격은 6분의 1 수준이다.

운영 중인 스택도 단순하지 않았다. MySQL 데이터베이스 30개(총 248GB), Nginx 가상 호스트 34개, GitLab EE, Neo4j 그래프 DB, Supervisor로 관리되는 다수의 백그라운드 워커, Gearman 잡 큐, 그리고 수십만 명이 사용하는 라이브 모바일 앱까지 포함된 스택이었다. 여기에 CentOS 7(EOL)에서 AlmaLinux 9.7로의 OS 전환까지 동시에 수행했다.

이 조건에서 무중단 이전을 설계했다는 점이 이 사례를 주목할 만하게 만든다.

핵심 구현 방법

전략의 핵심은 "어느 순간에도 트래픽이 끊기지 않는 구조를 만든 뒤 전환한다"는 원칙이다. 6단계로 설계됐다.

1단계: 새 서버에 풀 스택 사전 구성

DNS를 건드리기 전에 Nginx, PHP, MySQL 8.0, Neo4j, GitLab EE, Node.js, Supervisor, Gearman을 모두 설치하고 기존 서버와 동일하게 설정했다. Let's Encrypt 인증서는 /etc/letsencrypt/ 디렉토리 전체를 rsync로 복사했고, 전환 완료 후 certbot renew --force-renewal로 일괄 갱신했다.

2단계: 웹 파일 rsync 복제

약 65GB, 150만 개 파일을 --checksum 플래그와 함께 rsync로 복제했다. 전환 직전에 증분 동기화를 한 번 더 실행해 초기 복제 이후 변경된 파일을 반영했다.

3단계: MySQL 마스터-슬레이브 리플리케이션

248GB 데이터를 오프라인 dump/restore로 처리하면 수일이 걸린다. 대신 mydumper를 48코어 병렬 처리로 활용해 덤프와 로드를 수 시간 내에 완료했다. 덤프 메타데이터에 기록된 binlog 포지션을 시작점으로 리플리케이션을 설정해 두 서버의 데이터를 실시간으로 동기화했다.

여기서 발생한 실제 문제가 있었다. 덤프가 두 번에 나눠 진행되는 사이 일부 테이블에 쓰기가 발생해 1062(Duplicate Key) 에러로 리플리케이션이 멈췄다. 해결책은 SET GLOBAL slave_exec_mode = 'IDEMPOTENT'였다. 중복 키와 누락 행 에러를 자동으로 건너뛰어 리플리케이션이 정상 추적 상태에 도달했다.

또 하나의 함정은 SUPER 권한이었다. 애플리케이션 DB 사용자 24명 전원에게 SUPER 권한이 부여돼 있어서 read_only = 1이 무력화됐다. 전환 전에 모든 사용자에서 SUPER를 회수해야 슬레이브 보호가 제대로 작동한다.

4단계: DNS TTL 사전 축소

DigitalOcean DNS API로 A, AAAA 레코드의 TTL을 3600초에서 300초로 낮췄다. MX, TXT 레코드는 건드리지 않았다. 메일 레코드 TTL 변경은 전달성 문제를 유발할 수 있기 때문이다. 기존 TTL이 전 세계에서 만료되도록 1시간 대기한 뒤 다음 단계로 넘어갔다.

5단계: 기존 서버 Nginx를 리버스 프록시로 전환

34개 Nginx 설정 파일을 손으로 수정하는 대신, Python 스크립트로 모든 server {} 블록을 파싱해 새 서버로 프록시하는 설정으로 자동 교체했다. DNS 전파가 완료되기 전까지 구 IP로 들어오는 요청도 새 서버로 투명하게 포워딩됐다.

6단계: DNS 전환 및 순서 제어

전환 순서가 중요하다. 새 서버에서 슬레이브를 중단하고 read_only를 해제한 뒤, 구 서버의 Supervisor를 멈추고, Python 스크립트로 모든 A 레코드를 새 서버 IP로 일괄 변경했다. DNS 반영까지 약 5분, 전체 전환은 10초 내에 완료됐다.

전환 후 추가 작업도 있었다. GitLab 프로젝트 웹훅이 여전히 구 서버 IP를 가리키고 있어서, GitLab API를 통해 전체 프로젝트를 스캔하고 일괄 업데이트하는 스크립트를 별도로 작성했다.

실전에서 주의할 점

MySQL 5.7 → 8.0 업그레이드는 예상치 못한 문제를 낸다. 이 사례에서는 import 후 mysql.user 테이블의 컬럼 구조가 맞지 않아 mysql.infoschema가 누락됐고 사용자 인증이 깨졌다. mysqld --upgrade=FORCE를 시도했지만 sys 스키마가 뷰가 아닌 일반 테이블로 임포트된 탓에 첫 시도가 실패했다. DROP DATABASE sys 후 재실행으로 해결했다. 마이그레이션 전 mysqlcheck --check-upgrade로 호환성을 반드시 검증해야 한다.

로컬 /etc/hosts를 이용한 사전 검증은 필수다. 로컬 머신의 hosts 파일에 도메인들을 새 서버 IP로 임시 매핑해두면, 실제 사용자 트래픽에 영향을 주지 않고 모든 API 엔드포인트와 관리자 패널을 새 서버 기준으로 검증할 수 있다. DNS 전환 전 이 검증 단계를 건너뛰는 것은 위험하다.

전용 서버는 퍼블릭 클라우드의 대체재가 아닌 보완재다. 오토스케일링, 로드 밸런서, 매니지드 DB 같은 클라우드 생태계 기능을 적극적으로 쓰는 팀이라면 단순 가격 비교만으로 전환을 결정하면 안 된다. 반면 워크로드가 예측 가능하고 단일 대형 서버로 충분한 구조라면, 전용 서버 비용은 퍼블릭 클라우드 대비 압도적으로 낮다.

전체 마이그레이션 소요 시간은 약 24시간이었고, 사용자에게 미친 다운타임은 0분이었다.

자주 묻는 질문

Q.mydumper를 써야 하는 이유가 있나? mysqldump도 되지 않나?

mysqldump는 단일 스레드로 동작한다. 수백 GB 규모의 데이터베이스를 mysqldump로 처리하면 내보내기와 불러오기 모두 수일이 걸릴 수 있다. mydumper와 myloader는 멀티스레드 병렬 처리를 지원해, 코어가 많은 서버에서 같은 작업을 수 시간 내에 완료한다. 대규모 MySQL 마이그레이션에서 mydumper 사용은 선택이 아니라 필수에 가깝다.

Q.DNS TTL을 낮추기 전 1시간을 기다리는 이유는?

TTL 값을 낮춰도 기존 TTL이 만료되기 전까지 전 세계 DNS 리졸버는 이전 값을 캐싱하고 있다. 기존 TTL이 3600초(1시간)였다면, TTL 변경 후 최소 1시간은 기다려야 대부분의 리졸버가 새 TTL(300초)로 갱신된다. 이 대기 없이 DNS를 전환하면 일부 사용자가 최대 1시간 동안 구 서버로 접근할 수 있다.

Q.Hetzner 전용 서버는 퍼블릭 클라우드와 비교해 어떤 단점이 있나?

가장 큰 차이는 관리 부담과 유연성이다. 하드웨어 장애 시 Hetzner가 교체해주지만, 대응 시간이 퍼블릭 클라우드 SLA와 다를 수 있다. 오토스케일링이 없어서 트래픽 급증에 즉시 대응하기 어렵고, 스냅샷이나 클론 같은 클라우드 편의 기능도 직접 구성해야 한다. 또한 서버 1대에 모든 서비스를 올리면 단일 장애점이 생기므로 별도 장애 대응 계획이 필요하다.

직접 구축이 어렵다면, 전문가에게 맡겨보세요

대표 개발자가 직접 소통하고, 설계하고, 구축합니다. 중간 과정 없이 의도 그대로.

관련 아티클