고가용성을 위한 Nginx 로드 밸런싱 전략

Nginx 로드 밸런싱을 통해 웹 애플리케이션의 고가용성을 달성하는 방법을 알아보세요. 이 가이드는 라운드 로빈, 가중치 라운드 로빈, 최소 연결, IP 해시를 포함한 필수 Nginx 로드 밸런싱 전략을 다룹니다. 실용적인 구성 예제를 살펴보고, 상태 확인 메커니즘을 이해하며, 다양한 트래픽 부하에서 애플리케이션의 접근성과 성능을 유지하기 위한 모범 사례를 구현하세요.

고가용성을 위한 Nginx 로드 밸런싱 전략

Nginx 로드 밸런싱은 일반적으로 첫 번째 고통스러운 한계 이후에 도입됩니다. 하나의 애플리케이션 서버가 너무 바쁘거나, 유지보수가 필요하거나, 전체 사이트를 마비시키는 방식으로 장애가 발생하는 경우입니다. 여러 백엔드 앞에 Nginx를 배치하면 요청을 분산하고, 서버를 드레이닝하며, 일반적인 장애에서 살아남을 수 있는 여유를 얻을 수 있습니다.

그 자체로 마법 같은 고가용성은 아닙니다. 오픈 소스 Nginx는 연결 실패 후 백엔드로 트래픽을 보내지 않을 수 있지만, 체크아웃 페이지, API 종속성 또는 데이터베이스 연결이 정상인지 깊이 이해하지는 못합니다. 좋은 설정은 Nginx 업스트림 구성, 적절한 타임아웃, 애플리케이션 수준의 상태 엔드포인트, 외부 모니터링, 그리고 잘못된 백엔드를 신속하게 제거할 수 있는 배포 프로세스를 결합합니다.

로드 밸런싱 이해하기

핵심적으로 로드 밸런싱은 클라이언트 요청을 서버 풀에 지능적으로 전달하는 것입니다. 단일 서버가 모든 트래픽을 처리하는 대신 여러 서버가 협력하여 작동합니다. 이는 몇 가지 주요 이점을 제공합니다.

  • 고가용성: 하나의 서버가 감지 가능한 방식으로 장애가 발생하면 다른 서버가 계속 요청을 처리할 수 있습니다.
  • 확장성: 트래픽이 증가하면 풀에 서버를 더 추가하여 부하를 처리할 수 있습니다.
  • 성능: 트래픽을 분산하면 단일 서버가 과부하되는 것을 방지하여 응답 시간을 단축할 수 있습니다.
  • 신뢰성: 단일 장애 지점을 제거하면 애플리케이션이 더욱 강력해집니다.

Nginx는 로드 밸런싱 설정에서 리버스 프록시 역할을 합니다. 들어오는 클라이언트 요청을 수신하고 구성된 알고리즘에 따라 사용 가능한 백엔드 서버 중 하나로 전달합니다. 또한 백엔드 서버로부터 응답을 수신하여 클라이언트에게 다시 보내므로 최종 사용자에게는 프로세스가 투명하게 보입니다.

Nginx 로드 밸런싱 지시어

Nginx는 구성 파일(일반적으로 nginx.conf 또는 여기에서 포함된 파일) 내에서 특정 지시어를 사용하여 업스트림 서버 그룹과 로드 밸런싱 동작을 정의합니다.

upstream 블록

upstream 블록은 Nginx가 트래픽을 분산할 서버 그룹을 정의하는 데 사용됩니다. 이 블록은 일반적으로 http 컨텍스트에 배치됩니다.

http {
    upstream my_backend_servers {
        # 서버 구성이 여기에 위치합니다
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://my_backend_servers;
        }
    }
}

upstream 블록 내에서 server 지시어를 사용하여 백엔드 서버를 나열하고 IP 주소 또는 호스트 이름과 포트를 지정합니다.

upstream my_backend_servers {
    server backend1.example.com;
    server backend2.example.com;
    server 192.168.1.100:8080;
}

proxy_pass 지시어

location 블록 내에서 사용되는 proxy_pass 지시어는 정의한 upstream 그룹을 가리킵니다. 그러면 Nginx는 구성된 로드 밸런싱 알고리즘을 사용하여 각 요청에 대해 이 그룹에서 서버를 선택합니다.

Nginx 로드 밸런싱 알고리즘

Nginx는 각각 고유한 트래픽 분산 방식을 가진 여러 로드 밸런싱 알고리즘을 지원합니다. 기본 알고리즘은 라운드 로빈입니다.

1. 라운드 로빈 (기본값)

라운드 로빈에서 Nginx는 upstream 그룹의 각 서버에 순차적으로 요청을 분산합니다. 시간이 지남에 따라 각 서버는 동일한 부하를 받습니다. 동일한 서버에 대해 간단하고 효과적이며 가장 일반적으로 사용되는 방법입니다.

구성:

upstream my_backend_servers {
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

장점:

  • 구현 및 이해가 간단합니다.
  • 서버 용량이 비슷하면 부하를 고르게 분산합니다.

단점:

  • 서버 부하나 응답 시간을 고려하지 않습니다. 느린 서버가 계속 요청을 받을 수 있습니다.

2. 가중치 라운드 로빈

가중치 라운드 로빈을 사용하면 각 서버에 가중치를 할당할 수 있습니다. 가중치가 높은 서버는 비례적으로 더 많은 트래픽을 받습니다. 이는 서버 용량이 다른 경우(예: 더 강력한 하드웨어)에 유용합니다.

구성:

upstream my_backend_servers {
    server backend1.example.com weight=3;
    server backend2.example.com weight=1;
}

이 예에서 backend1.example.combackend2.example.com보다 세 배 더 많은 요청을 받습니다.

장점:

  • 서버 용량에 따른 밸런싱을 허용합니다.

단점:

  • 여전히 실시간 서버 부하를 고려하지 않습니다.

3. 최소 연결

최소 연결 알고리즘은 활성 연결이 가장 적은 서버로 요청을 전달합니다. 이 방법은 각 서버의 현재 부하를 고려하기 때문에 더 동적입니다.

구성:

최소 연결을 활성화하려면 upstream 블록에 least_conn 매개변수를 추가하기만 하면 됩니다.

upstream my_backend_servers {
    least_conn;
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

장점:

  • 현재 서버 부하를 고려하여 더 지능적으로 부하를 분산합니다.
  • 연결 시간이 다양한 애플리케이션에 적합합니다.

단점:

  • 연결 수가 급격하게 변동하는 경우 관리가 약간 더 복잡할 수 있습니다.

4. IP 해시

IP 해시를 사용하면 Nginx는 클라이언트 IP 주소의 해시를 기반으로 요청을 처리할 서버를 결정합니다. 이렇게 하면 동일한 클라이언트 IP 주소의 요청이 항상 동일한 백엔드 서버로 전송됩니다. 이는 공유 세션 스토리지를 사용하지 않고 세션 지속성(고정 세션)에 의존하는 애플리케이션에 중요합니다.

구성:

upstream 블록에 ip_hash 매개변수를 추가합니다.

upstream my_backend_servers {
    ip_hash;
    server backend1.example.com;
    server backend2.example.com;
}

장점:

  • 즉시 세션 지속성을 제공합니다.

단점:

  • 많은 클라이언트가 단일 IP 주소를 공유하는 경우(예: NAT 게이트웨이 뒤) 부하 분산이 고르지 않을 수 있습니다.
  • 서버에 장애가 발생하면 해당 서버로 해시된 모든 클라이언트가 서버가 다시 온라인 상태가 되거나 해시가 다시 계산될 때까지 영향을 받습니다(Nginx가 다시 라우팅을 시도하지만).

5. 일반 해시

IP 해시와 유사하게 일반 해시를 사용하면 해싱 키를 지정할 수 있습니다. 이 키는 $request_id, $cookie_jsessionid와 같은 변수 또는 변수 조합이 될 수 있습니다. 이는 세션 지속성 또는 특정 요청 속성에 따른 라우팅에 더 많은 유연성을 제공합니다.

구성:

upstream my_backend_servers {
    hash $remote_addr consistent;
    server backend1.example.com;
    server backend2.example.com;
}

hash와 함께 consistent를 사용하면 일관된 해싱을 구현하여 서버 세트가 변경될 때 키 재분배를 최소화합니다.

장점:

  • 사용자 정의 라우팅 로직에 매우 유연합니다.
  • 서버 변경 중 더 나은 안정성을 위해 일관된 해싱을 지원합니다.

단점:

  • 해싱 키를 신중하게 선택해야 합니다.

상태 확인 및 서버 상태

유용한 고가용성을 위해 Nginx는 장애가 발생한 백엔드를 피해야 합니다. 오픈 소스 버전은 주로 수동적으로 이를 수행합니다. 실제 트래픽을 프록시하는 동안 실패한 시도를 감지합니다. 이는 죽은 호스트, 연결 거부 및 일부 시간 초과 사례에 도움이 됩니다. 사용자가 서비스에 도달하기 전에 몇 초마다 /healthz를 호출하는 활성 상태 확인과는 다릅니다.

max_failsfail_timeout

upstream 블록 내의 server 지시어에 추가되는 이러한 매개변수는 Nginx가 실패한 서버를 처리하는 방법을 제어합니다.

  • max_fails: 지정된 fail_timeout 기간 내에 서버와 통신하려는 실패한 시도 횟수입니다. max_fails 실패 후 서버는 사용 불가능으로 표시됩니다.
  • fail_timeout: 서버가 사용 불가능으로 간주되는 기간입니다. 이 기간 후 Nginx는 상태를 다시 확인하려고 시도합니다.

구성:

upstream my_backend_servers {
    server backend1.example.com max_fails=3 fail_timeout=30s;
    server backend2.example.com max_fails=3 fail_timeout=30s;
}

이 예에서 backend1.example.com이 실패 창 내에서 세 번의 실패한 시도를 하면 Nginx는 일시적으로 이를 피합니다. 시간 초과 후 Nginx는 다시 시도할 수 있습니다. 실패는 추가 도구나 Nginx Plus 기능을 사용하지 않는 한 사용자 정의 애플리케이션 상태 응답이 아닌 연결/프록시 시도를 기반으로 합니다.

backup 매개변수

backup 매개변수는 서버를 백업으로 지정합니다. upstream 그룹의 다른 모든 활성 서버를 사용할 수 없는 경우에만 트래픽을 받습니다.

구성:

upstream my_backend_servers {
    server backend1.example.com;
    server backend2.example.com;
    server backup.example.com backup;
}

backend1backend2가 다운되면 backup.example.com이 대신합니다.

Nginx Plus 상태 확인

상용 버전인 Nginx Plus에는 내장된 활성 상태 확인 기능이 포함되어 있습니다. 주기적으로 백엔드에 요청을 보내고 응답을 평가하며 사용자 트래픽이 라우팅되기 전에 비정상 서버를 제거할 수 있습니다. 오픈 소스 Nginx를 사용하는 경우에도 견고한 시스템을 구축할 수 있지만 일반적으로 업스트림 대상을 편집/제거하는 외부 모니터링, 서비스 검색 또는 자동화와 함께 사용합니다.

실용적인 구성 예제

이러한 개념을 일반적인 시나리오에 적용해 보겠습니다.

시나리오 1: 간단한 라운드 로빈 로드 밸런싱

두 개의 동일한 웹 서버에 트래픽을 분산합니다.

구성:

http {
    upstream web_servers {
        server 10.0.0.10;
        server 10.0.0.11;
    }

    server {
        listen 80;
        server_name yourdomain.com;

        location / {
            proxy_pass http://web_servers;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

설명:

  • upstream web_servers: web_servers라는 그룹을 정의합니다.
  • server 10.0.0.10;server 10.0.0.11;: 백엔드 서버를 지정합니다.
  • proxy_pass http://web_servers;: 트래픽을 web_servers 업스트림 그룹으로 전달합니다.
  • proxy_set_header: 이러한 지시어는 원래 클라이언트 정보를 백엔드 서버에 전달하는 데 중요하며, 로깅 또는 애플리케이션 로직에 필요한 경우가 많습니다.

시나리오 2: 세션 지속성을 사용한 로드 밸런싱 (IP 해시)

사용자가 동일한 백엔드 서버에 계속 연결되도록 하여 세션 데이터를 로컬에 저장하는 애플리케이션에 유용합니다.

이 기능은 절충점을 이해하는 경우에만 사용하십시오. 많은 사용자가 동일한 사무실 NAT, 모바일 캐리어 게이트웨이 또는 회사 프록시를 통해 오는 경우 IP 해시는 하나의 백엔드에 너무 많은 트래픽을 보낼 수 있습니다. 공유 세션 스토리지, 서명된 상태 비저장 쿠키 또는 애플리케이션 수준의 세션 복제는 클라이언트 IP 고정에 의존하는 것보다 더 깔끔한 경우가 많습니다.

구성:

http {
    upstream app_servers {
        ip_hash;
        server 192.168.1.50:8000;
        server 192.168.1.51:8000;
    }

    server {
        listen 80;
        server_name api.yourdomain.com;

        location / {
            proxy_pass http://app_servers;
            # ... 다른 proxy_set_header 지시어 ...
        }
    }
}

시나리오 3: 장애 조치가 있는 가중치 로드 밸런싱

더 강력한 서버로 더 많은 트래픽을 보내고 백업을 준비합니다.

구성:

http {
    upstream balanced_app {
        server app_server_1.local weight=5;
        server app_server_2.local weight=2;
        server app_server_3.local backup;
    }

    server {
        listen 80;
        server_name staging.yourdomain.com;

        location / {
            proxy_pass http://balanced_app;
            # ... 다른 proxy_set_header 지시어 ...
        }
    }
}

여기서 app_server_1.local은 트래픽의 5부분을, app_server_2.local은 2부분을 가져가며, app_server_3.local은 다른 두 서버를 사용할 수 없는 경우에만 요청을 처리합니다.

모범 사례 및 팁

  • proxy_set_header 사용: 백엔드 애플리케이션이 원래 클라이언트의 세부 정보를 알 수 있도록 Host, X-Real-IP, X-Forwarded-ForX-Forwarded-Proto와 같은 헤더를 항상 설정하십시오.
  • Nginx 업데이트 유지: 보안 및 성능 향상을 위해 안정적이고 최신 버전의 Nginx를 실행하고 있는지 확인하십시오.
  • 백엔드 서버 모니터링: Nginx의 내부 상태 확인 외에도 외부 모니터링 도구를 구현하십시오. Nginx는 서버에 도달할 수 있는지만 알 수 있으며 서버 에서 애플리케이션이 제대로 작동하는지 반드시 알 수 있는 것은 아닙니다.
  • Nginx Plus 고려: 미션 크리티컬 애플리케이션의 경우 Nginx Plus는 활성 상태 확인, 세션 지속성 및 실시간 활동 모니터링과 같은 고급 기능을 제공하여 관리를 단순화하고 복원력을 향상시킬 수 있습니다.
  • DNS 로드 밸런싱: 지역 간 또는 여러 Nginx 진입점 간 트래픽 분산을 위해 DNS가 도움이 될 수 있지만 DNS 장애 조치는 해석기 동작 및 TTL에 따라 달라집니다. 이를 즉각적인 장애 조치로 취급하지 마십시오.
  • SSL 종료: 로드 밸런서(Nginx)에서 SSL을 종료하여 백엔드 서버의 SSL 처리를 오프로드할 수 있습니다.

실용적인 시작점

두세 개의 동일한 앱 서버의 경우 일반 라운드 로빈, 보수적인 프록시 시간 초과 및 명확한 업스트림 로깅으로 시작하십시오. max_failsfail_timeout을 추가한 다음 하나의 백엔드를 중지했을 때 어떤 일이 발생하는지 테스트하십시오. 실제 사고가 발생할 때까지 기다리지 말고 Nginx가 어떻게 작동하는지 배우십시오.

요청 시간이 매우 다른 경우 least_conn을 시도하십시오. 하나의 서버가 다른 서버보다 크면 가중치를 사용하십시오. 애플리케이션이 세션 상태를 로컬에 저장하는 경우 가능하면 세션 설계를 수정하십시오. 실용적인 브리지가 필요한 경우에만 ip_hash를 사용하십시오.

최고의 Nginx 로드 밸런싱 전략은 애플리케이션이 실패하는 방식과 일치하는 전략입니다. 죽은 VM, 느린 백엔드, 손상된 릴리스 및 데이터베이스 중단은 모두 프록시의 관점에서 다르게 보입니다. 알고리즘을 구성한 다음 설정을 고가용성이라고 부르기 전에 작은 테스트를 통해 장애 동작을 증명하십시오.