- Django
- sql
- java
- 자바
- spring security 6
- select
- session
- 1차원 배열
- spring mvc
- @transactional
- AWS
- spring
- 프로그래머스
- nginx
- 데이터베이스
- ORM
- mysql
- spring boot
- SSL
- Git
- 스프링
- string
- DI
- 스프링부트
- 문자열
- springboot
- jpa
- join
- Docker
- PYTHON
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
개발하는 자몽
Nginx 로드 밸런싱 on Docker 본문
→😎Nginx의 로드 밸런싱 기능을 사용하게 된 이유는 아래 글을 참고😎
위와 같은 이유로 로드 밸런싱을 적용하게 되었다. Nginx를 사용한 이유는 적용 방식이 간단하고 L4, L7 스위치로 하는 방법과 달리 비용이 들지 않기 때문이다.
그리고 Nginx를 도입하기 전 같은 기능을 제공하는 AWS의 ELB(Elastic Load Balancing)와도 비교해 보았다. 자세한 내용은 잘 정리해 둔 아래 글을 참고!
상황
서버는 Docker 상에서 컨테이너로 하나로 돌아가고 있었다. 여기에 Nginx 컨테이너를 올리고, 서버도 컨테이너를 하나 더 추가하는게 목표였다.
체크리스트
본격적으로 적용하기 전 무엇을 해야 하는지 명확하게 정리하고 싶어서 체크 리스트를 정리했다.
✅ 한 대의 컨테이너에서 동작하던 서버를 두 대의 컨테이너로 나누기
- docker-compose.yml 수정
- `service` 블록에 추가
- 각 서버 컨테이너 별 port 설정
- `command`에 적는 port 설정
- `service` 블록에 추가
✅ Nginx 로깅을 위한 폴더 생성
- Dockerfile에 해당 동작을 하는 명령어 작성
✅ Docker에 Nginx를 컨테이너로 올리기
- docker-compose.yml 수정
- `service` 블록에 추가
- `volumes` 설정
- `호스트머신의_conf_위치 : nginx_컨테이너_내의_conf 위치`
✅ Nginx 로드 밸런싱 설정 하기
- Nginx 설정 파일(nginx.conf) 수정하기
- 호스트 머신에서 nginx.conf 생성하기
- `upstream` 설정으로 두 컨테이너 이어주기
- `keepalive` 설정 고려
코드 작성
docker-compose.yml
version: "3.5"
services:
_build_image:
image: ...
build: .
server1:
image: ...
restart: always
command: python ... runserver 0.0.0.0:4100
ports:
# - 4100:4100 # host_server_port:docker_server_port
- "4100" # host server 포트를 열지않고 컨테이너의 포트만 열어둠
depends_on:
- _build_image
server2:
image: ...
restart: always
command: python ... runserver 0.0.0.0:4100
ports:
# - 4101:4100 # host_server_port:docker_server_port
- "4100" # host server 포트를 열지않고 컨테이너의 포트만 열어둠
depends_on:
- _build_image
nginx:
image: nginx # tag the image with the name "nginx"
build: ./nginx # Dockerfile location for Nginx
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro # read-only
depends_on:
- server1
- server2
ports:
- "4100:80"
server1, server2
두 대의 컨테이너로 늘리기 위해서 `service` 블록에 server를 하나 더 추가했다. 단순하게 이름 뒤에 숫자를 추가해 주었다.
depends_on
`depends_on`은 어떤 서비스 이후에 실행할 건지 정한다(종속성 순서). nginx를 사용하는 이유가 로드밸런싱 때문이므로 서버 역할을 하는 두 컨테이너가 실행된 이후에 nginx 컨테이너가 실행되도록 위와 같이 작성했다.
ports
server1, server2 포트(주석 처리 X)는 아래와 같이 생각하면 된다.
- `command` : 컨테이너의 4100번 포트에서 서비스 실행
- `ports` : 호스트 머신의 포트는 열어두지 않고, 도커 컨테이너의 4100번 포트만 열어둔 상태
nginx의 포트는 서버와 다르게 `4100:80`로 설정되어 있는데 이는 호스트 머신의 4100번 포트를 컨테이너의 80번 포트에 연결한다는 의미이다.
이 상태에서 nginx(컨테이너)로 들어온 요청이 두 대의 서버(컨테이너)에게 전달되도록 `nginx.conf`에서 설정하면 된다.
Dockerfile in nginx directory
FROM nginx:latest
# remove nginx default configuration file
# RUN rm /etc/nginx/conf.d/default.conf -> ERROR : No such file or directory
# copy custom nginx configuration file
COPY ./nginx.conf /etc/nginx/nginx.conf
# for Nginx log
RUN mkdir -p /var/log/nginx/server
WORKDIR /var/log/nginx/server
RUN touch access.log error.log
참고로 Dockerfile, docker-compose.yml 파일과 같은 위치에 nginx 디렉터리를 생성했다. 이에 맞춰서 docker-compose.yml > services > nginx > build에 Dockerfile의 위치가 설정되어 있다. nginx 디렉터리에는 아래의 파일들이 있다.
- Nginx 이미지 생성을 위한 Dockerfile (서버 컨테이너와는 별도의 이미지를 사용하기 위함)
- Nginx 설정을 위한 nginx.conf
nginx.conf in nginx directory
worker_processes auto;
events {
worker_connections 1024;
}
http {
# log format
log_format upstream_time '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'rt=$request_time uct="$upstream_connect_time"';
# --- global log ---
# access_log /var/log/nginx/access.log upstream_time;
access_log off; # 전역 로그 비활성화
log_not_found off; # 파일을 찾을 수 없을 때 에러 로그 X
error_log /var/log/nginx/error.log crit; # 에러 로그, 로그 레벨 - critical
# for load balacing
upstream server {
server server1:4100;
server server2:4100;
keepalive 1024;
}
server {
listen 80;
location / {
proxy_pass http://server/; # upstream 이름
}
# --- server log ---
access_log /var/log/nginx/server/access.log upstream_time; # 접속 로그
error_log /var/log/nginx/server/error.log crit; # 에러 로그
}
}
worker_processes
Nginx는 하나의 Master Process와 여러 개의 Worker Process로 구성되어 실행된다. Master Process는 설정 파일을 읽고 유효성을 검사하며, Worker Process를 관리한다.
모든 요청은 Worker Process에서 처리된다. Worker Process의 개수는 설정 파일에서 정의되며, 정의된 프로세스 개수와 사용 가능한 CPU 코어 숫자에 맞게 자동으로 조정된다.
Worker Process의 개수는 물리적 CPU 코어 개수만큼 할당하는 것이 가장 이상적이며, 설정 파일에서 `auto`로 지정하면 자동으로 CPU 코어 개수에 알맞게 프로세스를 생성한다.
events
`events` 블록은 네트워크의 작동 환경을 설정하는 지시어를 제공한다. `http`, `server`, `local` 블록과는 상속 관계를 갖지 않는다. 아래 지시어들은 `events` 블록 안에서만 사용 가능하다.
- accept_mutex : LISTEN 소켓을 오픈하기 위한 accept mutex의 사용/해제를 설정한다.
- accept_mutex_delay : 자원 획득을 다시 시도하기 전에 `worker process`가 기다려야 하는 시간을 정의한다. `accept_mutex` 지시어가 `off`로 설정되어 있으면 이 값은 사용되지 않는다.
- worker_connections : `Worker Process`가 동시에 처리할 수 있는 접속자 수를 정의한다.
- worker_processes * worker_connections = 최대 접속자 수
http
http 블록은 Nginx로 들어오는 웹 트래픽에 대한 처리 방법과 방향을 설정해 준다.
🔹 upstream
upstream 서버는 다른 말로 Origin 서버라고 부른다. `proxy_pass`를 통해 Nginx 웹 서버가 받은 요청들을 여러 대의 애플리케이션 서버로 넘겨주는 역할을 한다.
http는 데이터 전송을 받기 위해 서버에 접속해서 데이터를 받은 다음, 연결을 끊는다. upstream 안에 있는 `keepalive`를 설정하면 이 연결을 일정 시간 유지 시켜준다. 따라서 지속되는 만큼 매번 연결을 하지 않아도 되기 때문에 성능 향상에 도움 된다.
🔹 server
server 블록은 가상 서버에 대해서 정의할 수 있는 곳이다. IP 주소 기반이나 server_name을 기반으로 정의할 수 있다.
- `listen` : Nginx는 80번 포트를 listening 하여 외부로부터 Nginx(컨테이너) 내부의 80번 포트로 요청을 받는다.
- `location` : 서버 안의 리소스에 대한 요청을 어떻게 응답해야 할지를 설정한다. 특정 파일과 특정 디렉터리에 대한 요청을 처리한다. (이 글을 읽고 location를 이해할 수 있었다!)
포트 정리
4100번 포트로 요청이 들어오면 Nginx(컨테이너)의 80번 포트(listen)로 연결되며, `upstream` 설정에 의해 server1(컨테이너) 또는 server2(컨테이너)로 요청이 전달된다. 요청이 전달될 서버가 선택되는 디폴트 방식은 라운드 로빈(Round Robin) 알고리즘이다. 그 외에 로드 밸런싱을 선택하는 방법으로는 `Least-connections`, `IP Hash`가 있다. 이와 관련된 자세한 내용은 공식 문서에 잘 나와있다.
회고
개인적으로 포트 설정이 생각보다 어려웠는데, 도커 공부를 제대로 하지 않아서 그랬던 것 같다🙃
도커 포트 관련해서 잘 정리해 둔 블로그 글이 있는데, 읽어보는 것을 추천한다!
'Architecture & Tool > Nginx' 카테고리의 다른 글
[NGINX] HTTPS 서버 구성하기 (Let's Encrypt) (2) | 2024.04.12 |
---|