호스팅 기기를 완전히 바꾸는 작업, 그것도 도메인이 걸린 실제 운영 중인 블로그를 옮기는 작업은 보통 다운타임을 전제로 한다. 이번엔 그걸 피할 수 있는 구조가 이미 손에 있었다.
blog.mhub.uk는 Cloudflare Tunnel로 외부에 노출되어 있었다. 터널이라는 게 결국 “이 도메인으로 오는 요청을, 이 토큰을 가진 어떤 커넥터든 받아서 로컬 포트로 넘겨준다"는 구조라는 걸 다시 떠올렸다 — 여기서 핵심은 “어떤 커넥터든"이라는 부분이다. 하나의 터널에 같은 토큰으로 여러 개의 cloudflared 프로세스를 동시에 띄우면, Cloudflare가 이걸 자동으로 고가용성(HA) 구성으로 인식해서 트래픽을 분산하거나 장애 시 다른 쪽으로 넘긴다.
이 성질을 그대로 이전 절차에 썼다. 원래 기기(quantum2)에서 실행 중이던 cloudflared tunnel run --token <토큰>을 그대로 둔 채로, 새 기기(gal9rooted)에서 정확히 같은 토큰으로 두 번째 커넥터를 하나 더 띄웠다. DNS 레코드도, Cloudflare 대시보드의 라우팅 설정도 전혀 건드리지 않았다 — 둘 다 같은 터널 ID를 가리키고 있으니 애초에 바꿀 이유가 없었다.
새 커넥터가 정상적으로 연결 등록(Registered tunnel connection 로그)됐는지 확인한 뒤, 실제로 blog.mhub.uk가 정상 응답하는지, /blog-api/api/posts로 글 작성 API가 작동하는지까지 라이브로 검증했다. 이 시점부터는 두 기기가 동시에 같은 도메인의 트래픽을 나눠 받고 있는 상태다 — 사용자 입장에서는 아무 차이도 느껴지지 않는다.
여기서 중요했던 건 순서였다. 새 기기 쪽이 완전히 검증되기 전까지는 기존 기기의 서비스(Caddy, 관리자 API 백엔드, cloudflared)를 먼저 끄지 않았다. 두 백엔드가 동시에 떠 있는 상태에서 글쓰기 요청이 어느 쪽 파일시스템에 저장될지 예측할 수 없어지는 위험이 있었기 때문이다. 검증이 끝난 뒤에야 기존 기기의 cloudflared 커넥터를 종료했고, 그 직후 blog.mhub.uk가 새 기기 단독으로도 정상 응답하는지 다시 확인했다. 이 순서를 지키면, 전환의 순간에 “지금부터 새 서버"라고 스위치를 누르는 순간 자체가 없다 — 이미 둘 다 받고 있던 트래픽에서 한쪽이 조용히 빠질 뿐이다.
기능(관리자 API, Hugo 빌드)까지 실제로 옮기는 과정에서 예상 밖의 기술 부채도 하나 드러났다. 새 기기의 Hugo 버전이 더 최신이라 paginate 설정 키가 완전히 제거되어 있었고, [pagination] pagerSize = 10 형식으로 바꿔야 빌드가 통과했다. 오래된 설정 문법이 구버전 Hugo에서만 조용히 버텨주고 있었던 셈이다.
이렇게 GitHub 저장소 하나(콘텐츠와 관리자 백엔드 소스 둘 다)를 공통의 진실 원천으로 삼고, 같은 터널 토큰을 공유하는 방식으로 옮기고 나니, “서버를 옮긴다"는 게 생각보다 덜 무서운 일이 됐다. 다만 이 방식이 모든 이전에 그대로 통하지는 않았다 — 다음 편에서 다룰 phoneserver 이전은 훨씬 더 꼬였다.


💬 댓글