IL-0085 Magic Pages · 2025-04-14kubernetes
Magic Pages 는 Ghost 사이트를 호스팅하는 서비스이고, Hetzner Cloud 위의 쿠버네티스 클러스터에서 돈다. 이중화를 위해 컨트롤 플레인 노드를 세 대 둔다. 2025-04-14 09:33 UTC 쯤 그중 한 대가 메모리 부족으로 꺼졌다. 운영자는 hetzner-k3s 도구로 더 큰 서버를 띄워 교체하려 했지만 배포가 두 번 연속 실패했다.
그사이 클러스터가 Hetzner API 의 시간당 3600건 한도를 다 써 버렸다. 운영자는 이 한도를 알고 있었지만, 클러스터가 네트워킹과 일부 스토리지 관리 같은 평상시 작업에 Hetzner API 를 얼마나 쓰는지는 제대로 몰랐다. 운영자는 컨트롤 플레인에 문제가 생기자 내부 구성 요소들이 스스로 바로잡으려고 API 호출을 늘렸을 것으로 추정했다. 한도가 바닥나자 새 노드를 띄울 수 없었고 기존 클러스터 구성 요소도 제대로 돌지 못해 장애가 연쇄로 번졌다. 남은 노드들이 API 를 계속 호출해서 한도는 기다려도 회복되지 않았다. 2025-04-16 에 Hetzner 는 쿠버네티스 클러스터와 Hetzner 클라우드 사이를 잇는 클라우드 컨트롤러에 버그가 있다고 알려 왔다. Hetzner 도 이 버그의 근본 원인은 몇 달째 찾지 못했지만, 같은 일이 생겼을 때 훨씬 빨리 복구할 수 있는 우회 방법을 알려 주었다.
운영자는 API 호출을 확실히 멈추려고 서버 전원을 내려 클러스터 전체를 껐다. 그러자 ratelimit-remaining 이 다시 오르기 시작했고, 여유분이 쌓일 때까지 기다린 뒤 컨트롤 플레인 서버를 다시 켰다. 워커 노드를 다시 올리자 모든 웹사이트 컨테이너가 한꺼번에 시작하려 했지만, 사이트가 뜨려면 스토리지 볼륨이 먼저 준비돼야 했다. Longhorn 은 보통 스스로 복구되지만, 클러스터 전체가 비정상 종료된 뒤에는 자기 구성 요소가 최소 몇 개는 돌고 있어야 한다. 그래서 핵심 구성 요소를 손으로 띄웠다. 그 뒤 볼륨이 차례로 붙으면서 한 시간쯤에 걸쳐 사이트가 살아났고, 17:00 UTC 쯤 사이트의 95% 가 돌아왔다. 남은 몇 개는 손으로 다시 시작했다.
장애는 약 7.5시간 이어졌다. 데이터베이스는 그해 초에 쿠버네티스 클러스터 밖으로 옮겨 둔 덕분에 영향이 없었고 데이터 손실도 없었다. CDN 을 켠 사이트는 캐시해 둔 옛 페이지를 계속 내보냈다.
2025-04-14 09:33 UTC 쯤 Magic Pages 의 컨트롤 플레인 노드 세 대 중 한 대가 갑자기 오프라인이 됐다. 노드를 살펴보니 메모리가 바닥나 있었다. 운영자는 평소 절차대로 고장 난 노드를 빼고 메모리가 조금 더 큰 서버로 바꾸려 했다.
hetzner-k3s 도구로 시작한 교체 배포가 실패했다. API 요청은 가끔 일시적으로 실패하기 때문에 다시 실행했지만 또 실패했다. hetzner-k3s 는 Hetzner API 호출이 왜 실패하는지 자세한 로그를 보여 주지 않았다.
비슷한 시각부터 모니터링 경보가 쏟아졌고 고객 웹사이트에 접속할 수 없게 됐다. 30분쯤 조사한 끝에 원인을 찾았다. Hetzner Cloud 가 프로젝트마다 시간당 3600건으로 묶어 둔 API 호출 한도에 걸려 있었다.
처음에는 한도가 풀리기를 기다렸다. 시간당 한도이므로 30분이면 약 1800건이 돌아와야 했지만, 30분 뒤에도 API 상태는 ratelimit-remaining: 0 이었다. 15분에서 20분을 더 보고서야 이유를 알았다. 컨트롤 플레인이 망가져 API 를 쓸 수 없는 상태에서도 남은 노드들이 계속 API 를 호출했고, 그래서 한도가 회복되지 않았다. 별도 클러스터를 띄워 트래픽을 옮기는 방법도 API 호출이 필요해서 쓸 수 없었다.
원인 ? 용량 한계
모니터링 경보가 쏟아지면서 고객 웹사이트에 접속할 수 없다는 것을 알았다. API 호출 한도가 원인이라는 것은 30분쯤 조사한 뒤에야 찾았다. hetzner-k3s 가 Hetzner API 오류를 감춰서 원인 파악이 늦어졌다.
Hetzner 의 기본 API 한도인 시간당 3600건은 장애나 복구 상황에서 클러스터가 쓰는 양에 비해 부족했다. 운영자는 배포에 쓰는 API 사용량만 봤고, 실행 중인 클러스터 내부에서 나가는 호출은 지켜보지 않았다.
네트워킹 같은 핵심 기능이 클라우드 API 하나에 기대고 있으면, 그 API 가 막히는 순간 클러스터 전체가 약해진다. hetzner-k3s 의 추상화는 API 오류를 감춰 문제 해결을 늦췄다. 클러스터가 비정상 종료된 뒤 Longhorn 을 살리는 데 손이 많이 가서 장애가 길어졌다.
같은 원인 · 공통 영구 볼륨과 스테이트풀셋