로드 밸런싱과 고가용성 (2) - DNS 기반 로드 밸런싱 - soo:bak
작성일 :
DNS로 어떻게 트래픽을 분산하는가
Part 1에서 로드 밸런서가 클라이언트와 서버 사이에서 트래픽을 분산하는 원리를 살펴보았습니다. 하나의 로드 밸런서가 여러 서버로 요청을 나눠 보내고, 헬스 체크로 장애 서버를 제외하고, 세션을 유지하는 구조였습니다.
하지만 이 구조에는 전제가 있습니다. 클라이언트가 로드 밸런서의 주소를 알아야 한다는 것입니다. 사용자가 www.example.com을 입력하면, 가장 먼저 일어나는 일은 DNS 질의입니다. DNS가 어떤 IP를 돌려주느냐에 따라 사용자가 어느 데이터센터로 향할지가 결정됩니다.
그런데 모든 트래픽이 하나의 로드 밸런서를 거쳐야 한다면, 로드 밸런서 자체가 병목이 될 수도 있습니다. 서울에 있는 사용자와 뉴욕에 있는 사용자가 같은 로드 밸런서로 몰릴 수도 있습니다. 더 앞단에서, 사용자가 로드 밸런서에 도달하기도 전에 분산할 수는 없을까요?
DNS가 그 답입니다. 사용자가 도메인을 입력하면 브라우저는 먼저 DNS에 IP 주소를 질의합니다. DNS가 돌려주는 IP에 따라 사용자가 어느 서버, 어느 데이터센터로 향할지가 결정되므로, 로드 밸런서에 도달하기도 전에 트래픽을 분산할 수 있습니다.
가장 단순한 방법인 DNS 라운드 로빈부터 살펴봅니다.
DNS 라운드 로빈
DNS에서 하나의 도메인에는 여러 IP 주소를 등록할 수 있습니다.
1
2
3
example.com. IN A 203.0.113.1
example.com. IN A 203.0.113.2
example.com. IN A 203.0.113.3
DNS 서버는 질의가 들어올 때마다 이 목록의 순서를 한 칸씩 돌려서 반환합니다.
1
2
3
첫 번째 질의: 203.0.113.1, 203.0.113.2, 203.0.113.3
두 번째 질의: 203.0.113.2, 203.0.113.3, 203.0.113.1
세 번째 질의: 203.0.113.3, 203.0.113.1, 203.0.113.2
DNS가 여러 IP를 반환하면 클라이언트의 네트워크 라이브러리가 목록 형태로 주소를 돌려주는데, 대부분의 애플리케이션은 이 목록에서 첫 번째 IP로 연결을 시도합니다. 순서가 매번 바뀌므로 각 클라이언트가 받는 첫 번째 IP가 달라지고, 결과적으로 트래픽이 세 서버에 분산됩니다.
DNS 라운드 로빈의 한계
DNS 라운드 로빈은 트래픽을 분산하지만, DNS 자체는 서버의 상태나 부하를 알지 못합니다. 로드 밸런싱을 목적으로 설계된 프로토콜이 아니기 때문입니다.
헬스 체크 없음: DNS 서버는 등록된 서버가 정상인지 확인하지 않습니다. 서버에 장애가 발생해도 해당 IP를 계속 응답에 포함하므로, 클라이언트가 장애 서버로 연결될 수 있습니다.
캐싱 문제: 클라이언트와 리졸버가 DNS 응답을 캐시합니다. 서버를 추가하거나 제거해도 캐시가 만료될 때까지 변경이 반영되지 않습니다.
불균등 분산: 대기업처럼 NAT 뒤에서 수천 명이 하나의 리졸버를 공유하면, 모두 같은 캐시 결과를 사용합니다. 특정 서버 한 곳으로 트래픽이 쏠릴 수 있습니다.
세션 지속성 없음: 다음 DNS 질의에서 다른 IP를 받을 수 있습니다. 로그인 상태처럼 유지해야 하는 세션이 다른 서버로 넘어가면 끊어집니다.
단순한 분산에는 충분하지만, 정교한 제어가 필요하면 부족합니다. 이러한 한계를 보완하기 위해 더 발전된 DNS 기반 분산 방식이 등장했습니다.
지리 기반 라우팅 (GeoDNS)
DNS 라운드 로빈은 모든 클라이언트에게 같은 IP 목록을 돌려줍니다. 한국 사용자가 미국 서버로, 미국 사용자가 유럽 서버로 연결될 수도 있습니다.
네트워크 지연은 물리적 거리에 비례합니다. 서울에서 미국 서버까지 왕복하면 약 150~200ms가 걸리지만, 서울 서버라면 수 ms면 충분합니다. 사용자를 가까운 서버로 안내할 수는 없을까요?
지리 기반 라우팅(GeoDNS)은 클라이언트의 위치에 따라 다른 IP를 응답합니다.
1
2
3
한국 사용자: example.com → 1.2.3.4 (서울 서버)
미국 사용자: example.com → 5.6.7.8 (버지니아 서버)
유럽 사용자: example.com → 9.10.11.12 (프랑크푸르트 서버)
클라이언트 위치 추정
GeoDNS는 클라이언트의 위치를 어떻게 알까요? 권한 있는 DNS 서버가 참고할 수 있는 정보는 질의를 보낸 쪽의 IP 주소뿐입니다.
그런데 클라이언트는 권한 있는 DNS 서버에 직접 질의하지 않습니다.
중간에 리졸버를 거치므로, 권한 있는 DNS 서버가 보는 IP는 클라이언트가 아니라 리졸버의 것입니다.
클라이언트와 리졸버가 같은 지역에 있다면 문제가 없지만, 다른 지역에 있으면 위치 추정이 빗나갑니다.
1
2
3
4
5
6
7
8
9
클라이언트 (한국)
│
▼
리졸버 (미국 Google DNS: 8.8.8.8)
│
▼
권한 있는 DNS 서버
→ 8.8.8.8의 위치를 조회 → "미국"
→ 미국 서버 IP를 응답 ← 한국 사용자에게 잘못된 안내
EDNS Client Subnet (ECS)
리졸버가 클라이언트의 위치 힌트를 함께 보내면 어떨까요?
EDNS(Extension Mechanisms for DNS)는 DNS 프로토콜에 추가 정보를 실을 수 있게 하는 확장 규격입니다. ECS(EDNS Client Subnet, RFC 7871)는 이 확장을 이용하여, 리졸버가 클라이언트의 서브넷 정보(IP 주소의 네트워크 부분)를 권한 있는 DNS 서버에 함께 전달합니다.
권한 있는 DNS 서버는 이 서브넷 정보로 클라이언트의 실제 위치를 파악하여, 리졸버가 아닌 클라이언트의 위치에 맞는 서버를 안내할 수 있습니다.
1
2
3
4
5
6
7
8
9
클라이언트: 203.0.113.42 (한국)
│
▼
리졸버 (Google DNS):
질의에 포함: EDNS-CLIENT-SUBNET: 203.0.113.0/24
│
▼
권한 있는 DNS 서버:
203.0.113.0/24는 한국 → 한국 서버 IP 응답
클라이언트의 전체 IP(203.0.113.42)가 아닌 서브넷(203.0.113.0/24)만 전달하므로, 위치는 파악하되 개별 사용자를 특정하지 않습니다.
GSLB (Global Server Load Balancing)
GeoDNS는 클라이언트 위치에 따라 가까운 서버를 선택합니다. 그러나 GeoDNS도 본질적으로는 DNS이기 때문에, 선택한 서버가 정상인지 과부하 상태인지는 알지 못합니다. DNS에 로드 밸런서의 판단 능력을 더할 수는 없을까요?
GSLB(Global Server Load Balancing)는 데이터센터 간 트래픽을 분산하는 기술로, DNS와 로드 밸런싱을 결합한 형태입니다. 각 데이터센터의 상태를 주기적으로 모니터링하고, DNS 질의에 대한 응답을 결정할 때 서버 상태와 부하까지 고려합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DNS 질의
│
▼
┌─────────┐
│ GSLB │
└────┬────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌──────────┐
│서울 DC │ │도쿄 DC │ │싱가포르DC│
│ (정상) │ │ (정상) │ │ (장애) │
└────────┘ └────────┘ └──────────┘
↑ ↑ ↑
└────────────┴────────────┘
주기적 헬스 체크
→ 한국 클라이언트에게 서울 DC IP 응답
(가장 가깝고 정상인 데이터센터)
GSLB의 결정 요소
GSLB는 단순히 가까운 데이터센터를 선택하지 않습니다. 여러 요소를 종합적으로 평가하여, 예를 들어 서울 데이터센터가 가장 가깝더라도 과부하 상태이면 도쿄로 안내할 수 있습니다.
지리적 거리: 클라이언트와 가까운 데이터센터를 우선 선택하여 지연을 최소화합니다.
서버 상태: 주기적으로 헬스 체크를 수행하고, 장애가 감지된 데이터센터를 후보에서 제외합니다.
부하 상태: 각 데이터센터의 처리량과 자원 사용률을 확인하여, 과부하 데이터센터를 피합니다.
정책: 데이터 주권(개인정보가 해당 국가 내에 저장되어야 한다는 법적 요구사항) 등의 규정에 따라 특정 지역의 트래픽을 특정 데이터센터로 고정합니다.
Anycast
GeoDNS와 GSLB는 DNS 응답을 통해 트래픽을 분산합니다. 하지만 DNS 캐싱 때문에 변경이 즉시 반영되지 않는다는 근본적인 제약이 있습니다. DNS가 아닌 네트워크 계층에서 분산할 수는 없을까요?
애니캐스트(Anycast)는 여러 위치의 서버에 같은 IP 주소를 부여하는 방식입니다.
일반적으로 하나의 IP 주소는 하나의 서버를 가리킵니다. 하지만 서울, 도쿄, 런던의 서버가 모두 203.0.113.1이라는 같은 IP를 사용하면 어떻게 될까요?
각 서버는 자기 근처의 라우터에 “203.0.113.1로 오는 트래픽은 나에게 보내달라”고 알립니다. 인터넷의 라우터들은 BGP(Border Gateway Protocol)를 통해 이 경로 정보를 교환하는데, 같은 IP에 대한 경로가 여러 개이면 가장 가까운 경로를 자동으로 선택합니다.
1
2
3
4
5
6
7
8
9
10
[경로 광고] 각 서버가 근처 라우터에 같은 IP를 알림
서울 서버(203.0.113.1) ──→ 아시아 라우터: "203.0.113.1은 여기로"
런던 서버(203.0.113.1) ──→ 유럽 라우터: "203.0.113.1은 여기로"
뉴욕 서버(203.0.113.1) ──→ 미국 라우터: "203.0.113.1은 여기로"
[라우팅 결과] 같은 IP를 요청해도 가장 가까운 서버에 도달
한국 사용자 → 203.0.113.1 요청 → 아시아 라우터 → 서울 서버
영국 사용자 → 203.0.113.1 요청 → 유럽 라우터 → 런던 서버
Anycast의 특징
장점:
분산이 IP 라우팅 수준에서 이루어지므로, DNS나 애플리케이션을 수정할 필요가 없습니다.
장애 대응도 자동입니다. 특정 위치의 서버가 다운되면 해당 서버의 경로 광고가 사라지고, 라우터들은 남은 경로 중 가장 가까운 서버로 트래픽을 보냅니다.
DDoS 공격에도 강합니다. 공격 트래픽이 여러 위치로 자연스럽게 분산되어 개별 서버의 부하가 줄어들기 때문입니다.
한계:
BGP 경로가 변경되면 기존 TCP 연결이 끊어질 수 있습니다. 경로가 바뀌면 패킷이 이전과 다른 서버에 도달하는데, 새 서버에는 해당 연결 상태가 없으므로 재연결이 필요합니다.
경로 변경 후 모든 라우터가 새 경로에 합의하는 데에도 시간이 걸립니다. 여러 위치에서 동일 IP의 서버를 운영하면서 설정을 일관되게 유지해야 하므로 운영 부담도 큽니다.
이러한 제약 때문에 Anycast는 주로 DNS 서버나 CDN 서버처럼 짧은 트랜잭션에 사용됩니다. DNS 질의는 주로 UDP 기반의 단일 요청-응답이므로 연결 상태를 유지할 필요가 없어, 경로가 변경되어도 영향을 받지 않습니다.
DNS TTL과 장애 복구
앞서 살펴본 DNS 라운드 로빈, GeoDNS, GSLB는 모두 DNS 응답을 통해 트래픽을 분산합니다. 하지만 DNS 응답은 캐시됩니다.
서버에 장애가 발생하여 DNS 레코드를 변경하더라도, 캐시가 만료되기 전까지 클라이언트는 여전히 장애 서버의 IP로 요청을 보냅니다.
DNS 레코드에는 캐시 유지 시간인 TTL(Time To Live)이 포함되어 있습니다. TTL이 길면 캐시 효율은 높아지지만, 장애 시 새 응답이 반영되기까지 그만큼 오래 걸립니다.
1
2
3
example.com. 300 IN A 203.0.113.1
│
└── TTL: 300초 (5분)
TTL의 트레이드오프
낮은 TTL(예: 60초)은 장애 시 빠르게 새 응답을 반영할 수 있습니다. 하지만 캐시가 자주 만료되므로 리졸버가 권한 있는 DNS 서버에 더 자주 질의해야 합니다. 재질의가 일어나는 동안 클라이언트는 응답을 기다려야 하므로 체감 지연이 늘어나고, 권한 있는 DNS 서버의 부하도 증가합니다.
TTL이 60초인 도메인에 초당 1만 명이 접속하더라도, 같은 리졸버를 쓰는 사용자끼리는 캐시를 공유하므로 리졸버당 1분에 1회 정도만 질의가 발생합니다. 하지만 리졸버 수가 많아질수록 권한 있는 DNS 서버가 받는 총 질의량도 비례하여 증가합니다.
반면 높은 TTL(예: 86400초 = 24시간)은 캐시가 오래 유지되어 질의 빈도와 부하가 줄어듭니다. 하지만 장애가 발생하여 DNS 레코드를 변경하더라도, 캐시가 만료될 때까지 최대 24시간 동안 일부 사용자는 장애 서버로 접속을 시도합니다.
장애 복구 시나리오
서울 데이터센터에 장애가 발생했고 DNS TTL이 1시간인 경우를 살펴봅시다.
1
2
3
4
5
6
시간 0분: 장애 발생, GSLB가 서울 IP를 DNS 응답에서 제거
시간 0~60분: 기존 캐시가 유효
→ 일부 사용자는 여전히 서울로 접속 시도 → 연결 실패
시간 60분: 캐시 만료, 새 질의 → 정상 서버의 IP 수신 → 서비스 복구
TTL을 낮추면 이 복구 시간을 줄일 수 있을까요? 반드시 그렇지는 않습니다.
DNS 캐시 문제
TTL을 아무리 낮춰도, 권한 있는 DNS 서버와 클라이언트 사이에는 서버가 제어할 수 없는 캐시 계층이 여러 겹 존재합니다.
ISP 리졸버는 자체 부하를 줄이기 위해 최소 TTL을 강제하기도 합니다. TTL을 60초로 설정해도 리졸버가 최소 300초를 적용하면, 실제 캐시 시간은 5분이 됩니다. 리졸버가 새 응답을 반환하더라도 운영체제의 DNS 캐시, 브라우저의 DNS 캐시가 각각 이전 결과를 보관하고 있을 수 있습니다.
DNS만으로는 즉시 전환을 보장할 수 없습니다.
해결책: 다중 방어 계층
장애 복구를 DNS TTL에만 의존할 수는 없습니다. 실무에서는 여러 계층에서 서로 다른 속도로 장애에 대응합니다.
DNS 라운드 로빈에서 여러 IP를 받은 클라이언트는, 첫 번째 IP 연결에 실패하면 다음 IP로 재시도할 수 있습니다. 이 로직은 애플리케이션이나 SDK에 내장되어 있어 DNS 캐시와 무관하게 즉시 동작합니다.
Anycast는 장애 서버의 경로 광고가 사라지면, 라우터가 수초 내에 다른 서버로 트래픽을 전환합니다. 이 역시 DNS 캐시와 무관하게 네트워크 계층에서 동작합니다.
GSLB는 헬스 체크로 장애 서버를 감지하고 DNS 응답에서 제외합니다. TTL을 60초 정도로 낮추면 대부분의 클라이언트가 1분 이내에 새 응답을 받습니다.
CloudFlare나 AWS CloudFront 같은 CDN을 앞단에 배치하면, CDN이 원본 서버의 장애를 감지하고 다른 원본 서버로 전환합니다. CDN 자체가 Anycast를 사용하므로 DNS 지연 없이 전환됩니다.
1
2
3
4
클라이언트 재시도: 즉시 (밀리초)
Anycast/BGP 전환: 수초
GSLB + 낮은 TTL: 수십 초 ~ 수 분
DNS 캐시 만료: 수 분 ~ 수 시간 ← DNS에만 의존할 때
각 계층의 복구 속도가 다르므로, 여러 계층을 조합하면 DNS 캐시 만료를 기다리지 않고도 빠르게 복구할 수 있습니다.
다중 레벨 로드 밸런싱
지금까지 살펴본 기술들을 종합하면, 실제 대규모 서비스는 여러 계층의 로드 밸런싱을 조합하여 사용합니다. DNS/GSLB는 넓은 범위를 다루지만 반영이 느리고, 로드 밸런서는 반영이 빠르지만 단일 데이터센터 안에서만 동작합니다. 한 계층만으로는 이 두 가지를 동시에 충족할 수 없기 때문에, 각 계층이 자신의 강점에 집중하는 구조가 필요합니다.
요청은 넓은 범위에서 좁은 범위로, 각 계층을 순서대로 거칩니다.
- DNS/GSLB: 대륙·지역 단위로 가장 적합한 데이터센터를 선택합니다.
- CDN: Anycast 기반으로 정적 콘텐츠를 사용자 가까이에서 처리합니다.
- L4 로드 밸런서: 데이터센터 내부에서 IP와 포트 정보로 TCP 연결을 분산합니다.
- L7 로드 밸런서: URL, 헤더, 쿠키 등 요청 내용을 해석하여 최종 서버를 선택합니다.
L4, L7 로드 밸런서는 Part 1에서 다룬 전송 계층, 애플리케이션 계층 로드 밸런서입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
사용자
│
▼
DNS/GSLB ← 글로벌: 데이터센터 선택
│
▼
CDN (Anycast) ← 정적 콘텐츠 처리
│
▼
L4 로드 밸런서 ← 데이터센터 내: TCP 연결 분산
│
├──────┬──────┐
▼ ▼ ▼
L7 LB L7 LB L7 LB ← 요청 내용 기반 라우팅
│ │ │
▼ ▼ ▼
서버 서버 서버
상위 계층이 “어느 데이터센터로 보낼지”를 결정하고, 하위 계층이 “그 안에서 어느 서버가 처리할지”를 결정하는 구조입니다.
실제 구현: Route 53, CloudFlare, Akamai
DNS 라운드 로빈부터 GSLB, Anycast까지 원리를 살펴보았습니다. 이 기술들을 직접 구축할 수도 있지만, 대부분의 경우 클라우드 서비스가 이를 대신 제공합니다.
Amazon Route 53
AWS의 관리형(인프라 운영을 AWS가 대신 맡아 주는) DNS 서비스입니다. 글로벌 Anycast 네트워크 위에서 동작하며, 헬스 체크와 자동 장애 복구를 기본 제공합니다.
Route 53은 앞서 살펴본 개념들을 라우팅 정책으로 제공합니다.
- Simple: DNS 라운드 로빈
- Weighted: 가중치 기반 분산
- Latency: 지연 시간이 가장 짧은 서버 선택
- Geolocation: 클라이언트 지역 기반 라우팅
- Failover: Active-Passive 장애 복구
이 외에 Geoproximity, Multivalue Answer, IP-based 정책도 지원합니다.
각 정책은 헬스 체크와 연동되어, 정상 서버만 DNS 응답에 포함됩니다. 여러 정책을 조합할 수도 있어서, Geolocation으로 대륙을 선택한 뒤 Weighted로 서버 간 비율을 조절하는 구성도 가능합니다.
AWS에서 CDN 계층은 CloudFront가 담당합니다. Route 53이 도메인을 CloudFront 배포 주소로 해석하면, CloudFront가 전 세계 서버에서 캐시된 콘텐츠를 전달합니다. 원본 서버에 장애가 발생하면 CloudFront가 다른 원본으로 전환할 수 있어, Route 53의 DNS 분산과 CloudFront의 CDN 분산이 서로 다른 계층에서 함께 동작합니다.
CloudFlare
CloudFlare는 CDN, DNS, 보안을 통합 제공하는 서비스입니다. 모든 DNS 질의가 Anycast 네트워크를 통해 처리되므로, 별도의 GeoDNS 설정 없이도 사용자가 가장 가까운 서버에서 응답을 받습니다.
헬스 체크가 기본 내장되어 있어, 원본 서버(CDN 뒤에 위치한 실제 서비스 서버)에 장애가 발생하면 자동으로 트래픽을 우회합니다. Anycast 특성상 DDoS 트래픽도 전 세계 서버로 분산되어 흡수됩니다.
유료 플랜에서는 가중치 기반 로드 밸런싱과 지역별 서버 그룹 설정이 가능하여, GSLB와 유사한 수준의 트래픽 관리를 제공합니다.
Akamai
Akamai는 세계 최대 CDN 중 하나로, GTM(Global Traffic Management)이라는 트래픽 관리 기능을 제공합니다.
GTM은 전 세계에 분산된 서버에서 실시간으로 네트워크 지연과 가용성을 측정합니다. 클라이언트의 지역만 보는 GeoDNS와 달리 실제 성능 데이터를 기반으로 라우팅 경로를 결정합니다.
가중치, 지리, 성능 기반 등 여러 알고리즘을 조합할 수 있으며, 장애 복구 정책도 데이터센터 단위에서 개별 서버 단위까지 세분화할 수 있습니다.
마무리: DNS는 첫 번째 관문
- DNS 라운드 로빈은 여러 IP를 돌려가며 응답하는 가장 간단한 분산 방식이지만, 헬스 체크와 정밀 제어가 없습니다.
- GeoDNS는 클라이언트 위치 기반으로 가까운 서버를 안내하고, ECS로 위치 정확도를 높일 수 있습니다.
- GSLB는 헬스 체크와 부하 상태까지 고려하여 데이터센터를 선택합니다.
- Anycast는 네트워크 계층에서 자동으로 분산하지만, TCP 연결 유지에 제약이 있습니다.
- DNS TTL은 장애 복구 속도와 캐시 부하 사이의 트레이드오프이며, 다중 방어 계층으로 보완합니다.
DNS 라운드 로빈에서 Anycast까지, 각 방식은 분산의 정밀도와 복잡도가 다르지만 캐싱이라는 근본적 제약을 공유합니다. DNS는 글로벌 분산의 첫 번째 관문이며, 정밀한 분산과 빠른 장애 복구는 그 뒤의 로드 밸런서가 담당합니다.
트래픽을 “어디로 보낼지”는 DNS가 결정하지만, “보낸 곳에서 장애가 나면 어떻게 할지”는 아직 다루지 않았습니다. 로드 밸런서 자체가 멈추면? 서버 전체가 다운되면?
Part 3에서는 이 질문을 다룹니다. Active-Passive, Active-Active 구성과 VRRP/HSRP 같은 장애 전환 프로토콜을 살펴봅니다.
관련 글
시리즈
- 로드 밸런싱과 고가용성 (1) - 로드 밸런싱의 원리
- 로드 밸런싱과 고가용성 (2) - DNS 기반 로드 밸런싱 (현재 글)
- 로드 밸런싱과 고가용성 (3) - 고가용성 아키텍처