AI Briefing

DigitalOcean에서 Hetzner로 마이그레이션하기

·2026.04.19 09:49

핵심 내용

30개 MySQL과 34개 Nginx 서비스를 0분 다운타임으로 Hetzner로 이전

자세히 보기

월 $1,432 규모의 DigitalOcean 프로덕션 인프라를 $233짜리 Hetzner 전용 서버로 옮기며, 운영체제도 CentOS 7 → AlmaLinux 9.7로 바꿨다. 결과적으로 월 $1,199, 연 $14,388를 절감했고, 0분 다운타임으로 컷오버를 끝냈다.

구성은 단순한 테스트 환경이 아니라 실제 프로덕션이었다. MySQL 30개(248GB), Nginx 가상 호스트 34개, GitLab EE, Neo4J, Supervisor, Gearman이 함께 돌아갔고, 수십만 사용자를 받는 모바일 앱을 운영 중이었다.

마이그레이션은 6단계로 진행했다.

  • 새 서버에 동일한 스택을 먼저 설치하고, Nginx/PHP/MySQL 8.0/Neo4J/GitLab/Node.js/Supervisor/Gearman을 기존과 맞췄다.
  • /var/www/html 약 65GB, 150만 파일을 rsync로 복제했다.
  • MySQL은 mysqldump 대신 mydumper + myloader를 사용해 병렬로 옮기고, replication으로 실시간 동기화를 유지했다.
  • DNS는 A/AAAA TTL을 3600초 → 300초로 줄여 전파 시간을 짧게 만들었다.
  • 기존 서버의 Nginx를 새 서버로 프록시하도록 바꿔, DNS 전파 중에도 유입 트래픽을 새 서버로 넘겼다.
  • 최종적으로 모든 A 레코드를 새 IP로 바꾸고 기존 서버는 cold standby로 1주일 유지한 뒤 종료했다.

가장 까다로운 부분은 MySQL이었다. mydumper의 --threads 32, --compress, --trx-consistency-only를 써서 빠르게 덤프했고, 메타데이터에 기록된 **binlog 위치(mysql-bin.000004:21834307)**부터 replication을 시작했다. 이후 mysql.user 스키마 불일치와 sys 스키마 문제로 업그레이드가 한 번 실패했지만, DROP DATABASE sys; 후 재실행해 해결했다.

replication 중에는 duplicate key(1062) 오류가 났고, 이는 덤프와 binlog 재생이 겹친 탓이었다. SET GLOBAL slave_exec_mode = 'IDEMPOTENT';로 넘어가며 동기화를 계속했고, 몇 분 만에 Seconds_Behind_Master가 0이 됐다.

또 다른 함정은 SUPER 권한이었다. 새 서버를 read_only = 1로 둬도 애플리케이션 계정들이 SUPER 권한 때문에 쓰기를 우회할 수 있었다. 24개 애플리케이션 사용자에서 SUPER를 제거한 뒤에야 읽기 전용이 정상 동작했다.

컷오버 직전에는 /etc/hosts로 로컬에서 새 서버를 먼저 검증했고, GitLab 웹훅도 사후에 일괄 갱신했다. 마이그레이션에 사용한 Python 스크립트들은 GitHub에 공개했고, TTL 조회, DNS 일괄 변경, Nginx 프록시 변환, MySQL 비교, GitLab 웹훅 수정까지 모두 자동화했다.

핵심 교훈은 세 가지다.

  • MySQL replication은 무중단 이전의 중심축이다.
  • 대용량 이전은 mydumper + myloader가 mysqldump보다 훨씬 효율적이다.
  • DNS, Nginx, 웹훅 같은 작업은 반드시 스크립트화해야 한다.

저렴한 전용 서버로 옮겨도, 사전 준비와 자동화가 충분하면 서비스 연속성을 유지하면서 비용과 성능을 동시에 개선할 수 있다.

이 한국어 요약은 AI가 자동으로 만들었습니다. 원문의 주장과 맥락은 원문에서 확인해 주세요. 저작권은 원저작자에게 있습니다.

AI 처리 방식을 확인하거나, 요약 오류와 출처 표기 문제, 삭제 요청을 문의 · 건의로 알려주세요.