앞선 글에서 Express를 이용해 클러스터를 구성해 여러 워커를 가진 서버를 띄워보았습니다. 이번에는 제작한 웹 서버를 이용해 이중화 구성을 한 뒤 nginx를 이용해 로드밸런서를 만들어 그럴듯한 운영 환경을 구성해 보도록 하겠습니다.
0. 사전 준비.
이 글에서는 앞선 글에서 작성한 웹 서버를 사용합니다. 이전 글을 참고해 웹 서버를 준비해 주세요.
Nginx를 사용할 예정이니 미리 서버를 다운로드하여 주셔도 좋습니다. 단, Docker를 이용하실 분은 별도의 Nginx를 준비하지 않으셔도 됩니다.
모든 결과물을 한번에 배포하기 위해 docker-compose를 사용할 예정입니다. docker-compose를 준비해 주세요.
- 2020/06/18 - [JavaScript] - Express를 이용한 서버 클러스터 구성.
- 2020/01/14 - [Linux] - [Ubuntu] Docker-compose 설치하기
1. Nginx를 이용한 이중화 구성.
이제 우리가 만든 서버는 여러 개의 워커를 갖고 도커 위에서 동작합니다. 이제 안정성을 위해 두 개의 서버를 올려두고 그 앞에 Nginx를 둬서 로드밸런서의 역할을 하도록 합시다.
우리가 할 일은 단지 nginx의 config파일을 추가하는것 뿐 입니다. 아무런 추가 작업을 진행하지 않은 채 설정 파일을 추가해 주는 것만으로도 로드밸런서를 구성할 수 있습니다.
새로운 폴더인 my-nginx를 생성하고 그 안에 nginx 폴더를 만들어줍니다. 그리고 그 안에 nginx.conf 파일을 작성합니다.
#./nginx/nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
사실 위의 설정파일은 nginx를 설치하면 가장 기본으로 들어가 있는 값입니다. 스킵하셔도 무방합니다만 나중에 수정된 값을 사용할 수 있으므로 넣어주었습니다.
설정 파일을 살펴봅시다. nginx.conf의 맨 아래 include를 보세요. 이 include가 의미하는 바는 /etc/nginx/conf.d폴더 내의 모든 conf파일을 가져와 http 아래에 두겠다는 뜻입니다.
따라서 우리가 다음에 작성할 설정 파일이 바로 아래 추가될 것이란 의미가 됩니다. 이 외에도 nginx의 default 페이지가 담긴 default.conf도 추가될 것입니다.
그다음으로nginx 안에 conf.d 폴더를 생성 한 뒤 my-react-lb.conf파일을 작성합니다.
# ./nginx/conf.d/my-react-lb.conf
upstream my-react {
#least_conn;
#ip_hash;
server localhost:3000 weight=10 max_fails=3 fail_timeout=10s;
server localhost:3001 weight=10 max_fails=3 fail_timeout=10s;
}
server {
listen 8080;
server_name localhost;
location / {
proxy_pass http://my-react;
}
}
이 파일의 폴더 경로도 실제 리눅스 시스템에서 nginx가 위치하는 폴더의 경로를 맞추기 위함이므로 임의의 폴더에 작성하셔도 됩니다.
server항목을 먼저 봅시다. 이 서버는 8080 포트에서 동작하고 리버스 프록시로 동직 합니다. 즉, localhost:8080으로 들어오면 내부적으로 my-react로 연결시킨다는 뜻입니다. 이때 연결하는 my-react가 위에서 정의한 upstream입니다.
upstream 내부에 두 개의 서버가 정의되어 있습니다. 유저가 들어올 때마다 번갈아 가면서 3000, 3001 포트에 올라간 서버가 접속을 수용할 겁니다. 정리하자면 localhost:8080으로 접속하게 되면 내부적으로 로드밸런싱을 거쳐 localhost:3000, locahost:3001로 이동하게 된다는 겁니다.
주석 처리한 least_conn은 번갈아 가면서 수용하는 게 아닌 현재 접속이 가장 적은 곳에 우선 접속할 수 있도록 하는 기능이며 ip_hash는 접속한 사용자의 ip를 해싱해서 같은 사용자는 같은 서버로 접속할 수 있게 해 세션 문제 등을 해결할 수 있도록 하는 기능입니다.
따라서 메인 서버에 위의 설정 파일을 갖는 nginx를 설치한 뒤 Docker로 3000, 3001 포트에 앞서 작성한 웹 서버를 두대 올려 둔 후 localhost:8080으로 접속하면 정상적으로 로드밸런싱이 동작할 겁니다.
2. Docker compose를 이용해 한번에 배포하기.
사실 위와 같이 배포하기 위해서는 서버에 nginx를 직접 설치해야 하며 그 서버에서 Docker가 동작 해 웹서버가 돌아야 합니다. 즉, nginx가 돌아가는 localhost에 웹서버가 동작해야 한다는 뜻입니다.
서버에 접속해 nginx를 설치하고 conf 파일을 수정해준 뒤 그 서버에 docker를 설치하고 docker 명령어로 두 개의 웹서버를 실행시키는 과정이 필요하게 됩니다. 이것만 해도 귀찮아지기 시작합니다. 게다가 수작업이 들어가는 만큼 오류의 가능성도 높아집니다.
이제 좀 더 편하게 위 과정을 다음과 같이 변경할 겁니다.
- Nginx를 직접 설치하지 않고 Docker Image로 빌드 해 Docker 명령어로 배포한다
- 명령어를 미리 작성해 둬 한번에 여러 이미지를 배포한다.
그러기 위해선 docker-compose와 nginx를 docker image로 빌드하는 작업이 필요합니다. 우선 nginx를 docker image로 빌드하기 위해 Dockerfile을 작성합니다.
# ./Dockerfile
FROM nginx:stable
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./nginx/conf.d/my-react-lb.conf /etc/nginx/conf.d/my-react-lb.conf
CMD ["nginx", "-g", "daemon off;"]
매우 간단합니다. nginx 안정화 버전을 받아 우리가 작성한 conf파일을 복사 한 뒤 실행합니다. 이제 다음 명령어로 docker image를 빌드합니다.
$ docker build . -t my-nginx:0.0.1
그리고 빌드한 이미지를 실행해 봅시다.
$ docker run -itd -p 3000:3000 my-react:0.0.1
$ docker run -itd -p 3001:3000 my-react:0.0.1
$ docker run -itd -p 8080:8080 my-nginx:0.0.1
localhost:8080으로 이동하면 어떻게 될까요?
에러가 보이는 이유에 대해 생각해 봅시다. 우리는 nginx를 도커로 돌렸으며 my-react-lb.conf의 upstream에 server로 localhost:3000와 localhost:3001을 등록했습니다. 과연 nginx 컨테이너 내에서 이 두 서버에 접속할 수 있을까요?
궁금하신 분은 다음 명령어로 한번 테스트해보시기 바랍니다.
$ docker exec -u 0 -it (my-nginx 컨테이너 ID) bash
$ apt-get update
$ apt-get intall telnet
$ telnet localhost 3000
당연히 독립된 네트워크 이므로 접속을 할 수 없습니다.
그러므로 우리는 Docker compose를 이용해야 합니다. Docker compose는 같이 배포되는 컨테이너끼리 미리 정의된 이름으로 접속이 가능합니다. 마치 도메인 네임처럼 말입니다.
최상위 폴더로 이동해 docker-compose.yml 파일을 생성합니다 저 같은 경우엔 my-react와 my-nginx를 포함하고 있는 폴더에 작성하였습니다.
그리고 docker-compose.yml을 다음과 같이 작성해 줍니다.
# docker-compose.yml
version: "3"
services:
my-react-A:
image: my-react:0.0.1
ports:
- "3000:3000"
my-react-B:
image: my-react:0.0.1
ports:
- "3001:3000"
nginx:
image: my-nginx:0.0.1
ports:
- "8080:8080"
여기서 서비스 항목을 봅시다. 우리는 my-react-A, my-react-B, nginx 이렇게 총 세 개의 컨테이너를 생성하도록 작성하였습니다. 당연히 이름은 변경해도 됩니다. 서비스 내의 image는 도커 이미지를 의미합니다. ports는 docker run의 -p 옵션과 동일합니다.
이런 식으로 docker-compose를 구성하게 되면 위의 세 컨테이너 간에는 서비스에 작성한 이름으로 서로 접근이 가능해집니다. 따라서 우리의 my-react-lb.conf가 바뀌어야 함을 의미하죠.
# ./nginx/conf.d/my-react-lb.conf
upstream my-react {
#least_conn;
#ip_hash;
server my-react-A:3000 weight=10 max_fails=3 fail_timeout=10s;
server my-react-B:3000 weight=10 max_fails=3 fail_timeout=10s;
}
server {
listen 8080;
server_name localhost;
location / {
proxy_pass http://my-react;
}
}
그리고 다시 도커 이미지를 빌드해 줍니다.
$ docker build ./my-nginx -t my-nginx:0.0.2
잊지 마세요. 태그에 버전을 0.0.2로 변경하였으므로 당연히 docker-compose를 변경해야 합니다. 이제 docker-compose를 통해 컨테이너를 올려보겠습니다.
$ docker-compoase up -d
우리가 작성한 docker-compomse파일 대로 컨테이너가 실행되었습니다. 이제 localhost:8080으로 이동해 보세요. react페이지가 보이시나요?
그렇다면 우리가 어떤 서버로 접속했는지 알아보기 위해 localhost:8080/where로 이동해 봅시다.
위와 같이 접속할 때마다 서버 정보가 변경되는 것을 확인할 수 있습니다. 실제 운영환경에선 이렇게 하면 서버가 바뀌며 세션정보가 유실되므로 앞서 설명한 ip_hash와 least_conn옵션을 켜고 서버를 배포해야 합니다.
'Programming' 카테고리의 다른 글
[JIRA] Ubuntu Server 20.04에 Jira 설치하기. (0) | 2020.06.25 |
---|---|
[Redmine] Ubuntu 20.04에 Redmine 설치하기. (3) | 2020.06.24 |
[Jenkins] 빌드 결과를 이메일로 알려주기 (0) | 2020.05.07 |
[Jenkins] NodeJS 프로젝트 빌드하기. (0) | 2020.05.07 |
[Jenkins] SSH를 이용한 GitHub연동 (0) | 2020.04.29 |