DigitalOcean에서 Hetzner로 — 월 $1,432를 $233으로 줄인 무중단 이전기

8 min read
HetznerDigitalOcean서버 마이그레이션인프라DevOps
DigitalOcean에서 Hetzner로 — 월 $1,432를 $233으로 줄인 무중단 이전기

왜 옮겼나

클라우드 비용이 점점 부담스러워지는 상황에서, 한 터키 소프트웨어 회사가 DigitalOcean을 Hetzner로 교체했다. 이유는 단순했다. "steady-state 워크로드에서 가격 대비 성능이 더 이상 합리적이지 않다."

결과부터 보면:

항목DigitalOceanHetzner
월 비용$1,432$233
연 절감액$14,388
CPU32코어96코어
RAM192GB256GB DDR5

비용은 83% 절감, 성능은 3배 이상 향상이다.

[💡 잠깐! 이 용어는?] Hetzner: 독일에 본사를 둔 유럽 호스팅 업체. 전용 서버(Dedicated Server)와 클라우드 VM 모두 제공하며, 특히 전용 서버의 가격 대비 성능이 월등하다고 알려져 있다.


이전 대상 스택

이 사례의 서버는 단순하지 않다. 동시에 다음을 운영하고 있었다:

  • Nginx — 웹 서버 / 리버스 프록시 (설정 파일 34개)
  • PHP — 애플리케이션 런타임
  • MySQL 8.0 — 248GB 데이터베이스
  • Neo4J — 그래프 데이터베이스
  • 웹 파일 — 65GB, 파일 150만 개

이걸 다운타임 없이 이전해야 한다.


6단계 무중단 이전

1단계: 새 서버 스택 구축

Hetzner에 동일 스택을 구성한다. 기존 서버와 동일한 버전의 Nginx, PHP, MySQL 8.0, Neo4J를 설치한다.

bash — Hetzner 서버 초기 세팅
# Ubuntu 22.04 기준
sudo apt update && sudo apt upgrade -y
sudo apt install nginx php8.3-fpm mysql-server -y
 
# MySQL 버전 확인 (기존 서버와 일치해야 함)
mysql --version
# mysql  Ver 8.0.xx

이 단계에서 실제 데이터는 아직 없다. 새 서버가 정상 작동하는 환경인지만 확인한다.


2단계: 웹 파일 복제 (rsync)

65GB, 150만 개 파일을 rsync로 이전한다. rsync는 변경된 파일만 재전송하므로, 초기 이전 후 계속 동기화 상태를 유지하는 데 적합하다.

bash — rsync로 웹 파일 이전
# 기존 서버에서 실행
rsync -avz --progress \
  /var/www/html/ \
  user@hetzner-ip:/var/www/html/
 
# 이전 후 검증: 파일 수 비교
find /var/www/html -type f | wc -l
# → 1,500,000 확인

이 과정은 시간이 오래 걸린다. 초기 전송 후에는 컷오버 직전에 한 번 더 rsync를 실행해 변경분만 동기화하면 된다.


3단계: MySQL 마스터-슬레이브 복제

248GB 데이터베이스는 가장 까다롭다. mydumper를 사용해 병렬 처리로 덤프하고, 슬레이브 복제를 설정해 실시간 동기화 상태를 유지한다.

bash — mydumper로 248GB 데이터 병렬 덤프
# mydumper 설치
sudo apt install mydumper -y
 
# 병렬 덤프 (스레드 8개)
mydumper \
  --host=localhost \
  --user=root \
  --password=your_password \
  --outputdir=/backup/mysql-dump \
  --threads=8 \
  --compress \
  --verbose=3
bash — 슬레이브 복제 설정
# 기존 서버 (마스터) — my.cnf 설정
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = your_database
 
# 새 서버 (슬레이브)에서 복제 시작
CHANGE MASTER TO
  MASTER_HOST='old-server-ip',
  MASTER_USER='replication_user',
  MASTER_PASSWORD='password',
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=154;
 
START SLAVE;
SHOW SLAVE STATUS\G
# → Slave_IO_Running: Yes
# → Slave_SQL_Running: Yes

슬레이브가 마스터를 따라잡으면 두 서버의 데이터가 실시간 동기화된다.


4단계: DNS TTL 축소

컷오버 전에 DNS TTL을 줄여 전환 속도를 높인다.

bash — DNS TTL 변경
# 3600초 → 300초
# 변경 후 기존 TTL(3600초)만큼 대기해야 전파 완료
 
# 변경 확인
dig your-domain.com +short
# → 응답 시간이 빠르면 TTL이 적용된 것

[💡 잠깐! 이 용어는?] TTL(Time To Live): DNS 레코드가 캐시에 얼마나 오래 유지되는지 결정하는 값(초 단위). TTL이 3600이면 DNS 변경 후 최대 1시간까지 이전 서버를 가리킬 수 있다. 300으로 줄이면 5분 이내에 새 서버로 전환된다.


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

DNS 전파 중 기존 서버로 오는 트래픽을 새 서버로 포워딩한다. Nginx 설정 34개를 일괄 변환한다.

bash — Nginx 설정 자동 변환 (리버스 프록시)
# 기존 서버의 모든 Nginx 설정을 리버스 프록시로 변환
for conf in /etc/nginx/sites-enabled/*; do
  domain=$(grep -oP 'server_name \K[^;]+' "$conf" | head -1)
  cat > "$conf" << EOF
server {
    listen 80;
    server_name $domain;
    location / {
        proxy_pass http://hetzner-ip;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    }
}
EOF
done
 
sudo nginx -t && sudo systemctl reload nginx

이 시점부터 기존 서버는 새 서버로 요청을 중계한다.


6단계: DNS 컷오버

모든 A 레코드를 Hetzner IP로 변경한다.

bash — DNS 레코드 변경 확인
# 변경 후 전파 확인
dig your-domain.com @8.8.8.8 +short
# → Hetzner IP가 나오면 성공
 
# 새 서버에서 트래픽 수신 확인
tail -f /var/log/nginx/access.log

TTL을 300초로 줄여뒀으므로 전파는 5분 내에 완료된다.


결과

지표결과
다운타임0분
데이터 손실0건
월 비용$1,432 → $233
CPU32코어 → 96코어
RAM192GB → 256GB DDR5
연간 절감$14,388

성능이 3배 올라갔는데 비용은 83% 줄었다. 트래픽이 꾸준한 steady-state 워크로드라면 전용 서버가 클라우드 VM보다 월등히 유리하다.


적합한 케이스

이 방식이 맞는 케이스:

  • 트래픽이 예측 가능하고 일정한 서비스
  • 클라우드 탄력성보다 원시 성능이 필요한 경우
  • 비용 압박이 있는 스타트업/중소 회사

주의할 케이스:

  • 급격한 트래픽 변동이 있는 서비스 (오토스케일링이 필요)
  • 미국/아시아 레이턴시가 중요한 경우 (Hetzner는 주로 유럽/미국 데이터센터)

마무리

rsync, mydumper, MySQL 슬레이브 복제, Nginx 리버스 프록시. 네 가지 도구를 조합하면 수백 GB 규모의 서비스도 다운타임 없이 이전할 수 있다. 비결은 컷오버 전에 두 서버가 동기화 상태를 유지하는 것이다. DNS TTL을 줄이고 기존 서버를 리버스 프록시로 두는 것이 그 안전망이다.


참고:

관심 있을 만한 포스트

Node.js 인기 레포 6개의 미문서 환경변수 — 스캔 결과 공개

Express, NestJS, Supabase 등 6개 Node.js 프로젝트를 스캔해 실제 env 사용량과 문서화 수준의 격차를 분석한다.

Node.js환경변수

불 끄기 전에 원인 조사하는 소방관은 없다 — 장애 복구를 결정짓는 First Action

우아한형제들이 70건의 장애 사례를 분석해 도출한 First Action 전략. 장애 복구 시간을 결정하는 건 원인 분석이 아니라 최초 조치다.

장애 대응SRE

AI 코딩 시대 — 성장이 멈추는 개발자의 뇌에서 일어나는 일

AI에 의존할수록 AI를 잘 쓰기 어려워진다는 역설을 신경과학과 인지심리학으로 분석한다.

AI 코딩개발자 성장

배민 다국어 서비스 — 5년 백로그를 LLM으로 19일에 끝낸 이야기

5년간 미루던 다국어 번역 파이프라인을 Claude Haiku에서 Amazon Nova로 전환하며 19일 만에 완성한 우아한형제들의 기술 스택과 구현 구조.

LLM다국어

Bun v1.3.12 — 브라우저 자동화와 인프로세스 Cron이 기본 내장됐다

Playwright 없이 브라우저를 제어하는 Bun.WebView, 그리고 서버 재시작 없이 작업을 스케줄링하는 Bun.cron의 기술적 구조.

Bun브라우저 자동화

CSS animation-timeline — 스크롤 오버플로우를 감지해 Border-Radius 동적 조절하기

CSS scroll() 타임라인으로 컨테이너 오버플로우를 감지하고 border-radius를 자동 조절하는 순수 CSS 트릭을 정리한다.

CSSanimation-timeline

12컬럼 그리드 없이 반응형 레이아웃 — CSS Grid와 Flexbox 비교

Bootstrap의 12컬럼 시스템을 CSS Grid와 Flexbox만으로 대체하는 현대적 레이아웃 패턴을 비교한다.

CSSGrid

CSS linear() Easing — 자바스크립트 없이 스프링 애니메이션 만들기

CSS linear() 함수로 바운스와 탄성을 가진 스프링 애니메이션을 순수 CSS만으로 구현하는 방법을 정리한다.

CSSanimation