[Nginx] Load Balancer 설정하기
by Cory
사실 요즘 Loadbalancer 기능을 HAProxy 혹은 Envoy Proxy 를 사용해서 구축하는 경우가 더 많다고는 합니다. 가장 큰 이유가 health check 기능을 이용하려면 오픈소스만으로는 안되고 nginx plus 을 구매해서 사용해야 하기 때문일 것입니다. 그래도 nginx 는 로드벨런서 기능을 쉬우면서도 강력하기 때문에 알아둘 필요성은 있습니다.
이번 포스팅은 __ubuntu 18.04__ 기반으로 진행할 예정이며 sample app 으로 express app 두개를 사용하도록 하겠습니다.
이 포스팅은 nginx 설치와 proxy 설정에 대해서는 설명하지 않을 예정이니 궁금하신 분들은 이전 포스팅들을 참고해주시면 감사하겠습니다.
데모 앱 띄우기
nginx 를 들어가기 전에 demo app 을 다운받아 각각 다른 프로세스에서 실행시키도록 하겠습니다.
# 데모 앱 클론
$ git clone -b demo-nginx-loadbalancer-app --single-branch https://github.com/Lee-KyungSeok/blog-sample-projects.git
# 첫번째 demo app 실행
$ screen -S demoapp-1
$ cd blog-sample-projects/demo-app-express-1
$ npm install
$ PORT=7000 npm start
# 실행 후 "control + a + d" 로 빠져 나오기
# 두번째 demo app 실행
$ screen -S demoapp-2
$ cd blog-sample-projects/demo-app-express-2
$ npm install
$ PORT=8000 npm start
# 실행 후 "control + a + d" 로 빠져 나오기
# 두번째 demo app 실행
$ screen -S demoapp-3
$ cd blog-sample-projects/demo-app-express-3
$ npm install
$ PORT=9000 npm start
# 실행 후 "control + a + d" 로 빠져 나오기
# 프로세스 확인
$ ps aux | grep demo
이 앱은 express generator 로 간단하게 만들어진 앱으로 아래 사진과 같이 단순히 First App, Second App, Third App 이라는 html 페이지를 보여줍니다.
이제 이 앱을 이용해서 first app, second app, third app 으로 로드 벨런싱 되는 과정을 설명할 예정입니다.
nginx 설정
nginx 에서 로드벨런싱을 적용하는 법은 간단합니다. upstream 블락 을 만들어서 어던 서버로 로드벨런싱 할지 정해주면 됩니다. 즉 저희는 express-app 이라는 upstream 을 만들고 각각의 앱에 연결한 다음 proxy_pass 를 이용해 80 포트로 들어왔을 때 이 upstream 으로 전달하도록 하겠습니다.
이전과 마찬가지로 /etc/nginx/sites-available/proxy.conf
파일을 열어 내용을 작성하겠습니다.
server {
listen 80;
server_name {nginx server ip 혹은 domain 설정};
access_log /var/log/nginx/proxy/access.log;
error_log /var/log/nginx/proxy/error.log;
location / {
include /etc/nginx/proxy_params;
proxy_pass http://express-app;
}
}
upstream express-app {
server 127.0.0.1:7000;
server 127.0.0.1:8000;
server 127.0.0.1:9000;
}
아마 두번 실행하시면 아래 사진처럼 First App 과 Third App 으로 로드벨런싱이 되는 걸 확인할 수 있습니다. 한번 새로고침으로 여러번 눌러보시기 바랍니다.
그런데 뭔가 이상하지 않나요?? 왜 Second App 은 건너뛰고 Third App 으로 로드 벨런싱이 되었을까요??
사실 이상한게 아니라 아무런 설정을 하지 않으면 1. 라운드로빈 방식 으로 진행되는데 저희 express app 은 2. 정적파일 을 가지고 있기 때문입니다. 즉, 로드 벨런싱이 잘 되었다는 말이죠. 한번 아래 사진을 확인해보겠습니다.
그림에서 설명했듯이 처음 get 요청에 대한 응답은 demo app1 에서, 그다음 요청인 get styles.css 요청은 demo app2 으로 전달되며, 로드밸런싱이 잘 되는 것을 알 수 있습니다.
nginx LoadBalancing config
이번에는 nginx load balancing config 에 대한 설명을 드리고 로드벨런싱을 조금 더 정교하게 수정해 나가겠습니다.
이전 챕터에서 아무것도 설정하지 않으면 라운드 로빈 방식 이라고 말씀드렸었습니다. nginx 에서는 라운드로빈 방식 외에 추가적으로 두 가지 방식을 더 지원합니다.
- round-robin: 라운드 로빈방식으로 서버를 할당
- least-connected: 커넥션이 가장 적은 서버를 할당
- ip-hash: 클라이언트 IP를 해쉬한 값을 기반으로 특정 서버를 할당
저희는 least-connected 방식을 사용하겠습니다.
server {
# 생략...
}
upstream express-app {
# least-connected 설정
least_conn;
# IP hash 기반으로 하고 싶은 경우 아래로 설정
# ip_hash;
server 127.0.0.1:7000;
server 127.0.0.1:8000;
server 127.0.0.1:9000;
}
여기서 server 디렉티브에는 특정 옵션들이 사용될 수 있습니다. 생활코딩 에서 정리한 표를 가져왔습니다.
- weight=n: 업스트림 서버의 비중을 나타낸다. 이 값을 2로 설정하면 그렇지 않은 서버에 비해 두배 더 자주 선택된다.
- max_fails=n: n으로 지정한 횟수만큼 실패가 일어나면 서버가 죽은 것으로 간주한다.
- fail_timeout=n: max_fails가 지정된 상태에서 이 값이 설정만큼 서버가 응답하지 않으면 죽은 것으로 간주한다.
- down: 해당 서버를 사용하지 않게 지정한다. ip_hash; 지시어가 설정된 상태에서만 유효하다.
- backup: 모든 서버가 동작하지 않을 때 backup으로 표시된 서버가 사용되고 그 전까지는 사용되지 않는다.
저희는 demo-app-1 에 가중치 2 를 두고 5번의 실패 확인, 실패 후 30초 응답시간 설정, 마지막으로 demo-app-3는 모든 앱이 죽은 경우에만 사용 하도록 하겠습니다.
그럼 설정값을 다시 바꾸겠습니다.
server {
# 생략...
}
upstream express-app {
least_conn;
# demo-app-1 에 가중치 2 부여
# 5번의 실패 확인, 실패 후 30초 응답시간 설정
server 127.0.0.1:7000 weight=2 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8000 max_fails=3 fail_timeout=30s;
# demo-app-3는 모든 앱이 죽은 경우에만 사용
server 127.0.0.1:9000 backup;
}
마지막으로 저희는 proxy 방식을 사용했는데 이 때 에러처리할 항목을 추가해 주도록 하겠습니다. 이제 최종 config 파일의 항목은 아래와 같을 것입니다.
server {
listen 80;
server_name {nginx server ip 혹은 domain 설정};
access_log /var/log/nginx/proxy/access.log;
error_log /var/log/nginx/proxy/error.log;
location / {
include /etc/nginx/proxy_params;
proxy_pass http://express-app;
# 에러처리 항목 추가
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
}
}
upstream express-app {
least_conn;
server 127.0.0.1:7000 weight=2 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:9000 backup;
}
완성되었으면 nginx 를 재실행시켜 준 뒤 접속해 보도록 하겠습니다.
몇번을 반복해도 first app 과 second app 만 돌아가면서 보여줄 것으로 예상됩니다.
그럼 이번에 backup 을 확인하기 위해 실행되고 있는 두 개의 앱(demo1, demo2)을 중지시키겠습니다. 포스팅을 따라 오셨다면 두 개의 앱은 서로 다른 프로세스로 띄웠습을 알 수 있습니다. 즉, 이 두개의 프로세스를 죽이면 앱 실행을 중지시킬 수 있습니다.
# demo-app-1 프로세스 죽이기
$ kill -9 `ps -ef | grep demoapp-1 | awk '{print $2}'`
# demo-app-2 프로세스 죽이기
$ kill -9 `ps -ef | grep demoapp-2 | awk '{print $2}'`
이제 다시 접속해주세요!!
아무리 접속해도 위 스크린샷 처럼 Third App 만 나올 것입니다.
마지막으로 connection 이 연결이 끊겼는지 확인하겠습니다. nginx 설정 할 때 에러는 /var/log/nginx/proxy/error.log
에 저장하도록 했으니 아래 명령어를 이용해면 어떤 에러가 발생했는지 확인 할 수 있습니다.
$ cat /var/log/nginx/proxy/error.log
아래 사진처럼 connection, upstream 에러가 발생하시면 저희가 생각한 대로 작동하고 있었다는 것을 알 수 있습니다.
마무리
사실 어떻게 보면 이번 포스팅은 proxy 서버구축의 연장선상에 있다고 해도 무방하다고 생각합니다. 그래서 그런지 이번 포스팅은 조금 빨리 끝낸 느낌이 있군요.
이 강력하면서도 쉬운 로드 벨런싱 기능 덕분에 kubernetes 의 공식 ingress 도 nginx 로 되어 있지 않나 합니다. 만약 이 설정방법을 모른다면 kubernetes 를 이용할 때 더 헤머게 되겠죠? 그런 이유에서라도 한번쯤은 이렇게 기본적인 부분들을 보는 것도 도움이 되지 않나 싶습니다.
참고 자료
http://nginx.org/en/docs/http/load_balancing.html
https://www.lesstif.com/pages/viewpage.action?pageId=35357063
https://opentutorials.org/module/384/4328
https://seokjun.kim/haproxy-and-nginx-load-balancing/
Subscribe via RSS