DigitalOcean에서 Hetzner로 — 월 $1,432를 $233으로 줄인 무중단 이전기
왜 옮겼나
클라우드 비용이 점점 부담스러워지는 상황에서, 한 터키 소프트웨어 회사가 DigitalOcean을 Hetzner로 교체했다. 이유는 단순했다. "steady-state 워크로드에서 가격 대비 성능이 더 이상 합리적이지 않다."
결과부터 보면:
| 항목 | DigitalOcean | Hetzner |
|---|---|---|
| 월 비용 | $1,432 | $233 |
| 연 절감액 | — | $14,388 |
| CPU | 32코어 | 96코어 |
| RAM | 192GB | 256GB 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를 설치한다.
# 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는 변경된 파일만 재전송하므로, 초기 이전 후 계속 동기화 상태를 유지하는 데 적합하다.
# 기존 서버에서 실행
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를 사용해 병렬 처리로 덤프하고, 슬레이브 복제를 설정해 실시간 동기화 상태를 유지한다.
# mydumper 설치
sudo apt install mydumper -y
# 병렬 덤프 (스레드 8개)
mydumper \
--host=localhost \
--user=root \
--password=your_password \
--outputdir=/backup/mysql-dump \
--threads=8 \
--compress \
--verbose=3# 기존 서버 (마스터) — 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을 줄여 전환 속도를 높인다.
# 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개를 일괄 변환한다.
# 기존 서버의 모든 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로 변경한다.
# 변경 후 전파 확인
dig your-domain.com @8.8.8.8 +short
# → Hetzner IP가 나오면 성공
# 새 서버에서 트래픽 수신 확인
tail -f /var/log/nginx/access.logTTL을 300초로 줄여뒀으므로 전파는 5분 내에 완료된다.
결과
| 지표 | 결과 |
|---|---|
| 다운타임 | 0분 |
| 데이터 손실 | 0건 |
| 월 비용 | $1,432 → $233 |
| CPU | 32코어 → 96코어 |
| RAM | 192GB → 256GB DDR5 |
| 연간 절감 | $14,388 |
성능이 3배 올라갔는데 비용은 83% 줄었다. 트래픽이 꾸준한 steady-state 워크로드라면 전용 서버가 클라우드 VM보다 월등히 유리하다.
적합한 케이스
이 방식이 맞는 케이스:
- 트래픽이 예측 가능하고 일정한 서비스
- 클라우드 탄력성보다 원시 성능이 필요한 경우
- 비용 압박이 있는 스타트업/중소 회사
주의할 케이스:
- 급격한 트래픽 변동이 있는 서비스 (오토스케일링이 필요)
- 미국/아시아 레이턴시가 중요한 경우 (Hetzner는 주로 유럽/미국 데이터센터)
마무리
rsync, mydumper, MySQL 슬레이브 복제, Nginx 리버스 프록시. 네 가지 도구를 조합하면 수백 GB 규모의 서비스도 다운타임 없이 이전할 수 있다. 비결은 컷오버 전에 두 서버가 동기화 상태를 유지하는 것이다. DNS TTL을 줄이고 기존 서버를 리버스 프록시로 두는 것이 그 안전망이다.
참고:
- 원본 마이그레이션 기록: https://isayeter.com/posts/digitalocean-to-hetzner-migration/
- mydumper: https://github.com/mydumper/mydumper
- Hetzner 서버 목록: https://www.hetzner.com/dedicated-rootserver
관심 있을 만한 포스트
Node.js 인기 레포 6개의 미문서 환경변수 — 스캔 결과 공개
Express, NestJS, Supabase 등 6개 Node.js 프로젝트를 스캔해 실제 env 사용량과 문서화 수준의 격차를 분석한다.
불 끄기 전에 원인 조사하는 소방관은 없다 — 장애 복구를 결정짓는 First Action
우아한형제들이 70건의 장애 사례를 분석해 도출한 First Action 전략. 장애 복구 시간을 결정하는 건 원인 분석이 아니라 최초 조치다.
AI 코딩 시대 — 성장이 멈추는 개발자의 뇌에서 일어나는 일
AI에 의존할수록 AI를 잘 쓰기 어려워진다는 역설을 신경과학과 인지심리학으로 분석한다.
배민 다국어 서비스 — 5년 백로그를 LLM으로 19일에 끝낸 이야기
5년간 미루던 다국어 번역 파이프라인을 Claude Haiku에서 Amazon Nova로 전환하며 19일 만에 완성한 우아한형제들의 기술 스택과 구현 구조.
Bun v1.3.12 — 브라우저 자동화와 인프로세스 Cron이 기본 내장됐다
Playwright 없이 브라우저를 제어하는 Bun.WebView, 그리고 서버 재시작 없이 작업을 스케줄링하는 Bun.cron의 기술적 구조.
CSS animation-timeline — 스크롤 오버플로우를 감지해 Border-Radius 동적 조절하기
CSS scroll() 타임라인으로 컨테이너 오버플로우를 감지하고 border-radius를 자동 조절하는 순수 CSS 트릭을 정리한다.
12컬럼 그리드 없이 반응형 레이아웃 — CSS Grid와 Flexbox 비교
Bootstrap의 12컬럼 시스템을 CSS Grid와 Flexbox만으로 대체하는 현대적 레이아웃 패턴을 비교한다.
CSS linear() Easing — 자바스크립트 없이 스프링 애니메이션 만들기
CSS linear() 함수로 바운스와 탄성을 가진 스프링 애니메이션을 순수 CSS만으로 구현하는 방법을 정리한다.