NGINX Plus

TCP 및 UDP Load Blancning

이번 장에서는 NGINX Plus 와 NGINX Open Source를 사용하여 TCP 및 UDP 트래픽을 Proxy하고 Load Blancning하는 방법을 설명합니다.

목차

1. 소개
2. 전제 조건
3. Reverse Proxy 구성
4. TCP 및 UDP Load Balancing 구성
5. Health Check 구성
6. 실시간 구성
6-1. 실시간 구성의 예시
7. TCP 및 UDP Load Balancing 구성 예시

1. 소개

Load Balancing은 여러 Backend 서버에 네트워크 트래픽을 효율적으로 분산하는 것을 말합니다.

NGINX Plus 릴리스 5 이상에서 NGINX Plus는 TCP((Transmission Control Protocol) 트래픽을 Proxy하고 Load Balancing할 수 있습니다. TCP는 LDAP, MySQL, RTMP와 같이 널리 사용되는 많은 애플리케이션 및 서비스의 프로토콜입니다.

NGINX Plus 릴리스 9 이상에서 NGINX Plus는 UDP 트래픽을 Proxy하고 Load Balancing할 수 있습니다. UDP( User Datagram Protocol)는 DNS, syslog, RADIUS와 같이 널리 사용되는 많은 비트랜잭션 애플리케이션의 프로토콜입니다.

HTTP 트래픽의 Load Balancing을 설정하려면 HTTP Load Balancing 문서를 참조하세요.

2. 전제 조건

  • 최신 NGINX Plus (추가 빌드 단계 필요 없음) 또는 --with-stream 구성 플래그를 사용하여 빌드된 최신 NGINX Open Source를 사용합니다.
  • TCP 또는 UDP를 통해 통신하는 애플리케이션, 데이터베이스 또는 서비스
  • 애플리케이션, 데이터베이스 또는 서비스의 동일한 인스턴스를 각각 실행하는 Upstream 서버

3. Reverse Proxy 구성

먼저, Reverse Proxy를 구성하여 NGINX Plus 또는 NGINX Open Source가 클라이언트에서 Upstream 그룹 또는 Proxy 서버로 TCP 연결 또는 UDP 데이터그램을 전달할 수 있도록 해야 합니다.

NGINX 구성 파일을 열고 다음 단계를 수행합니다.

1. 최상위 stream { } 블록을 생성합니다.

stream {
    # ...
}

2. 최상위 stream { } 컨텍스트에서 각 가상 서버에 대해 하나 이상의 server { } 구성 블록을 정의합니다.

3. 각 서버에 대한 server { } 구성 블록 내에 서버가 수신 대기하는 IP 주소 및/또는 포트를 정의하는 listen 지시문을 포함합니다.

UDP 트래픽의 경우 udp 매개변수도 포함하세요. TCP가 stream 컨텍스트의 기본 프로토콜이므로 listen 지시문에 tcp 매개변수가 없습니다.

stream {

    server {
        listen 12345;
        # ...
    }

    server {
        listen 53 udp;
        # ...
    }
    # ...
}

4. Proxy된 서버 또는 서버가 트래픽을 전달할 Upstream 그룹을 정의하려면 proxy_pass 지시문을 포함하세요.

stream {

    server {
        listen     12345;
        #TCP traffic will be forwarded to the "stream_backend" upstream group
        proxy_pass stream_backend;
    }

    server {
        listen     12346;
        #TCP traffic will be forwarded to the specified server
        proxy_pass backend.example.com:12346;
    }

    server {
        listen     53 udp;
        #UDP traffic will be forwarded to the "dns_servers" upstream group
        proxy_pass dns_servers;
    }
    # ...
}

5. Proxy 서버에 여러 네트워크 인터페이스가 있는 경우 Upstream 서버에 연결할 때 특정 소스 IP 주소를 사용하도록 NGINX를 선택적으로 구성할 수 있습니다. 이 옵션은 NGINX 뒤에 있는 Proxy 서버가 특정 IP 네트워크 또는 IP 주소 범위의 연결을 허용하도록 구성된 경우에 유용할 수 있습니다.

proxy_bind 지시문과 적절한 네트워크 인터페이스의 IP 주소를 포함하세요.

stream {
    # ...
    server {
        listen     127.0.0.1:12345;
        proxy_pass backend.example.com:12345;
        proxy_bind 127.0.0.1:12345;
    }
}

6. 선택 사항으로 NGINX가 클라이언트 및 업스트림 연결 모두의 데이터를 저장할 수 있는 두 개의 내부 메모리 버퍼의 크기를 조정할 수 있습니다. 데이터 양이 적은 경우 버퍼를 줄여 메모리 리소스를 절약할 수 있습니다. 데이터의 양이 많으면 버퍼 크기를 늘려 소켓 읽기/쓰기 작업 횟수를 줄일 수 있습니다. 한 연결에서 데이터가 수신되는 즉시 NGINX는 데이터를 읽고 다른 연결을 통해 전달합니다. 버퍼는 proxy_buffer_size 지시문으로 제어합니다.

stream {
    # ...
    server {
        listen            127.0.0.1:12345;
        proxy_pass        backend.example.com:12345;
        proxy_buffer_size 16k;
    }
}

4. TCP 및 UDP Load Balancing 구성

Load Balancing을 구성하려면.

1. 서버 그룹 또는 트래픽이 Load Balancing Upstream 그룹을 생성합니다. 최상위 stream { } 컨텍스트에서 하나 이상의 upstream { } 구성 블록을 정의하고 Upstream 그룹의 이름(예: TCP 서버의 경우 stream_backend, UDP 서버의 경우 dns_servers)을 설정합니다.

stream {

    upstream stream_backend {
        # ...
    }

    upstream dns_servers {
        # ...
    }

    # ...
}

Reverse Proxy에 대해 위에서 구성한 것과 같이 proxy_pass 지시문으로 Upstream 그룹의 이름이 참조되는지 확인하세요.

2. Upstream 그룹을 Upstream 서버로 채웁니다. upstream { } 블록 내에 각 Upstream 서버에 대한 server 지시문을 추가하여 해당 서버의 IP 주소 또는 호스트 명(여러 IP 주소로 확인할 수 있는 경우)과 필수 포트 번호를 지정합니다. 각 서버에 대한 프로토콜은 이전에 만든 server 블록의 listen 지시문에 포함된 매개변수에 의해 전체 Upstream 그룹에 대해 정의되므로 각 서버에 대한 프로토콜을 정의하지 않는다는 점에 유의하세요.

stream {

    upstream stream_backend {
        server backend1.example.com:12345;
        server backend2.example.com:12345;
        server backend3.example.com:12346;
        # ...
    }

    upstream dns_servers {
        server 192.168.136.130:53;
        server 192.168.136.131:53;
        # ...
    }

    # ...
}

3. Upstream 그룹에서 사용하는 Load Balancing 방법을 구성합니다. 다음 방법 중 하나를 지정할 수 있습니다.

  • Round Robin – 기본적으로 NGINX는 Round Robin 알고리즘을 사용하여 트래픽을 Load Balancing하고 구성된 Upstream 그룹에 있는 서버로 순차적으로 트래픽을 보냅니다. 기본 방법이기 때문에 round-robin 지시문은 없으며, 최상위 stream { } 컨텍스트에서 upstream { } 구성 블록을 생성하고 이전 단계에서 설명한 대로 server 지시문을 추가하기만 하면 됩니다.
  • Least Connections – NGINX는 현재 활성화된 연결 수가 적은 서버를 선택합니다.
  • Least Time (NGINX Plus만 해당) – NGINX Plus는 평균 지연 시간이 가장 낮고 활성화된 연결 수가 가장 적은 서버를 선택합니다. 가장 낮은 평균 지연 시간을 계산하는 데 사용되는 방법은 least_time 지시문에 포함된 다음 매개변수에 따라 다릅니다.
    • connect – Upstream 서버에 연결하는 시간
    • first_byte – 첫 번째 Byte의 데이터를 전송받는 시간
    • last_byte – 서버로부터 전체 응답을 받는 데 걸리는 시간
upstream stream_backend {
    least_time first_byte;
    server backend1.example.com:12345;
    server backend2.example.com:12345;
    server backend3.example.com:12346;
}
  • Hash – NGINX는 사용자 정의 키(예: 소스 IP 주소($remote_addr))를 기반으로 서버를 선택합니다.
upstream stream_backend {
    hash $remote_addr;
    server backend1.example.com:12345;
    server backend2.example.com:12345;
    server backend3.example.com:12346;
}

Hash Load Balancing 방법은 세션 지속성을 구성하는 데도 사용됩니다. 해시 함수는 클라이언트 IP 주소를 기반으로 하므로 서버가 중단되거나 다른 방법으로 사용할 수 없는 경우를 제외하고 특정 클라이언트의 연결은 항상 동일한 서버로 전달됩니다. 선택적 consistent 매개변수를 지정하여 ketama 일관된 해싱 방법을 적용합니다.

hash $remote_addr consistent;
  • Random – 각 연결은 무작위로 선택된 서버로 전달됩니다. 2 개의 매개변수를 지정하면 먼저 NGINX가 서버 가중치를 고려하여 두 개의 서버를 무작위로 선택한 다음 지정된 방법을 사용하여 이 서버 중 하나를 선택합니다.
    • least_conn – 활성화된 연결 수가 가장 적은 경우
    • least_time=connect (NGINX Plus) – Upstream 서버에 연결하는 시간($upstream_connect_time)
    • least_time=first_byte (NGINX Plus) – 서버로부터 첫 번째 Byte의 데이터를 수신하는 데 걸리는 최소 평균 시간($upstream_first_byte_time)
    • least_time=last_byte (NGINX Plus) – 서버에서 마지막 데이터 Byte를 수신하는 데 걸리는 최소 평균 시간($upstream_session_time)
upstream stream_backend {
    random two least_time=last_byte;
    server backend1.example.com:12345;
    server backend2.example.com:12345;
    server backend3.example.com:12346;
    server backend4.example.com:12346;
}

여러 Load Balancer가 동일한 Backend 집합에 요청을 전달하는 분산 환경에서는 Random Load Balancing 방법을 사용해야 합니다. Load Balancer가 모든 요청을 전체적으로 파악할 수 있는 환경에서는 Round Robin, Least Connections 및 Least Time과 같은 다른 Load Balancing 방법을 사용하세요.

4. 선택 사항으로 각 Upstream 서버에 대해 최대 연결 수, 서버 가중치 등 각 서버에 맞는 매개변수를 지정할 수 있습니다.

upstream stream_backend {
    hash   $remote_addr consistent;
    server backend1.example.com:12345 weight=5;
    server backend2.example.com:12345;
    server backend3.example.com:12346 max_conns=3;
}
upstream dns_servers {
    least_conn;
    server 192.168.136.130:53;
    server 192.168.136.131:53;
    # ...
}

다른 방법은 Upstream그룹 대신 단일 서버로 트래픽을 Proxy하는 것입니다. 호스트 명으로 서버를 식별하고 여러 IP 주소로 확인하도록 호스트 명을 구성하면 NGINX는 Round Robin 알고리즘을 사용하여 IP 주소 간에 트래픽을 Load Balancing합니다. 이 경우 proxy_pass 지시문에 서버의 포트 번호를 지정해야 하며 IP 주소나 호스트 명 앞에 프로토콜을 지정해서는 안 됩니다:

stream {
    # ...
    server {
        listen     12345;
        proxy_pass backend.example.com:12345;
    }
}

5. Health Check 구성

NGINX는 TCP 또는 UDP Upstream 서버를 지속적으로 테스트하여 장애가 발생한 서버를 피하고 복구된 서버를 Load Balancing 그룹에 정상적으로 추가할 수 있습니다.

6. 실시간 구성

Upstream 서버 그룹은 NGINX Plus REST API를 사용하여 실시간으로 쉽게 재구성할 수 있습니다. 이 인터페이스를 사용하면 Upstream 그룹의 모든 서버 또는 특정 서버를 보고, 서버 매개변수를 수정하고, Upstream 서버를 추가 또는 제거할 수 있습니다.

즉각적인 구성을 활성화하려면.

1. 최상위 http { } 블록을 만들거나 구성에 있는지 확인합니다.

http {
    # ...
}

2. 구성 요청을 위한 location(예: API)을 만듭니다.

http {
    server {
        location /api {
            # ...
        }
    }
}

3. 이 위치에서 api 지시문을 지정합니다.

http {
    server {
        location /api {
            api;
            # ...
        }
    }
}

4. 기본적으로 NGINX Plus API는 데이터에 대한 읽기 전용 액세스를 제공합니다. write=on 매개변수를 사용하면 읽기/쓰기 액세스를 활성화하여 Upstream에 변경을 수행할 수 있습니다.

http {
    server {
        location /api {
            api  write=on;
            # ...
        }
    }
}

5. allowdeny 지시문을 사용하여 이 위치에 대한 액세스를 제한합니다.

http {
    server {
        location /api {
            api   write=on;
            allow 127.0.0.1; # permit access from localhost
            deny  all;       # deny access from everywhere else
        }
    }
}

6. API가 쓰기 모드에서 활성화된 경우 특정 사용자만 PATCH, POST, DELETE 메서드에 액세스할 수 있도록 제한하는 것이 좋습니다. 이는 HTTP 기본 인증을 구현하여 수행할 수 있습니다.

http {
    server {
        location /api {
            limit_except GET {
                auth_basic "NGINX Plus API";
                auth_basic_user_file /path/to/passwd/file;
            }
            api   write=on;
            allow 127.0.0.1;
            deny  all;
        }
    }
}

7. 모든 Worker Process가 동일한 구성을 사용할 수 있도록 Upstream 서버 그룹에 대한 공유 메모리 Zone을 생성합니다. 이렇게 하려면 최상위 stream { } 블록에서 대상 Upstream 서버 그룹을 찾고, 해당 그룹에 zone 지시문을 추가한 다음, Zone 이름(여기서는 stream_backend)과 메모리 용량(64KB)을 지정합니다.

stream {
    upstream stream_backend {
        zone backend 64k;
        # ...
    }
}

6-1. 실시간 구성의 예시

stream {
    # ...
    # Configuration of an upstream server group
    upstream appservers {
        zone appservers 64k;
        server appserv1.example.com:12345 weight=5;
        server appserv2.example.com:12345 fail_timeout=5s;
        server backup1.example.com:12345 backup;
        server backup2.example.com:12345 backup;
    }
    
    server {
        # Server that proxies connections to the upstream group
        proxy_pass appservers;
        health_check;
    }
}
http {
    # ...
    server {
        # Location for API requests
        location /api {
            limit_except GET {
                auth_basic "NGINX Plus API";
                auth_basic_user_file /path/to/passwd/file;
            }
            api write=on;
            allow 127.0.0.1;
            deny  all;
        }
    }
}

여기에서는 로컬 호스트 주소(127.0.0.1)에서만 해당 위치에 대한 액세스가 허용됩니다. 다른 모든 IP 주소에서의 액세스는 거부됩니다.

구성 명령을 NGINX에 전달하려면 curl과 같은 방법으로 API 명령을 보내세요.

예를 들어 서버 그룹에 새 서버를 추가하려면 다음과 같이 POST 요청을 보냅니다.

curl -X POST -d '{ \ 
   "server": "appserv3.example.com:12345", \ 
   "weight": 4 \ 
 }' -s 'http://127.0.0.1/api/6/stream/upstreams/appservers/servers'

서버 그룹에서 서버를 제거하려면 DELETE 요청을 보냅니다.

curl -X DELETE -s 'http://127.0.0.1/api/6/stream/upstreams/appservers/servers/0'

특정 서버의 매개변수를 수정하려면 PATCH 요청을 보냅니다.

curl -X PATCH -d '{ "down": true }' -s 'http://127.0.0.1/api/6/http/upstreams/appservers/servers/0'

7. TCP 및 UDP Load Balancing 구성 예시

다음은 NGINX를 사용한 TCP 및 UDP Load Balancing의 구성 예시입니다.

stream {
    upstream stream_backend {
        least_conn;
        server backend1.example.com:12345 weight=5;
        server backend2.example.com:12345 max_fails=2 fail_timeout=30s;
        server backend3.example.com:12345 max_conns=3;
    }
    
    upstream dns_servers {
        least_conn;
        server 192.168.136.130:53;
        server 192.168.136.131:53;
        server 192.168.136.132:53;
    }
    
    server {
        listen        12345;
        proxy_pass    stream_backend;
        proxy_timeout 3s;
        proxy_connect_timeout 1s;
    }
    
    server {
        listen     53 udp;
        proxy_pass dns_servers;
    }
    
    server {
        listen     12346;
        proxy_pass backend4.example.com:12346;
    }
}

이 예제에서는 HTTP 요청에 대한 설정이 http 블록에서 구성되는 것과 마찬가지로 모든 TCP 및 UDP Proxy 관련 기능이 stream 블록 내부에서 구성됩니다.

두 개의 이름이 지정된 upstream 블록이 있으며, 각 블록에는 서로 동일한 콘텐츠를 호스팅하는 세 개의 서버가 포함되어 있습니다. 각 server의 서버 이름 뒤에는 필수 포트 번호가 있습니다. 연결은 Least Connections Load Balancing 방법에 따라 서버 간에 분산됩니다. 즉, 활성화된 연결 수가 가장 적은 서버로 연결이 연결됩니다.

3 개의 server 블록은 3개의 가상 서버를 정의합니다.

  • 첫 번째 서버는 포트 12345에서 수신 대기하고 모든 TCP 연결을 Upstream 서버의 stream_backend 그룹으로 Proxy합니다. stream 모듈의 컨텍스트에 정의된 proxy_pass 지시문에 프로토콜을 포함하지 않아야 합니다.
    두 개의 선택적 timeout 매개변수를 지정할 수 있습니다. proxy_connect_timeout 지시문은 stream_backend 그룹의 서버와 연결을 설정하는 데 필요한 timeout을 설정합니다. proxy_timeout 지시문은 stream_backend 그룹의 서버 중 하나에 대한 Proxy가 시작된 후 사용되는 timeout을 설정합니다.
  • 두 번째 서버는 포트 53에서 수신 대기하고 모든 UDP 데이터그램(listen 지시문의 udp 매개변수)을 dns_servers라는 Upstream 그룹으로 Proxy합니다. udp 매개변수를 지정하지 않으면 소켓이 TCP 연결을 수신 대기합니다.
  • 세 번째 가상 서버는 포트 12346에서 수신 대기하며, Round Robin 방식으로 Load Balancing된 여러 IP 주소로 확인할 수 있는 backend4.example.com에 TCP 연결을 Proxy합니다.