IT·테크/인프라

Docker Compose scale과 nginx 로드 밸런싱으로 부하 나누기

레루루 2026. 7. 28. 21:53
반응형

이 글은 2022년 10월 기준 기록입니다.

 

동일한 서비스를 여러 개 작동시켜 각 컨테이너에게 전달되는 부하를 줄여 봅니다.

--scale 옵션이 같은 서비스를 여러 개 띄운다

Docker Compose를 실행할 때 --scale 옵션을 사용하면 동일한 서비스를 여러 개 작동시킬 수 있습니다.

$ docker compose --env-file test.env up -d --scale spring-crud=3 --scale postgres-db=2 --no-recreate

 

--scale에는 서비스 이름에 대한 개수를 지정하면 되고, 지정하지 않은 서비스는 자동으로 1로 지정됩니다. 함께 붙인 --no-recreate 옵션은 변경이 필요한 컨테이너만 조작하도록 해 줍니다. 이미 잘 돌고 있는 컨테이너까지 다시 만들 이유는 없기 때문입니다.

호스트 포트는 중복 할당할 수 없다

여기서 곧바로 걸리는 제약이 있습니다. 호스트 포트를 중복으로 할당할 수 없다는 점입니다. 같은 서비스를 세 개 띄웠는데 셋 다 호스트의 8080을 쓸 수는 없습니다.

 

해법은 두 갈래입니다. 호스트 포트의 범위를 주거나, 부하 분산을 담당하는 컨테이너를 활용하는 것입니다.

첫 번째 해법 — 호스트 포트를 범위로 준다

.env 파일에서 해당 서비스가 사용할 호스트 포트를 범위로 지정합니다.

# postgreSQL
POSTGRES_USER=springtest  # postgres 슈퍼유저 생성
POSTGRES_PASSWORD=springtest # postgres 슈퍼유저 비밀번호
POSTGRES_DB=postgres1 # postgres default db

INIT_LOCAL_CONTEXT=.
INIT_LOCAL_FOLDER_NAME=init_db

# spring boot app
SPRING_DATASOURCE_URL=jdbc:postgresql://load-balancer:5432/postgres1
SPRING_DATASOURCE_USERNAME=springtest
SPRING_DATASOURCE_PASSWORD=springtest

SPRING_PROFILES_ACTIVE=dev # application-dev.properties 선택

JAR_FILE=app.jar

# ===================== 해당 서비스가 사용할 호스트 포트를 범위로 지정
HOST_PORT=8080-8085 
# ==================================================================
INTERNAL_PORT=8080 # 내부 스프링 앱이 compose 내부에 노출할 포트 번호

# nginx Load Balancer
EXTERNAL_PORT=81 # 호스트에 노출될 포트 번호
LOAD_BALANCER_APP_PORT=80 # 내부 로드밸런서가 리슨할 웹 포트 번호
LOAD_BALANCER_DB_PORT=5432 # 내부 로드밸런서가 리슨할 디비 포트 번호

 

이렇게 하면 호스트로 노출되는 포트를 여러 개 사용하여 각각 해당하는 컨테이너로 접근할 수 있습니다. 다만 접근하는 쪽이 포트 번호를 골라야 한다는 점은 그대로 남습니다.

두 번째 해법 — nginx를 앞에 세운다

부하 분산을 컨테이너 하나에 맡기면 바깥에서는 진입점 하나만 알면 됩니다. nginx를 이용해 구현합니다.

load-balancer:
    image: nginx
    ports:
      - ${EXTERNAL_PORT}:${LOAD_BALANCER_APP_PORT}
    expose:
      - ${LOAD_BALANCER_DB_PORT}
    depends_on:
      - spring-crud
      - postgres-db
    volumes:
      - ./nginx-docker/load-balancer.conf:/etc/nginx/nginx.conf.template
    environment:
      - INTERNAL_PORT=${INTERNAL_PORT}
    # ================= nginx 설정 파일에서 환경 변수를 사용하기 위해 envsubst를 사용한다.
    command: /bin/bash -c "envsubst < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && nginx -g 'daemon off;'"

 

volumes를 이용해 nginx 설정 파일을 적용합니다. 다만 설정 파일 안에서도 환경 변수를 쓰고 싶으므로, 파일을 곧바로 nginx.conf로 넣지 않고 nginx.conf.template으로 넣은 뒤 envsubst로 치환한 결과를 nginx.conf로 만들어 nginx를 기동합니다.

 

설정 파일은 다음과 같습니다.

user nginx;

events { 
    worker_connections   1000; 
} 

stream {
    upstream postgres {
        server postgres-db:5432;
    }

    server {
        listen 5432;
        proxy_pass postgres;
    }
}

http {
    server {
        listen 80;
        location / {
            proxy_pass http://spring-crud:${INTERNAL_PORT};
        }
    }
}

DB는 stream 블록에서 TCP 수준으로, 웹은 http 블록에서 proxy_pass로 넘깁니다. 두 경우 모두 대상이 개별 컨테이너가 아니라 Compose의 서비스 이름(postgres-db, spring-crud)이라는 점이 핵심입니다. 서비스 이름으로 보내면 스케일된 컨테이너들로 요청이 흩어집니다.

전체 구조는 이렇게 됩니다. 호스트의 80 포트로 들어온 요청은 Compose 안의 NGINX 로드 밸런서에 도착하고, 로드 밸런서는 8080 포트로 동작하는 세 개의 Spring Boot App과 5432 포트로 동작하는 두 개의 PostgreSQL로 요청을 나눠 보냅니다. 어떤 .env를 물릴지는 --env-file 옵션으로, 스프링이 읽을 프로필은 application*.properties로 각각 고릅니다.

반응형