ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • NGINX 2: 고성능으로 부하 분산하기
    DevOps 2023. 3. 11. 23:51
    반응형

     

     

    인터넷 서비스의 사용자 경험은 높은 성능과 가용성이 필요하다.

    이를 위해 일반적으로 같은 시스템을 여러 대 운영하고 부하를 각 시스템으로 분산한다.

    그리고 부하가 지속적으로 증가할수록 동일한 시스템을 추가로 투입한다.

    이러한 아키텍처 기술을 수평적인 확장(horizontal scailng)이라고 하는데 NGINX는 HTTP, TCP, UDP 프로토콜 레벨에서 각 시스템으로의 부하분산을 지원한다.

     

    현대적인 웹 서비스 아키텍처는 상태 정보를 클라이언트나 서버에 저장하지 않고 공유 메모리나 공유 데이터베이스에 저장하는 stateless 애플리케이션 계층을 채택한다.

    하지만 그 정보가 매우 방대한 경우 데이터를 공유 시스템과 매번 주고받으면 네트워크 부하를 일으켜 성능 저하가 발생할 수 있기 때문에 서버의 로컬 환경에 저장하는 경우도 있다.

    이런 경우 사용자의 요청이 동일한 서버로 전송되어야 하는데 NGINX Plus는 쿠키 값이나 라우팅을 추적해서 문제를 해결한다.

     

    NGINX를 경유해 업스트림 서버로 전달되는 요청은 네트워크 연결이나 업스트림 서버에 문제가 있을 수 있고, 서버에서 동작하는 애플리케이션의 문제 등 여러 가지 이유로 실패할 수 있다.

    NGINX는 이러한 문제를 미리 파악하여 트래픽 전송을 중지할 수 있어야 하는데, 오픈 소스 버전에서는 사용자의 요청을 받은 시점에 업스트림 서버의 연결이나 응답을 확인하는 패시브(passive) 방식의 서버 상태 확인 기능을 제공하며 NGINX Plus는 추가로 업스트림 서버에 주기적으로 연결을 시도하고 요청을 보내 서버 응답에 문제가 없는지 확인하는 액티브(active) 방식을 제공합니다.


    1. HTTP 부하분산

    upstream backend {
    	server 10.10.12.45:80		weight=1;
    	server app.example.com:80	weight=2;
    	server spare.example.com:80	backup;
    }
    
    server {
    	location / {
        	proxy_pass http://backend;
        }
    }

    NGINX의 upstream 블록과 http 모듈을 이용해서 HTTP 서버 간 부하를 분산한다.

     

    weight 매개변수로 가중치를 설정한 두 대의 프라이머리 서버에 문제가 발생한다면 backup으로 지정한 서버를 사용한다.

    지정한 weight 매개변숫값에 따라 두 번째 서버는 첫 번째 서버보다 두 배 많은 요청을 처리한다. (weight의 기본 값은 1이다.)

     

    HTTP upstream 모듈

    - 부하분산을 위한 서버 풀은 유닉스 소켓, IP 주소, DNS 레코드로 구성한다.

    - 각 업스트림 대상은 server 지시자로 설정한다.

    - 매개변수는 요청을 적절한 목적지로 전달하도록 추가 제어 방법을 제공한다.

    - 매개변수는 분산 알고리즘에 가중치를 적용하거나 서버의 상태나 서버 가용 여부를 판단하는 방법을 정의한다.


    2. TCP 부하분산

    /etc/nginx/nginx.conf 설정 파일

    user nginx;
    worker_processes auto;
    pid /run/nginx.pid;
    
    stream {
    	include /etc/nginx/stream.conf.d/*.conf;
    }

    /etc/nginx/stream.conf.d/mysql_read.conf 설정 파일

    upstream mysql_read {
    	server read1.example.com:3306 	weight=5;
    	server read1.example.com:3306;
    	server 10.10.12.34:3306		backup;
    }
    
    server {
    	listen 3306;
    	proxy_pass mysql_read;
    }

    NGINX의 upstream 블록과 stream 모듈을 이용해 TCP 서버 간 부하를 분산한다.

     

    server 블록은 3306 포트로 TCP 요청을 받아서 read replica 두 대로 구성된 MySQL 서버로 부하를 분산한다.

    NGINX의 기본 설정 파일 경로인 conf.d는 http 블록에 포함되기 때문에 stream 모듈을 이용한 설정은 stream.conf.d라는 별도의 폴더를 생성해서 저장해야 한다.

    해당 경로를 nginx.conf 파일의 stream 블록을 추가해 NGINX가 참조하도록 한다.

     

    http 모듈은 OSI 모델의 7계층인 애플리케이션 계층에서 동작하며 stream 모듈은 4계층인 전송 계층에서 동작한다.

    NGINX에서 TCP 부하분산은 stream 모듈을 이용해 정의한다.

    stream 모듈은 TCP 연결과 관련된 리버스 프록시의 여러 속성을 정의할 수 있는 옵션을 제공한다.

    대표적으로 유효한 SSL/TLS 인증서 제한, 타임아웃, 킵얼라이브 시간 설정 등이 있다.


    3. UDP 부하분산

    /etc/nginx/stream.conf.d/ntp.conf 설정 파일

    upstream ntp {
        server ntp1.example.com:123	weight=2;
        server ntp2.example.com:123;
    }
    
    server {
        listen 123 udp;
        proxy_pass ntp;
    }

    udp로 정의된 upstream 블록을 NGINX의 stream 모듈에서 사용해 UDP 서버 간에 부하를 분산한다.

     

    예시는 UDP 프로토콜을 사용해 네트워크 타임 프로토콜 (Network Time Protocol, NTP) 서버 두 대로 부하를 분산한다.

    UDP 프로토콜의 부하분산 설정은 listen 지시자에 udp 매개변수만 추가하면 되기 때문에 간단하다.


    4. 부하분산 알고리즘

    라운드 로빈 (Round Robin)

    - 기본 값으로 설정된 부하분산 방법이다.

    - 업스트림 풀에 지정된 서버의 순서에 따라 요청을 전달한다.

    - 가중치를 적용할 수 있다.

     

    리스트 커넥션 (Least Connection)

    - NGINX와의 연결 수가 가장 적은 업스트림 서버로 요청을 전달한다.

    - 가중치를 적용할 수 있다.

    - least_conn 지시자를 사용한다.

     

    리스트 타임 (Least Time)

    - NGINX Plus에서 제공하는 기능이다.

    - 리스트 커넥션과 마찬가지로 연결 수가 가장 적은 업스트림 서버로 요청을 전달하지만, 그중 응답 시간이 가장 빠른 서버를 우선시한다는 차이가 있다.

    - header나 last_byte 매개변수 중 하나를 지정한다. header를 지정하면 업스트림 서버로부터 응답 헤더를 받을 때까지 소요된 시간을 사용하고, last_byte를 지정하면 헤더뿐 아니라 응답 전체를 받을 때까지 소요된 시간을 사용한다.

    - least_time 지시자를 사용한다.

     

    제네릭 해시 (Generic Hash)

    - 텍스트 문자열 혹은 요청이나 런타임의 변수를 사용해 해시를 정의한다.

    - NGINX는 수신한 요청의 해시를 생성하고 업스트림 서버 선택에 활용해 부하를 분산한다.

    - 요청을 처리할 서버를 선택하는 데 깊이 개입하거나 캐시가 있을 확률이 높은 서버로 요청을 전달하고 싶을 때 유용하다.

    - 단 서버가 업스트림 풀에서 추가되거나 삭제되면 해시 처리된 요청이 재분배되며 consistent 매개변수를 사용해 영향을 최소화할 수 있다.

    - hash 지시자를 사용한다.

     

    랜덤 (Random)

    - 업스트림 풀에 지정된 서버를 임의로 선택해 요청을 전달한다.

    - 가중치를 적용할 수 있다.

    - 매개변수로 two [method]를 사용하면 먼저 서버 두 대를 임의로 선택하고 method에 지정된 알고리즘을 이용해 2차 부하분산을 한다.

    - random 지시자를 사용한다.

     

    IP 해시 (IP Hash)

    - HTTP에 대해서만 동작하는 방법으로, IP 주소를 이용해 해시를 생성한다.

    - 이 방식은 업스트림 서버에 문제가 없는 한 같은 서버로 할당된다.

    - 서버 로컬에 세션을 가지고 있는 경우 유용하다.

    - 가중치를 적용할 수 있다.

    - ip_hash 지시자를 사용한다.


    5. 스티키 쿠키 - Sticky Cookie (NGINX Plus)

    upstream backend {
    	server backend1.example.com;
    	server backend2.example.com;
    	sticky cookie
        	   affinity
        	   expires=1h
               domain=.example.com
               httponly
               secure
               path=/;
    }

    이 설정은 사용자가 지속적으로 특정 업스트림 서버에 연결되도록 쿠키를 생성해 추적한다.

     

    사용자의 첫 번째 요청 수신 시 업스트림 서버 정보를 포함하는 쿠키를 생성한다.

    NGINX Plus는 쿠키를 추적해 이어지는 사용자 요청을 같은 서버로 전달한다.


    6. 스티키 런 - Sticky Learn (NGINX Plus)

    upstream backend {
    	server backend1.example.com:8080;
    	server backend1.example.com:8081;
        
    	sticky learn
                   create=$upstream_cookie_cookiename
                   lookup=$cookie_cookiename
                   zone=client_sessions:2m;
    }

    업스트림 서버의 응답 헤더 중 Set-Cookie 헤더에서 이름이 cookiename인 쿠키를 찾아 추적한다.

    이 값은 NGINX에 저장되고 사용자 요청 헤더에서 쿠키 이름이 같은 값이 확인되면 해당 세션을 가진 업스트림 서버로 요청을 전달한다.


    7. 수동적인 헬스 체크 - Passive Health Check

    upstream backend {
    	server backend1.example.com:1234 max_fails=3 fail_timout=3s;
    	server backend2.example.com:1234 max_fails=3 fail_timout=3s;
    }

    동작에 문제가 없는 서버만 사용하려면 NGINX 부하분산 설정에 헬스 체크 매개변수를 추가해야 한다.

     

    이 설정은 사용자 요청에 대한 업스트림 서버의 응답을 모니터링해 업스트림 서버의 상태를 수동으로 확인한다.

    max_fails 매개변수는 헬스 체크의 최대 실패 횟수이며 예시 설정에서는 3회로 지정하고, fail_timeout은 실패에 대한 타임아웃 값이며 3초로지정한다.

     

    클라이언트의 요청이 NGINX를 경유해 업스트림 서버로 보내진 후 타임아웃이나 요청 실패가 발생하는지 확인한다.

    부하분산에서 업스트림 서버의 상태를 모니터링하는 작업은 사용자 및 비즈니스 연속성 관점에서 중요하므로 NGINX는 수동적인 모니터링으로 업스트림 서버가 원활하게 동작하는지 확인한다.


    8. 능동적인 헬스 체크 - Active Health Check (NGINX Plus)

    http {
    	server {
    		location / {
    			proxy_pass http://backend;
    			health_check interval=2s
    					fails=2
    					passes=5
    					uri=/
    					match=welcome;
    		}
    	}
        
    	match welcome {
    		status 200;
    		header Content-Type=text/html;
    		body ~ "Welcome to nginx!";
    	}
    }

    이 설정은 업스트림 서버의 최상위(/) 경로로 2초마다 HTTP GET 요청을 보내 서버 상태를 확인한다.

     

    업스트림 서버는 헬스 체크에 대해 5회 연속 정상적으로 응답하면 상태가 양호하다고 간주되고, 헬스 체크가 2회 연속 실패하면 해당 업스트림 서버는 문제가 있다고 판단되어 풀에서 제외된다.

     

    응답 코드가 200이고 Content-Type이 "text/html"이면서 응답 바디에 "Welcome to nginx!" 문자열이 있으면 정상으로 간주된다.


    9. 슬로 스타트 - Slow Start (NGINX Plus)

    upstream {
    	zone backend 64k;
    	
    	server server1.example.com slow_start=20s;
    	server server2.example.com slow_start=15s;
    }

    슬로 스타트는 운영 환경에서 실사용자 트래픽을 받기 전에 애플리케이션의 예열이 필요한 상황에 사용된다.

     

    서버 시작 직후에 연결 폭주 없이 데이터베이스 연결을 맺고 캐시를 쌓을 시간을 확보함으로써 애플리케이션은 서비스를 원활히 제공할 준비를 할 수 있다.

     

    이 기능은 서버가 헬스 체크 실패로 풀에서 제외된 후 다시 정상화되어 풀에 투입되는 시점에 유용하다.

    단 hash, ip_hash, random 부하분산 방식에서는 사용할 수 없다.

    반응형
Designed by Tistory.