Recording/멋쟁이사자처럼 BE 13기

[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_74일차_"Docker 로드밸런싱"

LEFT 2025. 3. 26. 17:46

🦁멋쟁이사자처럼 백엔드 부트캠프 13기 🦁
TIL 회고 - [74]일차

🚀74차에는 Docker의 로드밸런싱을 이용하여 각 컨테이너 별 순차처리에 대해서 학습할 수 있었다.

학습 목표 : docker-compose.yml 설정이나 nginx를 통해 컨테이너 별로 서버를 열어 관리할 수 있도록 해야함

학습 과정 : 회고를 통해 작성


웹서버와 웹어플리케이션서버

➡️웹서버 = 정적 페이지 처리
➡️WAS = 동적 페이지 처리

주요 역할 정적 페이지(HTML, CSS, JS) 제공 동적 페이지(데이터 처리, 비즈니스 로직 실행) 제공
처리 방식 클라이언트 요청 → 정적 리소스 반환 클라이언트 요청 → 애플리케이션 로직 처리 → 동적 응답 생성
예시 Apache HTTP Server, Nginx Tomcat, JBoss, WebLogic

 

Nginx

  • HTTP웹서버, Reverse Proxy, 로드밸런서, 메일 프록시 등의 기능을 수행할 수 있는 가벼운 오픈소스 서버
  • 이벤트 기반 아키텍처로 설계되어 동시에 많은 연결을 효율적으로 처리 가능
  • 정적파일 서비스 제공 (HTML, CSS, JS, 이미지 등의 정적자원)
  • Reverse Proxy : 외부 클라이언트 요청을 내부 서버로 중계, 서버 IP나 포트를 감추고 보안을 강화함
  • TLS / SSL 처리 : HTTPS 종단, 인증서 관리 등

로드밸런싱 (Load-Balancing)

  • 여러 서버 (ex. Spring Boot 애플리케이션)로 트래픽을 분산하여 부하를 균등하게 나누고 장애 시 다른 서버로 우회
  • 여러 서버에 트래픽을 분산시켜 부하를 줄이고 성능을 향상시키는 기술
  • ex. 로드밸런서 없는 단일 서버
    사용자가 www.example.com 접속 ➡️ 서버 1이 모든 요청 처리 (과부하 가능성)

    ex. 로드 밸런서 적용
    사용자가 www.example.com 접속 ➡️ 로드 밸런서가 여러 서버 중 하나를 선택
    (=서버1, 서버2, 서버3 중 분산 처리)

 

로드밸런싱의 알고리즘 (=방식)

1. 라운드 로빈 (Round Robin)

  • 요청을 순차적으로 서버에 배분
  • (A → B → C → A → B → C ... )

2. 가중 라운드 로빈 (Weighted Round Robin)

  • 서버 성능에 따라 가중치(weight)를 부여해 트래픽 배분
  • 성능이 높은 서버는 더 많은 요청을 받음
    ex.
    서버 A (가중치 3) → 3번 요청 처리
    서버 B (가중치 2) → 2번 요청 처리
    서버 C (가중치 1) → 1번 요청 처리

▶️실습 - Nginx 컨테이너로 로드밸런싱

  • Nginx 컨테이너 2개 (각각 Reverse Proxy와 로드 밸런서 설정)
  • 외부에서 들어오는 요청을 뒤에 있는 Spring Boot API 컨테이너 3대로 분산
  • Spring Boot API 컨테이너 3대
    - 각각 다른 포트로 동작하거나 Docker 네트워크에서 독립된 IP로 띄워 로드밸런싱 테스트가능
    - Docker 환경에서 모두 운영

  • 목적 : Nginx가 Spring Boot API 서버에 라운드로빈 (Round Robin) 으로 요청 전달하는 것을 실습

  • 컨테이너 간 네트워크 (ex. bridge 모드 + 사용자 정의 네트워크) 를 활용하여 통신

  1. ./gradlew build -x test 로 빌드를 진행
  2. java -jar ./build/libs/ 안에 있는 jar를 실행해봐서 서버가 잘 동작하는지 테스트
  3. 이미지 만들기 docker build -t todoapp:3.0 .

  • todoapp 3개가 잘 돌아가고 있는지 확인 ➡️9513, 9514, 9515 열려있는 중
  • todoapp자체는 9512에서 실행중

 

  • 각각 9513, 9514, 9515 로 접근해서 열 수 있었고 모두 접속이 가능한 상태

 

  • api1을 넣었을떄 우측 가장 위 cmd 에서 insert가 수행되었음을 실시간 로그로 확인할 수 있다. (9513포트)
  • 실시간 로그 확인 : docker logs -f [컨테이너ID]
  • 다른 9514, 9515 포트에서도 실시간 로그 추적을 확인 가능

설정 추가

Dockerfile

FROM nginx:latest
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80

nginx.conf

worker_processes  1;

events {
    worker_connections  1024;
}
http {
    upstream spring-backend {
        # 라운드 로빈 대상
        server api1:8080;
        server api2:8080;
        server api3:8080;
    }
    server {
        listen 80;
        location / {
            proxy_pass <http://spring-backend>;
        }
    }
}

  • 이 설정파일들을 만든 디렉토리에서 cmd를 열고
  • docker build -t mynginx:1.0 . : 이미지 만들기


▶️실습 - Nginx컨테이너 2대 실행

docker run -d ^
--name nginx1 ^
--network spring-net ^
-p 9601:80 ^
mynginx:1.0
docker run -d ^
--name nginx2 ^
--network spring-net ^
-p 9602:80 ^
mynginx:1.0

  • docker rm nginx1 nginx2 : 기존것들은 지우고 새롭게 만든다.
  • 9601, 9602 포트로 만들어준다

  • 데이터를 넣어보니 9514 포트에서 로그 추적

 

  • fff 값을 넣어보니 9513 포트에서 로그 추적

 

  • ggg값에는 9515 포트가 로그 추적

이처럼 순서대로 로드밸런싱이 라운드로빈 알고리즘 방식으로 순차적으로 동작하고 있음을 알 수 있다.

마찬가지로 9601이 아닌 9602포트인 nginx2 를 쓰더라도 둘 중 어느것을 접속해도 동일한 스프링부트 3대에 분산이 된다.

정리하자면

현재 서버가 5대가 구동 중인 상태

  • Nginx 서버: 2대 (9601, 9602 포트로 접근 가능)
  • Spring Boot API 서버: 3대 (9513, 9514, 9515 포트로 접근 가능)

Round Robin을 사용하면

  • 업스트림 설정 별도 weight(가중치)나 ip_hash를 넣으면 특정한 서버 (포트)가 더 많은 요청이 들어가도록 할 수 있다.
  • (ex. server api1:8080 weight = 2; /// server api2:8080 weight = 1;)
    ➡️3개의 요청 시 api1, api1, api2 처럼 api1에 더 할당되는 설정인 것

헬스 체크(Health Check)

  • 로드 밸런서(ex. Nginx)가 백엔드 서버(API 서버)의 상태를 주기적으로 확인하여
    정상적으로 작동하는 서버에만 트래픽을 전달하도록 하는 기능

  • 헬스 체크를 통해 응답하지 않거나 오류가 발생하는 서버를 자동으로 감지하고
    트래픽 분배 대상에서 제외하여 서비스의 안정성을 높일 수 있습니다.

  • 헬스 체크는 서버의 건강 상태를 실시간으로 감지하여 트래픽 분배를 제어하고,
    로그는 서버의
    상세한 동작 기록을 제공하여 문제 진단 및 분석에 활용

▶️실습 - 설정 파일로 5개의 컨테이너 동시 선언 및 생성

  • docker compose up -d 활용
  • http://localhost:[포트번호], http://localhost:[포트번호]로 로드밸런싱 확인해보기
services:
  # 1) MySQL 컨테이너
  mymysql:
    image: mymysql:1.0
    container_name: mymysql-1.0
    environment:
      - MYSQL_ROOT_PASSWORD=rootpass
      - MYSQL_DATABASE=testdb
      - MYSQL_USER=testuser
      - MYSQL_PASSWORD=testpass
    ports:
      - "3308:3306"
    networks:
      - todo-net
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-ptestpass"]
      interval: 10s
      timeout: 5s
      retries: 3

  # 2) Todo 애플리케이션 컨테이너
  todoapp1:
    image: todoapp:5.0
    container_name: todoapp1
    depends_on:
      mymysql:
        condition: service_healthy # DB가 먼저 시작된 후 todoapp을 띄움 (단, 실제 DB 초기화가 끝났는지는 보장X)
    ports:
      - "9091:8080"
    networks:
      - todo-net
    # healthcheck 제거

	#... todoapp2, todoapp3

  # 3) Nginx 컨테이너
  nginx:
    image: todo-nginx:1.0
    container_name: mynginx
    depends_on:
      - todoapp1
      - todoapp2
      - todoapp3 # todoapp이 시작된 뒤에 nginx 기동
    ports:
      - "9666:80"
    networks:
      - todo-net

networks:
  todo-net:
    driver: bridge
  • depends_on에 todoapp1, todoapp2, todoapp3 를 넣어 todoapp을 먼저 시작하도록 설정
  • mymysql은 기존에 사용중이던 mymysql DB와 충돌될 수 있으므로
    image로 1.0 태그로 새로 생성 후 컨테이너 이름도 mymysql-1.0으로 참조

🚀회고 결과 :

컨테이너 5개를 docker-compose.yml 파일로 한 번에 선언했을때 포트 충돌 및 이미 존재하는 DB 오류가 계속 발생해서 회고에서 시간이 오래걸렸다.

정확한 오류에 대해서 더 알아볼 필요가 있을 것 같다.

라운드 로빈 방식 또한 자격증 공부때에만 잠깐 접했었는데 도커와 함께 로드밸런싱의 알고리즘으로 배워보니 이해가 쉬웠다.

향후 계획 : 

- 포트충돌 해결법 공부
- nginx 활용법