잘못된 정보가 있다면, 꼭 댓글로 알려주세요(비로그인 익명도 가능).

여러분의 피드백이 저와 방문자 모두를 올바른 정보로 인도할 수 있습니다.

감사합니다. -현록

후원해주실 분은 여기로→

현록의 기록저장소

NGINX 오픈소스 버전에 upstream health check 모듈 추가 본문

Study/NGINX

NGINX 오픈소스 버전에 upstream health check 모듈 추가

현록 2023. 1. 31. 22:42

[개요]

[설치]

[설정]

[실행]

 

 


[개요]

 

NGINX는 점유율 1위(2022년 5월 기준)의 웹서버로, 비동기식 처리 방식으로 로드밸런싱이 가능하다.

리버스 프록시로 동작하며, 이를 응용하여 무중단 배포 등을 가능하도록 한다.

 

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

 

HTTP Health Checks | NGINX Plus

HTTP Health Checks Monitor the health of HTTP servers in an upstream group by sending periodic health checks, including customizable active health checks in NGINX Plus. Introduction NGINX and NGINX Plus can continually test your upstream servers, avoid the

docs.nginx.com

들어온 요청을 뒷단(upstream) 중 하나로 전달하고 응답을 받아와서 넘겨주는데,

로직에 따라 서버를 선택하지만, 그 서버가 살아있는지 지속적으로 health check하는 기능은 NGINX Plus(commercial 버전. 구독료 버전)에만 있다.

오픈소스 버전에도 제공하는 옵션은 각 서버에 max_fails(기본값 1회), fail_timeout(기본값 10초) 을 지정하는 것 밖에 없다.

upstream spring-simple-rest {
    least_conn;
    server nginx-spring-01:8091 max_fails=1 fail_timeout=3s;
    server nginx-spring-02:8092 max_fails=1 fail_timeout=3s;
    server nginx-spring-03:8093 max_fails=1 fail_timeout=3s;
}

 

특정 API가 평균 소요시간이 길다면 fail_timeout은 그보다 길어야할 것이다.

설정에 따라 다르지만, 뒷단이 죽어있어도 fail_timeout만큼 기다린 후에야 다른 쪽으로 요청을 넘기기도 한다.

또한 fail_timeout만큼은 해당 쪽으로는 요청을 보내지 않지만, 그 이후로는 다시 해당 쪽으로 요청을 보낼 수 있으며 그 때마다 지연이 발생한다.

 

요청이 들어올 때 무작정 보내보고 실패하면 다른 쪽으로 넘기는 방식이 아니라,

특정 시간마다 개별적으로 health check를 하고, 뒷단들의 상태를 갖고 있어서

요청이 들어오면 살아있는 쪽들만 후보로 하면, upstream 중 일부에 장애가 있을 때 불필요한 지연을 없앨 수 있다.

 

그런데 이 기능이 NGINX Plus에만 제공되고 있다.

하지만 오픈소스 모듈을 설치 가능하니, 가장 많이 사용하는 upstream health check 모듈을 설치하여 사용해본다.

https://github.com/yaoweibin/nginx_upstream_check_module

 

GitHub - yaoweibin/nginx_upstream_check_module: Health checks upstreams for nginx

Health checks upstreams for nginx. Contribute to yaoweibin/nginx_upstream_check_module development by creating an account on GitHub.

github.com

 

 


[설치]

 

위의 오픈소스 모듈 git 프로젝트(https://github.com/yaoweibin/nginx_upstream_check_module)를 내려받는다.

 

오픈소스 모듈의 버전 업이 NGINX에 비해 원활하지 않은 것 같다.

NGINX가 너무 상위 버전이라면 아직 이 모듈이 적용되는 범위를 벗어날 수 있으니, 이를 참고하여 NGINX 역시 받는다.

https://nginx.org/download/

 

Index of /download/

 

nginx.org

 

NGINX의 압축도 풀어둔다.

patch 명령어로 풀어둔 NGINX들에 패치를 할 것이다.

※ patch 명령어는 Docker 이미지류에는 어디에도 존재하지 않는다.

 Docker가 이미 레이어 타입이기 때문.

 다른 Linux/Unix에서 패치 후 디렉터리들을 COPY하는 방식으로 사용하자.

~ % cd nginx-1.20.1

nginx-1.20.1 % patch -p1 < ~/Downloads/nginx_upstream_check_module/check_1.20.1+.patch

patching file 'src/http/modules/ngx_http_upstream_hash_module.c'

patching file 'src/http/modules/ngx_http_upstream_ip_hash_module.c'

patching file 'src/http/modules/ngx_http_upstream_least_conn_module.c'

patching file 'src/http/ngx_http_upstream_round_robin.c'

patching file 'src/http/ngx_http_upstream_round_robin.h'

nginx 디렉터리 내부에 ./src/http/...에 위 파일들이 수정된 것을 수정된 날짜나 내용등을 통해 확인할 수 있다.


이제 이대로 컴파일 후 빌드할텐데, c로 작성된 프로그램이니 c 컴파일러가 필요하다.

※ 여기서부터의 과정을 Docker 방식으로 하고 싶다면, 과정 아래에 Dockerfile로 간략하게 수록했으니 참고.

nginx-1.20.1 % ./configure    --add-module=~/Downloads/nginx_upstream_check_module 

Configuration summary

  + using system PCRE library

  + OpenSSL library is not used

  + using system zlib library

  nginx path prefix: "/usr/local/nginx"

  nginx binary file: "/usr/local/nginx/sbin/nginx"

  nginx modules path: "/usr/local/nginx/modules"

  nginx configuration prefix: "/usr/local/nginx/conf"

  nginx configuration file: "/usr/local/nginx/conf/nginx.conf"

  nginx pid file: "/usr/local/nginx/logs/nginx.pid"

  nginx error log file: "/usr/local/nginx/logs/error.log"

  nginx http access log file: "/usr/local/nginx/logs/access.log"

  nginx http client request body temporary files: "client_body_temp"

  nginx http proxy temporary files: "proxy_temp"

  nginx http fastcgi temporary files: "fastcgi_temp"

  nginx http uwsgi temporary files: "uwsgi_temp"

  nginx http scgi temporary files: "scgi_temp"

우선 nginx/configure--add-module 옵션을 주어 실행해보면 컴파일이 가능한지 확인해주며,

빌드할 때 프로그램 설치 경로가 어떨지 설정을 잡아준다.

※ 전체 설치 경로 시작(NGINX_PREFIX)의 기본값은 /usr/local/nginx인데, --prefix=경로 옵션을 주어 전체 설치 경로를 지정할 수 있다.

 다른 옵션은 https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/의 Configuring NGINX Paths에서 확인.


이제 빌드와 설치를 진행한다.

nginx-1.20.1 % make

...

...

nginx-1.20.1 % make install

...

...


오류 없이 설치되었다면 설치 경로를 확인해본다.

% ls -al /usr/local/nginx

total 4

drwxr-xr-x 11 root   root  352 Jan 31 12:52 .

drwxr-xr-x  1 root   root 4096 Jan 31 12:27 ..

drwx------  2 nobody root   64 Jan 31 12:52 client_body_temp

drwxr-xr-x 18 root   root  576 Feb  1 00:49 conf

drwx------  2 nobody root   64 Jan 31 12:52 fastcgi_temp

drwxr-xr-x  4 root   root  128 Jan 31 12:24 html

drwxr-xr-x  5 root   root  160 Jan 31 13:10 logs

drwx------  2 nobody root   64 Jan 31 12:52 proxy_temp

drwxr-xr-x  3 root   root   96 Jan 31 12:24 sbin

drwx------  2 nobody root   64 Jan 31 12:52 scgi_temp

drwx------  2 nobody root   64 Jan 31 12:52 uwsgi_temp

실행할 프로그램은 ./sbin/nginx 이다.

% /usr/local/nginx/sbin/nginx -V

nginx version: nginx/1.20.1

built by gcc 12.2.0 (GCC) 

configure arguments: --add-module=/nginx_upstream_check_module

-V 옵션으로 실행시키면 버전 확인과 빌드 설정시 지정했던 옵션 등을 확인할 수 있다.


./configure, make, make installDockerfile 예시를 수록한다.

c 컴파일러로 gcc 이미지를 사용했다.(예시의 이미지는 용량이 무려 1.27 GB이니, 가벼운 c 컴파일러 수록 이미지를 찾아보는 것을 추천)

FROM gcc:12.2.0

COPY --chown=0:0 --chmod=775 ./nginx-1.20.1 /nginx-1.20.1
COPY --chown=0:0 --chmod=775 ./nginx_upstream_check_module /nginx_upstream_check_module
WORKDIR /nginx-1.20.1
RUN [ "./configure", "--add-module=/nginx_upstream_check_module" ]
RUN [ "make" ]
RUN [ "make", "install" ]

WORKDIR /
ENTRYPOINT [ "bin/bash" ]

EXPOSE 8081

 

 


[설정]

 

설치한 nginx의 conf 디렉터리(/usr/local/nginx/conf)를 확인해보자.

% ls -al /usr/local/nginx/conf

total 76

drwxr-xr-x 18 root root  576 Feb  1 00:49 .

drwxr-xr-x 11 root root  352 Jan 31 12:52 ..

-rwxr-xr-x  1 root root 1077 Jan 31 12:24 fastcgi.conf

-rwxr-xr-x  1 root root 1077 Jan 31 12:24 fastcgi.conf.default

-rwxr-xr-x  1 root root 1007 Jan 31 12:24 fastcgi_params

-rwxr-xr-x  1 root root 1007 Jan 31 12:24 fastcgi_params.default

-rwxr-xr-x  1 root root 2837 Jan 31 12:24 koi-utf

-rwxr-xr-x  1 root root 2223 Jan 31 12:24 koi-win

-rwxr-xr-x  1 root root 5231 Jan 31 12:24 mime.types

-rwxr-xr-x  1 root root 5231 Jan 31 12:24 mime.types.default

-rwxr-xr-x  1 root root 2656 Jan 31 12:24 nginx.conf

-rwxr-xr-x  1 root root 2656 Jan 31 12:24 nginx.conf.default

-rwxr-xr-x  1 root root  636 Jan 31 12:24 scgi_params

-rwxr-xr-x  1 root root  636 Jan 31 12:24 scgi_params.default

-rwxr-xr-x  1 root root  664 Jan 31 12:24 uwsgi_params

-rwxr-xr-x  1 root root  664 Jan 31 12:24 uwsgi_params.default

-rwxr-xr-x  1 root root 3610 Jan 31 12:24 win-utf

여기서 nginx.conf 파일을 수정해보자.

http {} 블록 내부 맨 아래에 include my.conf; 한 줄만 추가해주자.

그리고 빠져나온 후, my.conf 문서를 생성하여 원하는 옵션을 작성해보자.

 

아래는 모듈을 설치하지 않은 my.conf 예시이다.

server_tokens off;

  

upstream spring-simple-rest {

    least_conn;

    server nginx-spring-01:8091 max_fails=1 fail_timeout=3s;

    server nginx-spring-02:8092 max_fails=1 fail_timeout=3s;

    server nginx-spring-03:8093 max_fails=1 fail_timeout=3s;

}

  

server {

    listen 8081;

    # server_name *;

    

    location / {

        proxy_pass http://spring-simple-rest;

        

        proxy_http_version 1.1;

        proxy_set_header Host $http_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;

        

        proxy_redirect off;

    }

}

아래는 모듈을 설치하여 upstream health check를 하는 my.conf의 예시이다.

server_tokens off;

  

upstream spring-simple-rest {

    least_conn;

    server nginx-spring-01:8091 max_fails=1 fail_timeout=3s;

    server nginx-spring-02:8092 max_fails=1 fail_timeout=3s;

    server nginx-spring-03:8093 max_fails=1 fail_timeout=3s;

    

    check interval=1000 rise=1 fall=4 timeout=200 type=http;

    check_http_send "HEAD /simple/healthcheck HTTP/1.0\r\n\r\n";

    check_http_expect_alive http_2xx http_3xx;

}

  

server {

    listen 8081;

    # server_name *;

    

    location / {

        proxy_pass http://spring-simple-rest;

        

        proxy_http_version 1.1;

        proxy_set_header Host $http_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;

        

        proxy_redirect off;

    }

    

    location = /nginx_status {

        check_status;

    }

}

 

upstream 부분에 check로 시작하는 3줄과

server { ..., location = /nginx_status {} } 부분을 추가한 것이다.

 

 

check interval=1000 rise=1 fall=4 timeout=200 type=http;

 체크 주기, 성공 판단 수, 몇 번 실패해야 최종 실패로 보는지, 요청당 최대 시간, 요청 타입을 지정할 수 있다.

 

check_http_send "HEAD /simple/healthcheck HTTP/1.0\r\n\r\n";

 체크 request method, api 주소 등을 지정할 수 있다. 예시에서는 HEAD 방식으로 http://nginx-spring-01:8091/simple/healthcheck 등으로 API Call로 응답을 확인할 것이다.

 

check_http_expect_alive http_2xx http_3xx;

 성공 판단 요청의 http status code를 지정할 수 있다.

 

server 블록 중 아래 check_status; 부분은 listen 포트(예시에서는 8081)의 해당 경로(예시에서는 ./nginx_status)로 접속하면 upstream 들의 상태를 확인할 수 있다.

 

더 상세한 설명 및 다른 추가 옵션들은 역시 공식 문서를 보시라.

https://github.com/yaoweibin/nginx_upstream_check_module#readme

 

 


[실행]

 

nginx 서버 실행은 그냥 옵션 없이 실행하면 된다.

% /usr/local/nginx/sbin/nginx

실행하여 upstream 서버 중 일부를 끄고 켜보면서 확인해본다.

 

참고로 stop, reload 등은 -s 옵션으로 가능하다.

% /usr/local/nginx/sbin/nginx    -s stop|reload|...  

 

 

Comments

잘못된 정보가 있다면, 꼭 댓글로 알려주세요(비로그인 익명도 가능).

여러분의 피드백이 저와 방문자 모두를 올바른 정보로 인도할 수 있습니다.

감사합니다. -현록

후원해주실 분은 여기로→