본문 바로가기
문제 해결, 기술 비교/개인프로젝트(북클럽)

nginx를 이용한 로드밸런싱 및 무중단 배포

코동이 2022. 2. 3.

개요


일반적으로 하나의 서버를 통해 애플리케이션을 운영하지만, 많은 사용자가 늘어남에 따라 요청을 감당하기 위해 여러개의 서버가 필요합니다. 이 요청을 여러개의 서버로 잘 분산시키기 위해서 도움을 주는 nginx를 알아보도록 하겠습니다.

 

 

nginx 동작 방식


 

  nginx는 1개의 마스터 프로세스와 여러개의 워커 프로세스를 가지고 있습니다. 마스터 프로세스 설정에 따라 워커 프로세스들 동작이 정해집니다. 워커 프로세스들은 실제 요청을 처리합니다. nginx는 이벤트 기반 모델과 OS 의존적인 메커니즘을 사용해 워커 프로세스들간에 요청을 효율적으로 분산시킵니다.

 

 워커 인스턴스들의 갯수는 설정파일에 정의되어 있으며 주어진 설정에 따라 고정될 수도 있고 사용가능한 CPU 코어에 따라서 자동으로 정해질 수도 있습니다. nginx와 모듈들이 동작하는 방식은 설정 파일에 정의되어 있습니다. 기본적으로 설정파일의 이름은 nginx.conf이고 /usr/local/nginx/confm, /etc/nginx, 혹은 /usr/local/etc/nginx에 위치해 있습니다.

nginx를 시작하기 위해서 실행파일을 실행시킵니다. nginx를 작동시키면, -s 매개변수를 붙인 명령어를 실행해서 관리할 수 있습니다. 

 

nginx -s signal


stop - fast shutdown(즉시 종료)
quit - graceful shutdown(우아한 종료)
reload - reloading the configuration file(설정파일 재시작)
reopen - reopen log files(로그파일 열기)

 


 만약에, 워커 프로세서가 현재 요청을 모두 처리하는 것을 기다리고 nginx를 종료하고 싶다면 다음을 입력합니다

 

nginx -s quit

 

 설정파일에 변경된 부분은 reload 혹은 restart 설정이 실행되기 전까지는 반영되지 않습니다.  따라서 다음을 입력합니다.

 

nginx -s reload

 

 마스터 프로세스가 설정파일을 reload 하라는 신호를 받는다면 새로운 설정파일의 문맥적 유효성을 검사하고 적용합니다. 만약 성공한다면, 마스터 프로세스는 새로운 워커 프로세스를 시작하고 이전 워커 프로세스는 종료하라는 메세지를 보냅니다. 실패하는 경우, 마스터 프로세스는 변경사항을 모두 롤배하고 이전 설정들을 계속 유지합니다. 종료를 명령받은 오래된 워커 프로세스들은 현재의 요청만 처리하다가 모두 요청을 처리하면 새로운 연결을 멈춥니다. 그 이후, 이전 워커 프로세스들은 종료됩니다.

 

 

Nginx 파일 설정하기


http {
    upstream myapp1 {
        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

    server {
        listen 80;

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

 

예제에서는 같은 어플리케이션에 3개의 인스턴스들이 있습니다. 로드 밸런싱 메서드가 따로 설정되지 않는다면, 기본적으고 round-robin으로 설정됩니다. 모든 요청들은 myapp1 서버 그룹으로 프록시되고 nginx는 HTTP 로드 밸런싱으로 요청을 분배합니다.

nginx에서 리버스 프록시 구현은 HTTP, HTTPS, FastCGI, SCI 등을 포함합니다.

HTTP 대신에 로드 밸런싱을 HTTPS에 적용하려면, https 프로토콜을 사용합니다.

 

 

Reverse Proxy


 

 리버스 프록시는 여러개의 서버 앞에 웹서버에 존재하여 로드밸런싱 역할을 합니다. 따라서 수많은 요청에도 과부하 걸리지 않도록 부하를 낮춥니다. 로드밸런싱 뿐만 아니라 보안에 좋습니다. 리버스 프록시를 사용하면 보래 서버의 IP주소를 노출시킬 필요 없이 해당 서버를 통해서 알고리즘에 따라 서버에 요청이 전달됩니다. 

 

 

 Load Balancing 설정하기


다양한 어플리케이션 인스턴스들의 로드 밸런싱은 자원 효율성과 처리율 극대화, 지연감소, 결함허용 등을 위해 사용되는 기술입니다.

Nginx를 사용해 로드 밸런싱을 적용하려면, 처음에는 upstream 지시어로 그룹을 정의해야 합니다. 지시어는 http 문맥 안에 있습니다.

그룹에 있는 서버들은 server 지시어로 설정됩니다. 예를 들어, 다음의 설정은 backend라고 이름이 지어진 그룹을 정의하고, 3개의 서버 설정으로 이루어져 있습니다. ( 실제로는 3개 이상의 서버가 될 수도 있습니다.)

 

http {
    upstream backend {
        server backend1.example.com weight=5;
        server backend2.example.com;
        server 192.0.0.1 backup;
    }
}

 

 

요청을 서버 그룹에 보내기 위해서, 그룹의 이름이 proxy_pass 지시어에서 정의되어야 합니다. 다음 단계로, NGINX 에서 구동중인 모든 가상 서버는 이전 예시에서 정의된 backend upstream 그룹에 모든 요청을 통과시킵니다.

 

server {
    location / {
        proxy_pass http://backend;
    }
}



다음의 예제는 2가지 명령어를 결합시킨 것입니다. 그리고 어떻게 HTTP 요청을 backend 서버 그룹에 프록시 시키는지 보여주니다. 그룹은 3개의 서버로 이루어져 있는데, 2개의 인스턴스는 같은 어플리케이션이고, 세번째는 백업 서버입니다. upstream에 따로 로드밸런싱 알고리즘이 설정되지 않기 때문에, 기본적으로 Round  Robin 알고리즘을 사용합니다.

 

http {
    upstream backend {
        server backend1.example.com;
        server backend2.example.com;
        server 192.0.0.1 backup;
    }
    
    server {
        location / {
            proxy_pass http://backend;
        }
    }
}

 

Server Weights


upstream backend {
    server backend1.example.com weight=5;
    server backend2.example.com;
    server 192.0.0.1 backup;
}

 

기본적으로 Nginx는 Round Robin 방식을 사용해서 weight에 따라 그룹에 있는 서버들에게 요청을 분산시킵니다. server 지시어에서 weight 파라미터는 서버의 weight를 설정합니다. 기본은 1입니다.

예를 들어, 5를 설정한다면 다른 서버들은 1입니다. 그러나 192.9.9.1은 백업 서버라고 표시되어 있고 만약에 2개의 서버가 이용불가능한 상태가 아니라면 요청을 받지 않습니다. weight 설정으로 6개의 요청이 올때마다, 5개는 첫번째 backend1.example.com으로 가고 1개는 backend2.example.com으로 보내집니다.

 

Health check


nginx에서 리버스 프록시 구현은 in-band 서버 헬스 체크를 포함합니다. 특정 서버로부터 응답이 실패한다면, nginx는 이 서버를 실패로 표시하고, 잠시동안 순차적인 요청에서 이 서버 선택을 제외시킵니다. 

max_fails 지시어는 연속적인 실패 시도 횟수를 나타내며, 달성 시, fail_timeout 동안 서버가 사용이 불가능합니다. 기본적으로, max_fails는 1이고, fail_timeout은 10입니다. 만약 max_fails가 0으로 설정된다면, 헬스 체크는 이 서버에 작동하지 않습니다. fail_timeout은 얼마나 오랫동안 서버가 실패로 표시되는지를 정의합니다. 서버 실패에 뒤이어 fail_timeout 시간이 지나면, nginx는 고객요청이 들어온 서버를 gracefully하게 조사합니다. 조사가 성공적으로 끝나면, 서버는 다시 살아있음으로 표시됩니다. 

따라서 max_fail과 fail_timeout을 3으로 설정하는게 안정적 서비스에 도움이 됩니다. 3회까지는 재시도를 하며, 모두 실패하더라도 3초간만 서버를 사용하지 못하게 됩니다.

 

 Session persistence


round-robin이든 least-connected 로드 밸런싱 알고리즘을 사용하든지, 각각의 순차거인 클라이언트 요청은 다른 서버에 분배될 가능성이 있습니다. 같은 고객이 항상 같은 서버로 요청을 전달할 것을 보장할 수 없습니다.

만약에 고객을 특정 어플리케이션 서버에 계속 묶어두어야 한다면, (sticky, persistent) ip-hash 로드 밸런싱 알고리즘이 사용됩니다.

ip-hash 알고리즘으로, 고객의 IP 주소는 고객이 요청시, 해싱 키로 사용하여 고객의 요청에 따라 어느 그룹으로 전달되어야 하는지 결정됩니다. 이 방법은 같은 고객의 요청이 서버가 사용하지 못하는 때를 제외하고는 항상 같은 서버로 전달되록 강제합니다.

ip-hash 로드 밸런싱을 설정하기 위해서는 단지 ip_hash 지시어를 서버 그룹 설정에 추가하면 됩니다.

 

 

Server Slow-Start


erver slow-start 특징은 최근에 복구된 서버가 연결에 의해서 과부화되는 것을 예방한다는 것입니다. 이 연결은 타임아웃을 내거나, 서버가 또다시 실패로 표시될 여지가 있는 경우입니다. 

 

비정상 서버가 정상이 될 때 또는 서버가 사용할 수 없는 것으로 간주되었는데 일정 기간 후 사용할 수 있게 되는 시간을 설정합니다. 기본값은 0입니다.

 

upstream backend {
    server backend1.example.com slow_start=30s;
    server backend2.example.com;
    server 192.0.0.1 backup;
}

 

 

프로젝트 개선하기


현재는 jenkins instance 1개와 애플리케이션 instance 1개가 구성되어있습니다. 하지만 대량의 트래픽을 감당하고 장애로부터 대비하기 위하여 여러대의 서버를 가지고 있는게 좋습니다. 따라서 instance 추가 및, 여러 instance들을 로드밸런싱 해줄 NGINX를 설치해보도록 하겠습니다.

 

1. instance 추가 증설

2. 추가된 instance에 도커 설치

4. jenkins에 새로운 instance 추가

2. nginx instance 추가 및 설치

3. nginx 설정

 

1. instance 추가 증설

 

여러대의 instance를 추가적으로 만들기 위해서 GCP가 제공하는 기능을 사용합니다. 머신 이미지를 통해, 기본 환경 설정을 재사용할 수 있습니다.

 

 

 

머신 이미지에 들어가면, 맨 오른쪽 작업에 [인스턴스 만들기]를 통해 새롭게 만들어주면 됩니다.

 

 

만약에 저장된 머신 이미지가 없다면 다음과 같이 기존에 존재하는 인스턴스를 이미지로 만들 수 있습니다.

 

 

혹은 머신 이미지에서 새롭게 만들어 줄 수도 있습니다.

 

 

추가적으로 아래 2개의 instance를 생성하였으므로 jenkins에 연동하게 되는 instance는 총 3개입니다.

 

 

2. 추가된 instance에 도커 설치

 

추가된 instance에 SSH접속을 해 아래 명령어로 도커를 설치합니다.

 

sudo yum install -y docker
sudo systemctl start docker
sudo chmod 666 /var/run/docker.sock

 

 

3. jenkins에 새로운 instance 추가

[Jenkins 관리] - [시스템 설정] - [Publish over SSH]에서 새로 생성한 instnace 2개를 추가합니다. 이전에 추가했던 내용과 똑같이 적용합니다.

 

Name에는 식별되는 규칙 있는 이름, Hostname에는 내부 IP, Username은 Terminal에서 표시되는 계정 ID, Remote Directory는 cd ~, pwd로 나온 경로를 입력해줍니다. 이제 총 SSH는 3개가 연결됩니다.

 

배포환경 cpu-worker-instance-1 deploy에 들어가 [구성] - [빌드 후 조치]에서 서버를 추가합니다.

Exec command에서 /dev/null은 로그기록을 휴지통으로 보내고 있기 때문에 전부 아래와 같이 바꿉니다.

 

nohup docker run -p 8080:80 userId/cpu-bound-app > nohup.out 2>&1 &

 

cpu-worker-instance-2와 cpu-worker-instance-3를 선택하고 Exec command를 추가합니다

 

 

4. Build Now & 정상 시작 확인하기

 

Build Now로 배포를 진행하고, SUCCESS가 뜨면 각 instance의 SSH에 접속해서 다음 명령어로 확인합니다

 

tail -f nohup.out

 

 

5. nginx instance 추가 및 설치

 

nginx를 위한 instance를 1개 만들고 SSH에 접속합니다

 

nginx 설치 및 nginx를 시작합니다

 

(sudo yum install -y epel-release)
sudo yum install -y nginx
sudo systemctl start nginx

 

nginx가 정상적으로 설치되었다면 IP 접속 시 아래 화면이 나타납니다

 

 

인바운드 규칙에 TCP 80번 포트를 추가합니다

 

 

설치된 instance를 nginx에 등록하기 위해 아래 명령어로 설정에 접속합니다

 

sudo vi /etc/nginx/nginx.conf

 

아래와 같이 nginx 설정 내용이 나옵니다.

 

 

위 로드밸런싱 설정에 아래 정보를 추가해주어야 합니다.

include와 server 사이에 upstream cpu-bound-app을 넣어줍니다

server 아래에 location 에 나머지 정보를 입력합니다

 

upstream cpu-bound-app {
  server {instance_1번의_ip}:8080 weight=100 max_fails=3 fail_timeout=3s;
  server {instance_2번의_ip}:8080 weight=100 max_fails=3 fail_timeout=3s;
  server {instance_3번의_ip}:8080 weight=100 max_fails=3 fail_timeout=3s;
}

location / {
  proxy_pass http://cpu-bound-app;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection 'upgrade';
  proxy_set_header Host $host;
  proxy_cache_bypass $http_upgrade;
}

 

아래 사진 위치에 2개의 큰 코드 명령어를 넣습니다

upstream 안에 IP는 내부(Private) IP입니다.

 

 

하지만 다시 nginx로 접속하면 오류가 나는데, 제대로 로드밸런싱이 안되어있기 때문입니다.

 

 

 

nginx 로드밸런싱을 설정했으므로 아래 명령어로 다시 재시작합니다

 

sudo systemctl reload nginx

 

 

nginx 관련 에러 메세지를 확인하고 싶다면 다음 명령어를 입력합니다

 

sudo tail -f /var/log/nginx/error.log

 

 

하지만 아래와 같이 오류가 납니다. 오류를 검색해보니 connect를 사용할 수 없습니다.

 

 

 

현재 CentOS에서 웹서버에 접속할 수 없는 오류가 나타납니다. 아래의 명령어로 해결합니다.

 

sudo setsebool -P httpd_can_network_connect on

 

 

worker-instance 중에 이미 포트를 사용중이라는 문구가 뜨는 경우가 있습니다.

 

 

netstat -ano,  sudo netstat -tulpn | grep LISTEN 등의 명령어로 port 사용을 확인합니다

 

 

centos7에서 포트를 삭제하기 위해서는 다음을 설치합니다

sudo yum install -y psmisc

 

아래 명령어로 삭제합니다

sudo fuser -k {port}/tcp

 

 

 

 

* 참고

https://nginx.org/en/docs/beginners_guide.html

https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/

https://nginx.org/en/docs/http/load_balancing.html

 

Using nginx as HTTP load balancer

Using nginx as HTTP load balancer Introduction Load balancing across multiple application instances is a commonly used technique for optimizing resource utilization, maximizing throughput, reducing latency, and ensuring fault-tolerant configurations. It is

nginx.org

 

https://nginx.org/en/docs/http/load_balancing.html#nginx_weighted_load_balancing

 

Using nginx as HTTP load balancer

Using nginx as HTTP load balancer Introduction Load balancing across multiple application instances is a commonly used technique for optimizing resource utilization, maximizing throughput, reducing latency, and ensuring fault-tolerant configurations. It is

nginx.org

 

https://losskatsu.github.io/it-infra/reverse-proxy/#3-%EB%A6%AC%EB%B2%84%EC%8A%A4-%ED%94%84%EB%A1%9D%EC%8B%9Creverse-proxy-%EC%84%9C%EB%B2%84%EB%9E%80

반응형