NGINX 및 NGINX Plus에서 임시 포트 고갈 문제 해결

NGINX | 2016년 4월 19일

NGINX와 NGINX Plus는 매우 강력한 HTTP, TCP, UDP 로드 밸런서입니다. 대량의 요청을 한꺼번에 프록시하고 수많은 동시 연결을 유지하는 데 매우 효율적입니다. 하지만 이러한 특성 때문에 NGINX와 NGINX Plus는 특히 임시 포트 고갈이 발생하기 쉽습니다. 이는 새 로컬 소켓을 설정하는 데 할당된 포트 번호가 OS에서 모두 소진되어 새 연결을 생성할 수 없는 상태를 말합니다. (임시 포트 고갈은 두 제품 모두에 해당하지만, 간결한 설명을 위해 이 블로그의 나머지 부분에서는 NGINX Plus만 언급하겠습니다.)

이 블로그에서는 TCP 연결의 구성 요소와 연결이 설정되기 전에 그 내용이 어떻게 결정되는지 살펴봅니다. 그런 다음 NGINX Plus가 임시 포트 고갈의 영향을 받고 있는지 확인하는 방법을 설명합니다. 마지막으로 Linux 커널 조정과 NGINX Plus 지시어를 모두 활용하여 이러한 제약을 해결하는 전략을 알아봅니다.

네트워크 소켓 간략히 살펴보기

TCP를 통해 연결이 설정되면 로컬 호스트와 원격 호스트 양쪽에 소켓이 생성됩니다. 그런 다음 이 소켓들이 서로 연결되어 소켓 쌍을 이루며, 이 소켓 쌍은 로컬 IP 주소와 포트, 원격 IP 주소와 포트로 구성된 고유한 4‑튜플로 표현됩니다.

원격 IP 주소와 포트는 연결의 서버 측에 속하며, 클라이언트가 연결을 시작하기 전에 미리 확인해야 합니다. 대부분의 경우 클라이언트는 연결에 사용할 로컬 IP 주소를 자동으로 선택하지만, 연결을 설정하는 소프트웨어가 이를 선택하기도 합니다. 마지막으로 로컬 포트는 운영 체제에서 제공하는 지정된 범위 내에서 무작위로 선택됩니다. 이 포트는 연결이 유지되는 동안에만 클라이언트에 할당되므로 임시 포트라고 합니다. 연결이 종료되면 임시 포트를 다시 사용할 수 있습니다.

임시 포트 고갈 인식하기

소개에서 언급했듯이 NGINX Plus는 특성상 임시 포트 고갈과 그로 인한 문제의 영향을 받을 수 있습니다. NGINX Plus가 요청을 업스트림 서버로 프록시할 때는 위에서 설명한 소켓 생성 과정에서 클라이언트 역할을 하며, 기본적으로 프록시된 요청의 소켓을 NGINX Plus가 실행 중인 호스트의 로컬 IP 주소와 사용 가능한 임시 포트에 자동으로 바인딩합니다. 연결 속도가 높아 새로 설정되는 소켓이 기존의 열린 소켓이 닫히는 속도보다 더 빠르게 대기 상태로 전환되면, 결국 사용 가능한 포트가 고갈되어 새 소켓을 생성할 수 없게 됩니다. 이로 인해 운영 체제와 NGINX Plus 모두에서 오류가 발생합니다. 다음은 NGINX Plus 오류 로그에 기록되는 오류의 예입니다.

2016/03/18 09:08:37 [crit] 1888#1888: *13 connect() to 10.2.2.77:8081 failed (99: Cannot assign requested address) while connecting to upstream, client: 10.2.2.42, server: , request: "GET / HTTP/1.1", upstream: "http://10.2.2.77:8081/", host: "10.2.2.77"

포트 고갈은 업스트림 서버가 아닌 NGINX Plus에서 발생하는 500 오류도 급증시킵니다. 다음은 NGINX Plus 액세스 로그의 예시 항목입니다.

10.2.2.42 - - [18/Mar/2016:09:14:20 -0700] "GET / HTTP/1.1" 500 192 "-" "curl/7.35.0"

NGINX Plus 서버에서 TIME-WAIT 상태인 소켓 수를 확인하려면 Linux 셸에서 다음 ss 명령을 실행합니다. 이 예에서는 TIME-WAIT 상태로 열린 소켓이 143개임을 보여 줍니다. 이 예에서는 wc 명령을 사용하여 명령 출력의 줄 수를 계산합니다.

# ss -a | grep TIME-WAIT | wc -l143

사용 가능한 임시 포트 수를 늘리기 위한 커널 튜닝

임시 포트 고갈을 줄이는 한 가지 방법은 Linux 커널의 net.ipv4.ip_local_port_range 설정을 사용하는 것입니다. 기본 범위는 일반적으로 32768부터 61000까지입니다.

임시 포트가 부족해지고 있다면 범위를 기본값에서 1024~65000으로 변경하여 사용 가능한 임시 포트 수를 두 배로 늘릴 수 있습니다. 커널 설정 변경에 대한 자세한 내용은 성능 향상을 위한 NGINX 튜닝 블로그 게시물을 참조하세요.

NGINX Plus에서 Keepalive 연결 활성화

임시 포트 고갈을 줄이는 또 다른 방법은 NGINX Plus와 업스트림 서버 간에 연결 유지를 활성화하는 것입니다. 가장 단순한 HTTP 구현에서는 클라이언트가 새 연결을 열고 요청을 전송한 다음 응답을 읽고, 마지막으로 관련 리소스를 해제하기 위해 연결을 닫습니다.

클라이언트가 응답을 읽은 후에도 keepalive 연결은 열린 상태로 유지되므로 이후 요청에 재사용할 수 있습니다.

Use the keepalive 지시어를 사용하여 NGINX Plus에서 업스트림 서버로의 keepalive 연결을 활성화하고, 각 워커 프로세스의 캐시에 유지할 업스트림 서버 대상 유휴 keepalive 연결의 최대 개수를 정의합니다. 이 개수를 초과하면 가장 오래 사용되지 않은 연결이 닫힙니다. keepalive를 사용하지 않으면 오버헤드가 증가하고 연결과 임시 포트가 모두 비효율적으로 사용됩니다.

다음 샘플 구성은 NGINX Plus가 upstream 블록에 정의된 서버에 대해 최소 128개의 keepalive 연결을 유지하도록 지정합니다. 이 블록의 이름은 backend입니다.

upstream backend {    server 10.0.0.100:1234;
    server 10.0.0.101:1234;
    keepalive 128;
}

업스트림 서버에 대한 keepalive 연결을 활성화할 때는 NGINX Plus가 HTTP 버전 1.1을 사용하도록 지정하기 위해 proxy_http_version 지시어도 사용해야 하며, 이름이 proxy_set_header인 헤더를 모두 제거하려면 Connection 지시어를 사용해야 합니다. 두 지시어 모두 http, server, 또는 location 구성 블록에 배치할 수 있습니다.

proxy_http_version 1.1; proxy_set_header Connection "";

정의된 로컬 IP 주소 목록에 연결을 동적으로 바인딩하기

커널을 최적화하고 keepalive 연결을 활성화하면 임시 포트의 가용성과 사용을 훨씬 더 세밀하게 제어할 수 있지만, 이러한 변경만으로는 임시 포트의 과도한 사용을 해결하기에 충분하지 않은 경우도 있습니다. 이 경우 NGINX Plus의 몇 가지 지시문과 매개변수를 사용하여 연결의 일정 비율을 정적 로컬 IP 주소에 바인딩할 수 있습니다. 이렇게 하면 사용 가능한 임시 포트 수가 구성에 정의된 IP 주소 수만큼 실질적으로 늘어납니다. 이를 위해 proxy_bind 및 split_clients 지시문을 사용합니다.

아래 예제 구성에서 proxy_bind 지시어는 location 블록에서 각 요청의 로컬 IP 주소를 $split_ip 변수 값에 따라 설정합니다. 이 변수는 split_clients 블록에서 생성된 값으로 동적으로 설정되며, 이 블록은 해시 함수를 사용하여 해당 값을 결정합니다.

The first parameter to the split_clients 지시문의 첫 번째 매개변수는 각 요청에서 "$remote_addr$remote_port" 문자열을 MurmurHash2 함수로 해싱한 값입니다. 두 번째 매개변수인 $split_ip는 첫 번째 매개변수의 해시를 기반으로 동적으로 설정하는 변수입니다.

중괄호 안의 문은 해시 테이블을 여러 “버킷”으로 나누며, 각 버킷에는 전체 해시 중 일정 비율이 포함됩니다. 여기서는 테이블을 크기가 같은 10개의 버킷으로 나누지만, 버킷은 원하는 수만큼 만들 수 있으며 크기가 모두 같을 필요도 없습니다. (해시 수가 지정된 비율에 따라 균등하게 나누어지지 않을 수 있으므로, 마지막 버킷의 비율은 항상 특정 숫자 대신 별표 [*]로 표시합니다.)

가능한 해시 값의 범위는 0부터 4294967295까지이므로, 이 경우 각 버킷에는 전체의 10%인 약 429496700개의 값이 포함됩니다. 첫 번째 버킷에는 0부터 429496700까지의 값이, 두 번째 버킷에는 429496701부터 858993400까지의 값이 포함되며, 이후에도 같은 방식으로 이어집니다. $split_ip 변수는 $remote_addr$remote_port 문자열의 해시가 포함된 버킷과 연결된 IP 주소로 설정됩니다. 구체적인 예로, 해시 값 150000000은 네 번째 버킷에 속하므로 이 경우 $split_ip 변수는 동적으로 10.0.0.213으로 설정됩니다.

http {    upstream backend {
        server 10.0.0.100:1234;
        server 10.0.0.101:1234;
 }

    server {
        # ...
        location / {
            # ...
            proxy_pass http://backend;
            proxy_bind $split_ip;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }

    split_clients "$remote_addr$remote_port" $split_ip { 10% 10.0.0.210; 10% 10.0.0.211; 10% 10.0.0.212; 10% 10.0.0.213; 10% 10.0.0.214; 10% 10.0.0.215; 10% 10.0.0.216; 10% 10.0.0.217; 10% 10.0.0.218; * 10.0.0.219; }
}

결론

임시 포트 수에 대한 Linux 커널 설정을 조정하면 가용성과 사용 효율을 높일 수 있습니다. NGINX Plus에서 업스트림 서버로의 연결에 keepalive를 활성화하면 임시 포트를 더 효율적으로 사용할 수 있습니다. 마지막으로 NGINX Plus 구성에서 split_clients 및 proxy_bind 지시어를 사용하면 송신 연결을 미리 정의된 로컬 IP 주소 목록에 동적으로 바인딩할 수 있어 NGINX Plus에서 사용할 수 있는 임시 포트 수가 크게 늘어납니다.

NGINX Plus를 사용해 보려면 지금 30일 무료 체험을 시작하거나 사용 사례에 대해 논의하려면 문의해 주세요.


공유

작성자 소개

관련 블로그 게시물

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
NGINX | 2023. 11. 30.

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric

이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
NGINX | 2023. 03. 23.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법

앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
NGINX | 2021. 09. 27.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션

개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX | 2021. 07. 06.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법

NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX | 2021. 06. 30.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리

NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
NGINX | 2015. 03. 25.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유

Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.