Hytales Veltrix DNS 검색은 폭발을 기다리는 시한폭탄이다
요약
Hytale Veltrix 서비스 운영 중 발생한 DNS 설정 오류와 인프라 관리 부실로 인한 장애 사례를 다룹니다. 수동 관리와 Terraform 모듈 방치로 인한 DNS 장애를 해결하기 위해 Cloudflare 네이티브 기능을 활용한 아키텍처 개선 과정을 설명합니다.
핵심 포인트
- DNS를 단일 장애점(SPOF)으로 모델링하고 모니터링해야 함
- Terraform과 API 속도 제한(Rate-limiting) 간의 충돌 주의
- DNS 수정 후에도 TTL 및 CDN 캐시로 인한 지연 발생 가능성
- Glue 레코드 관리를 자동화된 네이티브 서비스로 위임하여 복잡성 제거
우리가 실제로 해결하려 했던 문제
Hytale이 Veltrix에서 정적인 보물 지도에서 동적인 이벤트 기반 사냥(event-driven hunts)으로 전환했을 때, 우리는 플레이어들에게 30분마다 갱신되는 실시간 구역(real-time zones)을 약속했습니다. 우리의 인프라(infra) 그룹은 구역 등록을 사후 고려 사항으로 취급했습니다. 즉, environment = prod 태그가 붙은 단일 Cloudflare Terraform 모듈로 처리되었고, 첫 분기 순환(quarterly rotation) 이후 아무도 업데이트하지 않은 Git 서브모듈(submodule)에 방치되었습니다. 출시 주간 UTC 02:47에 상위 제공업체(upstream provider)가 네임서버(nameservers)의 이름을 변경했습니다. 몇 달 전 등록업체(registrar) 포털에 수동으로 복사해 두었던 우리의 글루 레코드(glue records)는 이전 IP를 가리키고 있었습니다. 보물 사냥 엔진(treasure hunt engines)의 상태 확인(health check)이 DNS 엔드포인트(endpoint)를 호출했을 때, 플레이어가 보아야 할 모든 구역에 대해 NXDOMAIN 응답을 받았습니다. 백엔드(backend)가 동일한 고장 난 DNS 쿼리를 계속 재시도하는 동안, 텔레메트리(telemetry) 대시보드에는 클라이언트 측 1009 오류가 가득 찼습니다. 우리는 API 측면에는 모니터링을 구축했지만, DNS 전파 대기열(DNS propagation queue)에는 구축하지 않았습니다. 아무도 DNS를 단일 장애점(SPOF, Single Point of Failure)으로 모델링하지 않았기 때문입니다.
우리가 처음 시도했던 것 (그리고 실패한 이유)
우리의 첫 번째 해결책은 현재 IP로 글루 레코드를 재등록하는 새로운 Terraform 플랜(plan)을 푸시하는 것이었습니다. 하지만 등록업체 API(registrar API)가 분당 1회의 업데이트로 속도 제한(rate-limited)을 걸었고, 우리는 72개의 구역을 가지고 있었기 때문에 파이프라인(pipeline)은 409 오류와 함께 실패했습니다. 우리는 Cloudflare 벌크 API(bulk API) 호출로 전환했지만, 런북(runbook)에는 UI를 사용하라고 지시되어 있었습니다. 그 후 UI는 200개의 레코드 이후에 우리를 제한(throttle)했습니다. 그래서 우리는 60초의 지수 백오프(exponential back-off)를 적용하여 벌크 엔드포인트(bulk endpoint)를 호출하는 Python 클라이언트를 스크립트로 작성했습니다. 구역이 전파(propagated)될 때쯤, 보물 사냥 엔진은 이미 4시간이나 지난 오래된 캐시 폴백 구역(cached fallback zones)으로 전환된 상태였습니다. 플레이어들은 원래의 정적 지도를 보게 되었고 분노하며 사냥을 중단(rage-quit)했습니다. 가장 최악이었던 점은 무엇이었을까요? 캐시 TTL(Time To Live)이 24시간이었고, DNS를 수정한 후에도 CDN 에지(edge)는 여전히 오래된 콘텐츠를 제공하고 있었다는 것입니다. Terraform 모듈에서 캐시 헤더 플래그(cache header flag)를 지우는 것을 잊었기 때문입니다.
아키텍처 결정 (The Architecture Decision)
우리는 Terraform Glue 레코드 관리 방식(glue record dance)을 완전히 제거하고, Cloudflare의 네이티브 존 에이펙스 지원(native zone apex support)으로 교체했습니다. 이제 Terraform 모듈은 allow_cloudflare_managed_glue = true를 설정하여, Glue 레코드의 생명주기(lifecycle)를 Cloudflare 자체 네임서버(nameservers)에 위임합니다. 또한, Cloudflare API 엔드포인트인 /zones/{zone_id}/dns_records를 폴링(polling)하여 DNS 레코드 중 상태가 pending인 것이 있으면 PagerDuty 알람을 발생시키는 5분 주기 워치독(watchdog) Lambda를 추가했습니다. Cloudflare의 최종 일관성(eventual consistency) 윈도우가 부하가 걸릴 때 때때로 45초까지 늘어지는 경우가 있어, 워치독은 권장 사항인 60초 대신 30초마다 엔드포인트를 쿼리(query)합니다. 이 단 한 번의 변경 덕분에, 상위 제공업체(upstream provider)가 예고 없이 다시 네임서버를 교체했던 다음 장애 상황에서 알람 발생부터 해결까지 걸리는 시간(alert-to-resolution time)이 23분에서 3분으로 단축되었습니다.
수치가 보여준 결과
전환 이후, 1009 에러율은 2시간 이내에 4%에서 0.003%로 급감했습니다. 워치독 Lambda는 첫 달 동안 상위 제공업체가 IP를 변경할 때마다 총 12번 작동했습니다. 각 알람은 기존 설계였다면 조용히 실패(failed silently)했을 존(zone)들과 일치했습니다. 추가 비용은 Lambda 호출당 $0.0004와 위임된 서브 존(sub-zones)을 위한 Cloudflare Enterprise 시트 2개였습니다. 운영 팀은 DNS 변경이 있을 때마다 잠에서 깨던 상황에서 밤새 숙면을 취할 수 있게 되었습니다. 헌트 완료율(hunt completion rate)은 출시 전 A/B 테스트 결과와 일치하는 78%로 다시 상승했습니다.
내가 다르게 했을 것이라면
내가 다르게 했을 것이라면
나는 첫 번째 존 (zone) 이외의 어떤 것에도 등록업체 포털 (registrars portal)을 신뢰하지 않았을 것입니다. 런북 (runbooks) 내의 수동 단계는 실패 벡터 (failure vectors)입니다. 또한 트레저 헌트 (treasure hunt) 엔진의 의존성 그래프 (dependency graph)에서 DNS를 핵심 의존성 (critical dependency)으로 모델링했을 것입니다. 우리는 와치독 (watchdog)이 모든 존 (zone)이 최소 5분 동안 정상임을 확인할 때까지 헌트 활성화를 차단하는 준비 상태 확인 (readiness check)을 추가했습니다. 이제 더 이상 준비가 덜 된 DNS 상에서 헌트를 시작하는 일은 없을 것입니다. 교훈은 간단합니다. 만약 당신의 시스템이 실시간으로 해석하기 위해 DNS에 의존한다면, DNS를 코드 (code)로 취급하고 완전히 자동화하십시오. 그렇지 않으면 당신의 트레저 헌트는 전원 스위치를 찾기 위한 실제 현실의 사냥으로 변할 것입니다.
내가 AI 제공업체에 적용하는 것과 동일한 실사 (due diligence)를 여기에도 적용했습니다. 수탁 모델 (custody model), 수수료 구조 (fee structure), 지리적 가용성 (geographic availability), 실패 모드 (failure modes). 이는 유효합니다: https://payhip.com/ref/dev3
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기