NGINX Plus R9에서는 UDP 트래픽을 리버스 프록시하고 부하 분산하는 기능이 도입되어 NGINX Plus의 레이어 4 부하 분산 기능이 크게 향상되었습니다.
이 블로그 게시물에서는 최신 애플리케이션 인프라에서 DNS 서버를 운영할 때의 과제를 살펴보고, NGINX Open Source와 NGINX Plus가 UDP 및 TCP 트래픽 모두를 얼마나 효과적이고 효율적으로 로드 밸런싱할 수 있는지 설명합니다. (애플리케이션 [능동형] 상태 확인 기능은 NGINX Plus에서만 제공되지만, 그 외의 내용은 NGINX Open Source에도 동일하게 적용됩니다. 간결한 설명을 위해 이 게시물의 나머지 부분에서는 NGINX Plus로 통칭하겠습니다.)
[편집자 주 – NGINX Plus R9의 모든 새로운 기능에 대한 개요는 블로그의 NGINX Plus R9 발표를 참조하세요.]
UDP 트래픽에 로드 밸런싱이 필요한 이유
TCP와 달리 UDP는 설계상 데이터의 종단 간 전송을 보장하지 않습니다. 이는 전서구를 통해 메시지를 보내는 것과 비슷합니다. 메시지가 전송되었다는 사실은 확실히 알 수 있지만, 제대로 도착했는지는 확신할 수 없습니다. 이러한 ‘비연결형’ 방식에는 여러 장점이 있습니다. 특히 UDP는 개별 메시지의 크기가 작아 대역폭을 적게 사용하고, 연결을 설정하기 위한 핸드셰이크 과정도 없기 때문에 TCP보다 지연 시간이 짧습니다. UDP에서는 시간 초과 및 기타 네트워크 수준의 문제를 감지하는 작업을 애플리케이션 개발자가 처리해야 합니다. 그렇다면 이것이 DNS에는 어떤 의미일까요?
다른 여러 UDP 기반 프로토콜과 마찬가지로 DNS는 요청-응답 방식으로 데이터를 주고받습니다. 예를 들어 DNS 클라이언트가 호스트 이름에 해당하는 IP 주소를 요청하면 응답을 받습니다. 정해진 제한 시간 내에 응답이 도착하지 않으면 DNS 클라이언트는 동일한 요청을 “백업” DNS 서버로 보냅니다. 하지만 요청을 다시 보내기 전에 제한 시간이 끝날 때까지 기다려야 하므로, 일반적으로 매우 빠른 프로세스(밀리초 단위)가 매우 느린 프로세스(초 단위)로 바뀔 수 있습니다.

NGINX Plus를 사용하여 DNS 트래픽을 프록시하고 부하 분산하면 클라이언트에서 시간 초과가 발생하는 경우를 줄일 수 있습니다. NGINX Plus 부하 분산 장치 뒤에 여러 DNS 서버를 배치하면 클라이언트와 NGINX Plus 사이에 네트워크 파티션이 발생한 경우에만 클라이언트에서 시간 초과가 발생합니다. NGINX Plus에서 애플리케이션 상태 확인을 사용하면 DNS 서버 자체에 문제가 발생하더라도 클라이언트에는 영향을 주지 않습니다. NGINX Plus는 각 서버의 가용성과 응답 시간을 모니터링하여 비정상 서버로 클라이언트 요청을 보내지 않습니다.
DNS는 UDP 전용 프로토콜이 아닙니다
DNS 트래픽의 대부분은 UDP를 통해 전송되지만, TCP를 사용하는 일반적인 DNS 작업도 있습니다. DNS는 작은 메시지(최대 512바이트)에 UDP를 사용하지만, 더 큰 메시지가 필요하거나 필요할 가능성이 높은 작업에는 TCP를 사용합니다. 과거에는 TCP가 권한 있는 주 네임 서버에서 보조 네임 서버로 영역을 전송할 때만 DNS와 함께 사용되었습니다. 그러나 컨테이너와 불변 인프라로 전환되면서 DNS는 SRV 레코드를 통해 기본 서비스 검색 메커니즘으로 점점 더 많이 사용되고 있습니다.
DNS SRV 레코드는 원래 SIP를 사용하는 VoIP(Voice over IP) 단말기가 서버를 검색할 수 있도록 도입되었지만, 모든 유형의 서비스에 사용할 수 있습니다. 그러나 SRV 레코드는 대부분의 다른 DNS 레코드 유형보다 훨씬 많은 정보를 포함합니다. 따라서 표준 512바이트 UDP 응답에는 SRV 레코드가 약 10개만 들어가는 반면, A 레코드는 약 30개까지 들어갑니다. DNS 응답이 512바이트 제한을 초과하면 처음 512바이트만 반환되고 응답은 “잘림”으로 표시됩니다. 이때 DNS 클라이언트는 잘린 응답을 최대한 처리하거나 TCP를 사용하여 동일한 요청을 다시 시도할 수 있습니다.
즉, 최신 네트워크 인프라에서 DNS 서버의 부하를 분산할 때 NGINX Plus는 UDP와 TCP 트래픽이 혼합되어 수신될 것으로 예상할 수 있습니다.
마이크로서비스 환경의 DNS
다음 그림은 두 개의 로드 밸런서가 있는 마이크로서비스 환경을 간략하게 보여 줍니다. 프런트엔드 로드 밸런서는 애플리케이션의 공개 클라이언트에서 들어오는 요청을 프록시하고, 최적의 마이크로서비스 인스턴스를 선택하며, 여기서는 다루지 않을 그 밖의 여러 기능을 수행합니다. 여기서는 마이크로서비스 환경과 마이크로서비스에 서비스 검색 정보를 제공하는 DNS 서버 사이에 위치한 DNS 로드 밸런서에 중점을 두겠습니다.

기본 DNS 부하 분산 구성
NGINX Plus는 Stream 모듈에서 계층 4 로드 밸런싱을 구현하므로, UDP 및 TCP 로드 밸런싱은 다음 코드 조각과 같이 stream 블록에서 구성합니다.
경고: 이 구성 스니펫을 새 파일로 만들어 /etc/nginx/conf.d 디렉터리에 추가하기만 해서는 안 됩니다. 기본 NGINX Plus nginx.conf 구성 파일이 conf.d 디렉터리에 있는 파일의 내용을 http 블록에 포함하므로 유효성 검사 오류(“여기에서는 stream 지시문을 사용할 수 없습니다”)가 발생합니다. 가장 간단한 해결 방법은 전체 stream 블록을 nginx.conf에 직접 포함하는 것입니다.
stream { upstream dns_servers {
server 192.168.136.130:53;
server 192.168.136.131:53;
}
server {
listen 53 udp;
listen 53; #tcp
proxy_pass dns_servers;
error_log /var/log/nginx/dns.log info;
}
}먼저 업스트림 DNS 서버 그룹을 정의합니다. server 지시문은 업스트림 서버가 수신 대기하는 포트 번호인 53(DNS에 널리 알려진 포트)을 지정합니다.
The server{} 블록은 NGINX Plus가 수신 DNS 트래픽을 처리하는 방식을 정의합니다. 두 개의 listen 지시문은 NGINX Plus가 UDP 및 TCP 트래픽 모두에 대해 포트 53에서 수신 대기하도록 지정합니다. TCP는 Stream 모듈의 기본 계층 4 프로토콜이므로 UDP와 달리 매개변수로 명시적으로 지정하지 않습니다.
The proxy_pass 지시문은 NGINX Plus가 수신 중인 트래픽을 어떻게 처리할지 지정합니다. 여기서는 이러한 트래픽을 dns_servers 업스트림 그룹으로 프록시합니다. NGINX Plus는 클라이언트의 UDP 요청을 업스트림 서버로 전달할 때 자동으로 UDP를 사용하고 클라이언트의 TCP 요청에는 TCP를 사용하므로, 업스트림 그룹에 계층 4 프로토콜을 명시적으로 지정할 필요가 없습니다.
Stream 모듈에는 access_log 지시어가 없습니다. NGINX Plus는 HTTP 패킷의 경우와 달리 TCP 세그먼트나 UDP 데이터그램의 페이로드를 검사하지 않기 때문입니다. 하지만 info 매개변수를 error_log 지시어에 사용하여 연결 처리 및 프록시 이벤트를 기록할 수 있습니다.
[편집자 주 – 액세스 로깅은 이 블로그가 게시된 후 Stream 모듈에서 NGINX Open Source 1.11.4 및 NGINX Plus R11부터 활성화되었습니다.]
고가용성을 위한 튜닝
DNS 서버의 가용성을 높이기 위해 몇 가지 지시문을 추가하고 능동형(애플리케이션) 상태 검사를 구성할 수 있습니다.
프록시 응답 횟수 및 시간 제한 설정
첫 번째 추가 지시어는 proxy_responses이며, 프록시된 각 UDP 요청에 대해 NGINX Plus가 예상하는 응답 수를 지정합니다. 이 경우 NGINX Plus는 응답 하나를 받으면 즉시 추가 응답 대기를 중단하여 해당 세션에서 사용하던 메모리와 소켓을 해제합니다.
두 번째 추가 지시문인 proxy_timeout은 NGINX Plus가 서버의 응답을 기다리는 시간을 결정합니다(여기서는 기본값인 10분을 1초로 줄임). 이 시간 내에 NGINX Plus가 응답을 받지 못하면 업스트림 그룹의 다음 서버를 시도하고, 응답하지 않는 업스트림 서버를 일정 시간(기본값은 10초) 동안 사용할 수 없는 상태로 표시하여 그동안 다른 클라이언트가 시간 초과로 인한 지연을 겪지 않도록 합니다.
server { listen 53 udp;
listen 53; #tcp
proxy_pass dns_servers;
error_log /var/log/nginx/dns.log info;
proxy_responses 1;
proxy_timeout 1s;
}업스트림 그룹의 fail_timeout 지시문에 server 옵션을 포함하여 서버가 사용 불가로 표시되는 시간을 변경할 수도 있습니다. 다음과 같이 설정하면 NGINX Plus는 장애가 발생한 업스트림 서버를 60초 동안 사용 불가로 표시합니다:
upstream dns_servers { server 192.168.136.130:53 fail_timeout=60s;
server 192.168.136.131:53 fail_timeout=60s;
}이를 통해 DNS 서버 중 하나에 장애가 발생할 경우 클라이언트가 겪는 지연 시간을 제어할 수 있습니다. 그러나 장애가 발생한 DNS 서버에 TCP 요청을 시도하면 TCP에 내장된 오류 검사 기능을 통해 NGINX Plus가 해당 서버를 자동으로 사용 불가 상태로 표시하므로, 이후에는 그 서버로 TCP 또는 UDP 요청을 보내지 않습니다.
능동 상태 검사 구성
NGINX Plus의 능동 상태 확인 기능은 DNS를 비롯한 모든 부하 분산 서비스의 고가용성을 보장하는 데 매우 유용한 추가 도구입니다. DNS 클라이언트의 실제 TCP 요청이 실패할 때까지 기다렸다가 DNS 서버를 다운 상태로 표시하는 대신, health_check 지시어와 해당 port=53 매개변수를 server{} 블록에 포함하여 NGINX Plus가 주기적으로 포트 53에 TCP 연결을 시도하도록 함으로써 DNS 서버가 가동 중이고 정상적으로 작동하는지 확인합니다. (기본적으로 NGINX Plus는 listen 지시어에 지정된 포트로 상태 확인을 전송하며, 여기서는 포트 53입니다. 따라서 여기서는 이 매개변수를 사용해 기본값을 명시적으로 설정하지만, DNS 서버도 해당 포트의 트래픽에 응답하도록 수정한다면 다른 포트를 지정할 수도 있습니다.)
UDP를 사용하면 한 단계 더 나아가 알려진 레코드에 대해 실제 DNS 조회를 수행하는 능동 상태 확인을 구성할 수 있습니다. 예를 들어 마이크로서비스 환경에서 서비스 검색에 사용하는 것과 동일한 하위 도메인의 영역 파일에 다음과 같은 CNAME 레코드를 추가할 수 있습니다.
healthcheck IN CNAME healthy.svcs.example.com.UDP는 가벼운 프로토콜이므로 네트워크 트래픽을 관찰하여 DNS 조회를 나타내는 바이트 문자열을 쉽게 추출할 수 있습니다. 그런 다음 해당 문자열을 match 구성 블록의 send 지시어에 매개변수로 지정합니다. expect 지시어는 서버가 정상 상태로 간주되기 위해 반환해야 하는 응답을 지정합니다.
match dns_lookup {
send x00x01x00x00x00x01x00x00x00x00x00x00x06x68x65x61 ...;
expect ~* "healthy.svcs.example.com.";
}이처럼 애플리케이션 수준에서 심층 상태 검사를 수행하면 네임 서버가 정상적으로 실행 중이더라도 프로덕션 도메인에 대해 실제 DNS 조회를 수행하여, 그렇지 않으면 이후 단계에서 문제를 일으킬 수 있는 구성 오류와 데이터 손상을 발견할 수 있습니다.
The NGINX Plus 지원팀에서는 DNS 조회 및 기타 프로토콜을 위한 UDP 상태 확인을 준비하는 데 도움을 드릴 수 있습니다.
다음 코드 조각은 능동 상태 확인에 필요한 추가 지시어를 보여 줍니다.
stream { upstream dns_servers {
zone dns_mem 64k;
server 192.168.136.130:53 fail_timeout=60s;
server 192.168.136.131:53 fail_timeout=60s;
}
match dns_lookup { send x00x01x00x00x00x01x00x00x00x00x00x00x06x68x65x61 ...; expect ~* "healthy.svcs.example.com."; }
server {
listen 53 udp;
listen 53; #tcp
health_check match=dns_lookup interval=20 fails=2 passes=2 udp;
health_check interval=20 fails=1 passes=2 port=53; #tcp
proxy_pass dns_servers;
error_log /var/log/nginx/dns.log debug;
proxy_responses 1;
proxy_timeout 1s;
}
}The zone 지시문은 dns_mem이라는 공유 메모리 영역을 정의하여 상태 확인 결과(및 기타 상태 정보)를 모든 NGINX Plus 워커 프로세스에서 사용할 수 있도록 합니다.
The match 지시어는 바로 위에서 설명합니다.
The health_check 지시문에는 환경에 맞게 조정할 수 있는 여러 매개변수가 있습니다. 여기서는 UDP와 TCP에 대해 각각 별도의 상태 확인을 정의합니다. UDP와 TCP의 차이로 인해 DNS 서버를 비정상으로 표시하려면 UDP 상태 확인은 두 번 연속 실패해야 하지만, TCP는 한 번만 실패해도 됩니다. 두 프로토콜 모두 서버를 다시 정상으로 표시하려면 두 번의 성공적인 응답이 필요합니다. 이는 상태가 불안정하여 정상과 비정상 상태를 반복하는 서버로 요청이 전송되는 것을 방지하기 위한 것입니다.
UDP 및 TCP 트래픽 모두에 대해 단일 DNS 서버 업스트림 그룹을 정의하면, 어느 프로토콜에서든 상태 확인에 실패할 경우 해당 서버가 비정상으로 표시되고 부하 분산 풀에서 제외된다는 이점이 있습니다.
대규모 환경을 위한 튜닝
백엔드 서버를 단 두 대만 배포해도 효과적인 고가용성 솔루션이 될 수 있지만, NGINX Plus의 로드 밸런싱 기능을 사용하면 클라이언트가 인지하지 못하는 상태에서 백엔드 서버를 수평으로 확장할 수 있습니다.
위에서 설명한 샘플 마이크로서비스 환경에서는 백엔드 DNS 서버를 확장해야 할 가능성이 낮습니다. 그러나 모든 가입자에게 DNS 서비스를 제공하는 ISP는 지속적인 부하와 급격한 트래픽 증가 가능성에 직면하므로, 다수의 DNS 서버와 이들 서버 간에 트래픽 부하를 분산할 프런트엔드 프록시가 필요합니다.
NGINX 및 NGINX Plus의 모든 부하 분산 알고리즘은 HTTP뿐만 아니라 TCP와 UDP에도 사용할 수 있습니다:
- 라운드 로빈(기본값)
- 일반 해시 및 그 일관된 변형(ketama 알고리즘)
- IP 해시
- 최소 연결
- 최소 시간(NGINX Plus 전용)
(모든 알고리즘에 가중치를 구성하여 효율성을 더욱 높일 수도 있습니다. 자세한 내용은 블로그의 NGINX Plus 부하 분산 기법 선택에서 가중치 관련 섹션을 참조하세요.)
HTTP 요청은 백엔드 서버에 가하는 부하와 처리 요구 사항이 크게 달라질 수 있지만, DNS 요청은 일반적으로 모두 동일한 부하를 발생시킵니다. 따라서 최소 연결 및 최소 시간 알고리즘은 라운드 로빈보다 이점을 제공하기 어렵습니다. 특히 최소 연결 알고리즘은 NGINX Plus가 업스트림 서버의 응답을 아직 기다리고 있는 모든 UDP 요청을 연결 수에 포함합니다. proxy_responses 및 proxy_timeout의 설정값에 도달하지 않은 동안에는 업스트림 서버가 이미 작업을 완료했더라도 NGINX Plus가 해당 서버의 연결을 계속 집계합니다.
클라이언트 수가 많고 RADIUS 챌린지-응답 흐름처럼 클라이언트와 서버 간에 여러 메시지를 주고받는 “대화”가 많은 프로토콜을 사용하는 경우, 소스 IP 해시를 사용하면 단일 백엔드 서버에서 해당 대화가 이루어지도록 할 수 있습니다. 즉, 세션 지속성을 설정하여 NGINX Plus가 특정 클라이언트의 모든 요청을 동일한 서버로 전달하도록 합니다. 다음 예에서는 소스(클라이언트) IP 주소($remote_addr 변수로 캡처됨)를 키로 사용하여 한 쌍의 RADIUS 인증 서버에 해시 부하 분산 알고리즘을 구성합니다.
upstream radius_servers { hash $remote_addr; # Source-IP hash
server 192.168.136.201:1812;
server 192.168.136.202:1812;
}추가 정보
UDP 및 TCP 부하 분산에 대한 자세한 내용은 다음 리소스를 참조하세요:
- TCP 및 UDP 부하 분산(NGINX Plus 관리자 가이드)
- NGINX Stream 모듈 및
upstream구성 컨텍스트 - 배포 가이드TCP 기반 트래픽과 HTTP 기반 트래픽을 모두 처리해야 하는 Microsoft® Exchange™ 서버의 부하 분산을 위한
- NGINX Plus 및 Galera Cluster를 활용한 MySQL 고가용성<.htmla> (TCP 부하 분산 사용 사례)
NGINX Plus R9의 다른 뛰어난 기능에 대해 알아보려면 블로그에서 NGINX Plus R9 발표를 읽고 주문형 웨비나인 NGINX Plus R9의 새로운 기능을 시청하세요.
NGINX Plus를 사용해 보려면 지금 30일 무료 체험을 시작하거나 사용 사례에 대해 논의하려면 문의해 주세요.
작성자 소개

관련 블로그 게시물
시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.
F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.
Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.
진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.
NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.
Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.