Post

Nginx와 Flask로 Load Balancing 테스트하기

Nginx와 Flask로 Load Balancing 테스트하기

nginx

What is Load-Balancing?

로드 밸런싱이란 트래픽이 급증하거나 여러 사용자가 동시에 접속할 때, 여러 대의 서버에 클라이언트의 요청을 분산시켜 개별 서버의 과부하를 막고, 전체 서비스의 안정성과 성능을 높이는 기술이다.

즉,

1
2
• 하나의 서버에만 요청이 몰려 서버가 느려지거나 다운되는 걸 방지하고,
• 여러 대의 서버가 함께 트래픽을 처리하도록 해 확장성과 가용성을 높여준다.

Nginx

Nginx(엔진엑스)웹 서버리버스 프록시 서버 역할을 동시에 할 수 있는, 가볍고 빠른 오픈소스 소프트웨어이다.
쉽게 말해 Nginx는 웹 서버이자 로드밸런서로, 빠르고 가볍게 트래픽을 효율적으로 관리하는 소프트웨어이다.

What is Reverse-Proxy?

클라이언트(브라우저)의 요청을 받아 실제 서버(백엔드)에 대신 전달하고, 그 응답도 다시 클라이언트에 돌려주는 중간 서버이다.

Reverse-Proxy는 사용자가 직접 백엔드 서버에 접속하지 못하게 하고, Nginx(혹은 다른 프록시 서버)가 모든 요청을 받아서 내부 서버에 대신 전달한다.

1
• 사용자는 실제로는 프록시 서버만 보게 되고,프록시가 내부적으로 Flask, Spring, Node 등 여러 백엔드 서버들과 통신한다.

Purpose

1
2
3
4
5
•	보안 강화: 직접 백엔드가 노출되지 않으니 보안이 좋아짐
•	로드 밸런싱: 여러 서버에 트래픽 분산 가능
•	SSL 종료: SSL 인증서 관리도 프록시에서
•	캐싱: 자주 쓰는 정적 자원(이미지 등)을 프록시가 직접 응답 가능
•	접근 제어: 특정 IP, 경로 등에 대한 제어 가능

Flow Chart

내가 테스트하고자 하는 구조는 다음과 같다. localhost:80 으로 접속하면 서버1,서버2,서버3으로 접속할 수 있게끔 하려고 한다.

  • Web Client는 Nginx서버 하나만을 바라보고 있다
  • NginxWeb ClientFlask서버 3개를 바라보고 있다.
  • 각 서버의 port는 순차적으로 5001,5002,5003으로 설정했다.

flow

Local에서 Flask 로 테스트 서버를 3개 띄어놓고 NginxDocker로 띄울 것이다.

Directory Tree

1
2
3
4
.
├── Dockerfile
├── app.py
└── default.conf
  • Dockerfile : Nginx를 Docker 띄우기 위한 파일
  • app.py : Flask server
  • default.conf : Nginx 설정 파일

    Python Server 띄우기

    app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask
import sys

app = Flask(__name__)

@app.route("/")
def hello():
    return f"Hello from Flask server {sys.argv[2]} (port {sys.argv[1]})"

if __name__ == "__main__":
    port = int(sys.argv[1]) if len(sys.argv) > 1 else 5000
    server_id = sys.argv[2] if len(sys.argv) > 2 else "no-id"
    app.config["SERVER_ID"] = server_id
    app.run(port=port)
  • sys.argv[1] , sys.argv[2]
  • 매개변수는 서버가 정상적으로 Load Balancing 되었음을 테스트하기 위해 넘겨주도록 만들었다.

    Command

    각기 다른 터미널에서 다음의 명령어를 실행한다.

    1
    2
    3
    
    python3 app.py 5001 one 
    python3 app.py 5002 two
    python3 app.py 5003 three
    

예시 실행 결과

flow-nginx

Docker

Dockerfile

1
2
3
4
5
FROM nginx:alpine

COPY ./default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

mount하는 파일명은 커스텀해도 문제 없다.

Nginx config file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
upstream flask_app {
    server host.docker.internal:5001;
    server host.docker.internal:5002;
    server host.docker.internal:5003;
}

server {
    listen 80;

    location / {
        proxy_pass http://flask_app;
        proxy_set_header Host $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;
    }
}
  • host.docker.internal : Nginx 서버는 Docker위에서 실행되기 때문에 Docker 내부에서 바라보는 로컬 환경은 host.docker.internal 설정하면 된다. localhost로 설정하면 Docker 내부의 주소(즉,nginx 컨테이너 자신)로 mapping되어 엉뚱한 주소를 넘겨주게 된다.

Docker image build

Dockerfile, app.py, default.conf이 같이 존재하는 디렉토리 위에서 다음과 같은 커맨드를 실행시킨다. image를 빌드하는 것이다.

1
docker build -t nginx-test .

Dockerfile를 사용하여 nginx-test라는 이름의 이미지를 생성하는 것이다. 이미지 이름은 원하는대로 설정하면 된다. 위에 명령어 실행 후 이미지가 정상적으로 생성이 되었는지 확인해 보자.

1
docker images
  • 실행 결과
    1
    2
    
    REPOSITORY TAG      IMAGE ID       CREATED         SIZE
    nginx-test latest  903baa599c27   46 hours ago    80.2MB
    

    본인이 설정한 이름의 image명이 보인다면 성공이다.

Docker container 올리기

1
docker run -d --name nginx-test-container -p 80:80 nginx-test
  • -d : 백그라운드에서 실행하는 옵션
  • --name : 컨테이너의 이름을 설정하는 옵션
  • -p: 포트 설정하는 옵션
  • nginx-test : 본인이 만들었던 이미지 이름

컨테이너가 올라갔는지 확인해보자

1
docker ps
1
2
CONTAINER ID   IMAGE        COMMAND                   CREATED          STATUS          PORTS                                 NAMES
dd1c05b9f220   nginx-test   "/docker-entrypoint.…"   11 minutes ago   Up 11 minutes   0.0.0.0:80->80/tcp, [::]:80->80/tcp   nginx-test-containe

본인이 설정한 컨테이너 명이 보인다면 성공이다.

Result

이제 모든 설정을 마쳤으니 결과를 확인해보자.

localhost:80으로 접속해보고 게속 새로고침을 눌러보자. 5001,5002,5003 포트로 순차적으로 접속이 됌을 확인할 수 있다.

nginx_result

This post is licensed under CC BY 4.0 by the author.