Logic in Code,
Freedom in Travel.

인생 뭐 있나 사람 사는거 다 똑같지

Computer Science/Network

[Network] Ubuntu DNS 네임서버의 TCP 튜닝: BIND9 적용 사례와 최적화 과정

귀찮은 개발자2026. 1. 6. 13:18
목차 (Table of Contents)

DNS와 TCP, 그리고 성능의 병목

DNS(Domain Name System)는 가볍고 빠른 UDP(User Datagram Protocol) 53번 포트를 주로 사용한다고 알려져 있다. 하지만 DNSSEC의 보급, IPv6 도입, TXT 레코드 등으로 인해 응답 패킷의 크기가 UDP의 제한인 512바이트(EDNS0 제외 시)를 초과하는 경우가 빈번해졌다. 이때 DNS는 신뢰성 있는 전송을 위해 TCP로 프로토콜을 전환(Fallback)한다.

학교에서 배웠듯 TCP는 3-way Handshake를 통해 연결을 수립하고, 흐름 제어(Flow Control)와 혼잡 제어(Congestion Control)를 수행한다. 이는 데이터 무결성을 보장하지만, UDP 대비 오버헤드가 크다는 것을 의미한다. 트래픽이 몰리는 네임서버에서 TCP 관련 커널 파라미터가 기본값으로 설정되어 있다면, 연결 수립 대기 시간(Latency)이 증가하거나 SYN Flooding과 유사한 증상을 겪으며 패킷 드랍이 발생할 수 있다. 해당 포스트에서는 Ubuntu 환경에서 DNS 네임서버를 운영하며 겪었던 TCP 병목 현상을 BING9 으로 실험한 튜닝 과정을 작성해봤다.  

환경 설정 및 기본 구성

  • OS: Ubuntu 22.04 LTS
  • DNS Software: BIND 9.18 (Stable) / 회사에서는 자체적으로 개발한 네임서버를 이용중이지만 공부를 위해 BIND 9를 사용했다. 
  • Hardware: 8 vCPU, 16GB RAM
sudo apt update
sudo apt install bind9 bind9utils bind9-doc

기본 설정 상태의 Ubuntu 커널 파라미터는 범용적인 워크로드에 맞춰져 있어, 초당 수천 건 이상의 쿼리를 처리해야 하는 전용 DNS 서버로서는 TCP 연결 처리 용량이 부족할 수 있다.

튜닝 과정: 커널 레벨의 TCP 최적화

TCP 튜닝의 핵심은 운영체제가 소켓(Socket)을 처리하는 방식을 최적화하는 것이다. /etc/sysctl.conf 파일을 수정하여 커널 파라미터를 영구적으로 적용한다.

연결 대기열(Backlog Queue) 확장

TCP 연결 과정에서 커널은 두 개의 큐를 관리한다. 하나는 SYN_RECEIVED 상태(Handshake 진행 중)를 위한 큐이고, 다른 하나는 ESTABLISHED 상태(연결 완료 후 애플리케이션의 accept() 대기)를 위한 큐이다.

# /etc/sysctl.conf

# 1. 최대 SYN 백로그 크기 증가 (SYN Queue)
# 동시에 많은 TCP 연결 요청(SYN)이 들어올 때 이를 수용할 수 있는 대기열의 크기다.
# 3-way Handshake의 첫 단계인 SYN 패킷 수신 용량을 늘려 패킷 유실을 방지한다.
net.ipv4.tcp_max_syn_backlog = 4096

# 2. 소켓 Listen 백로그 제한 증가 (Accept Queue)
# 3-way Handshake가 완료된 소켓이 어플리케이션(BIND)에 의해 accept() 되기를 기다리는 큐의 크기다.
# 이 값이 너무 작으면 연결은 성립되었으나 처리가 지연되어 클라이언트 입장에서 타임아웃이 발생한다.
net.core.somaxconn = 4096

TCP 메모리 및 윈도우 스케일링

TCP 슬라이딩 윈도우(Sliding Window) 메커니즘을 효율적으로 사용하기 위해 버퍼 크기를 조정한다.

# 3. TCP 소켓 버퍼 튜닝
# 읽기(rmem)와 쓰기(wmem) 버퍼의 최소, 기본, 최대 크기를 지정한다.
# 버퍼 크기는 윈도우 사이즈(Window Size)와 직결되며, 이는 대역폭 지연 곱(BDP)에 영향을 미쳐 전송 효율을 결정한다.
net.ipv4.tcp_rmem = 4096 87380 6291456
net.ipv4.tcp_wmem = 4096 16384 4194304

포트 고갈 방지 및 연결 재사용

짧은 시간 동안 많은 쿼리가 발생하고 종료될 때, 소켓은 TIME_WAIT 상태에 머무른다. 이는 가용 포트의 고갈을 유발할 수 있다.

# 4. TIME_WAIT 소켓 재사용
# TIME_WAIT 상태의 소켓을 새로운 연결에 재사용하도록 허용한다.
# tcp_tw_recycle 은 최신 커널에서 제거되었거나 NAT 환경에서 문제를 일으키므로 tcp_tw_reuse 만 사용한다.
net.ipv4.tcp_tw_reuse = 1

수정된 내용을 적용하기 위해 다음 명령어를 실행한다.

sudo sysctl -p
sudo systemctl restart bind9

테스트 및 검증

튜닝의 효과를 검증하기 위해 dig를 사용하여 강제로 TCP 쿼리를 전송하고, netstat으로 소켓 상태를 모니터링한다.

테스트 시나리오: 부하 테스트 도구(예: dnsperf)를 사용하여 TCP 모드로 대량의 쿼리를 발송한다.

# TCP 모드로 쿼리 전송 테스트
dig @localhost example.com +tcp

# 소켓 상태 및 드랍된 패킷 확인
netstat -s | grep "listen queue"

결과 분석

  • 튜닝 전: SYNs to LISTEN sockets dropped 카운트가 지속적으로 증가하며, 클라이언트 측에서 connection timed out 발생.
  • 튜닝 후: 동시 접속자가 2배 이상 증가했음에도 백로그 큐 오버플로우가 발생하지 않으며, 안정적인 응답 속도 유지.

잠재적 위험 및 베스트 프랙티스

성능 튜닝은 항상 트레이드오프(Trade-off)가 존재한다.

  • 메모리 과사용: TCP 버퍼와 큐 크기를 늘리는 것은 커널 메모리 사용량 증가로 이어진다. 물리 메모리가 부족한 상황에서 과도한 튜닝은 OOM(Out of Memory) 킬러에 의해 BIND 프로세스가 종료되는 원인이 될 수 있다.
  • 보안 고려사항: tcp_max_syn_backlog 를 무작정 늘리는 것은 SYN Flood 공격에 대한 완충지대를 제공하지만, 근본적인 해결책은 아니다. 방화벽(iptables, ufw) 수준에서의 Rate Limiting이 반드시 병행되어야 한다.
  • 점진적 적용: 모든 값은 운영 환경에 바로 적용하기보다, 스테이징 환경에서 부하 테스트를 거쳐 점진적으로 늘려가는 것이 베스트 프랙티스다.

결론

DNS 네임서버의 TCP 튜닝은 단순한 설정값 변경이 아니라, TCP/IP 프로토콜의 동작 원리를 이해하고 하드웨어 리소스와의 균형을 맞추는 과정이었다.. 대학 시절 배운 '3-way Handshake'와 'Sliding Window' 개념이 실제 대규모 트래픽 처리에 어떻게 적용되는지 확인할 수 있는 좋은 사례였다. 안정적인 서비스 운영을 위해 공식 문서와 커널 문서를 잘 봐야한다.

참고자료

'Computer Science > Network' 카테고리의 다른 글

[Network] HTTP 1.1 vs HTTP 2.0 : 구조적 한계  (0) 2025.05.13