도커 실습 10 - Nginx Proxy Manager 로드밸런싱 > 클라우드네이티브

본문 바로가기

[실습] 도커 실습 10 - Nginx Proxy Manager 로드밸런싱

필기자
2025-11-04 18:40 196 0

본문

목차
  • 로드밸런싱(Load Balancing)이란?
  • 네트워크 별칭(network-alias) 개념
  • mytaskapi 컨테이너 3개로 확장 (별칭 부여)
  • Redis 확장 설치 (세션 공유용)
  • 네트워크 별칭 정보 확인
  • Nginx Proxy Manager Custom Locations 라우팅
  • 동작 확인
  • 라운드로빈(round-robin) 검증
  • 장애 대응(failover) — 컨테이너가 죽으면?
  • (참고) 가중치(%) · 헬스체크 (upstream 방식)

■ 최종 시스템 구성도
gclb_diagram.png


1. 로드밸런싱(Load Balancing)이란?
  • 들어오는 요청을, 동일한 역할의 여러 서버(컨테이너)에 고르게 분산
  • 목적 : 부하 분산 · 가용성 향상 · 무중단 수평 확장
  • 실습 9의 단일 mytaskapi → 동일 컨테이너 3개(mytaskapi_1·2·3)로 확장, 리버스 프록시(Nginx Proxy Manager)가 번갈아(round-robin) 분배

2. 네트워크 별칭(network-alias) 개념
  • 컨테이너 본래 이름(mytaskapi_1 등) 외에 추가로 붙이는 DNS 이름
  • 여러 컨테이너가 같은 별칭을 공유 가능 → 그 별칭 = 해당 컨테이너들의 묶음(그룹). 여기선 taskapi_pool 별칭을 단 3개만 한 묶음
  • 그 별칭으로 조회 시 도커 내장 DNS(127.0.0.11)가 묶인 컨테이너 IP 전부를 응답
  • 자동 분산 : 별도 설정 없이, DNS가 요청마다 IP 순서를 번갈아(round-robin) 응답 → nginx가 매번 다른 컨테이너로 연결
  • 어느 컨테이너가 처리? : 응답만으론 구분 불가(3개 코드 동일) → 각 컨테이너 로그(docker logs)로 집계해 확인 (아래 8번)
핵심 전제 — 세션 공유로 무상태(stateless)
  • 3개 컨테이너 = 동일 이미지(myphpimage) + 동일 코드(~/php/task) 마운트
  • 로그인 세션 = 실습 8~9에서 만든 redis로 공유 → 어느 컨테이너로 가도 동일 상태 유지
  • 상태를 컨테이너 밖(redis)으로 분리 = 무상태 → 자유로운 복제·분산(로드밸런싱) 가능

3. mytaskapi 컨테이너 3개로 확장 (별칭 부여)
  • 동일 컨테이너 3개(mytaskapi_1·2·3) 생성, 세 개 모두 같은 별칭 --network-alias taskapi_pool 부여
  • 결과 : taskapi_pool 이름 하나 = 3개 컨테이너 IP (도커 DNS 라운드로빈)

docker run -d --name mytaskapi_1 -p 8012:80 --network php-mysql --network-alias taskapi_pool -v ~/php/task:/var/www/html myphpimage
docker run -d --name mytaskapi_2 -p 8022:80 --network php-mysql --network-alias taskapi_pool -v ~/php/task:/var/www/html myphpimage
docker run -d --name mytaskapi_3 -p 8033:80 --network php-mysql --network-alias taskapi_pool -v ~/php/task:/var/www/html myphpimage

gclb_dockerps.png

4. Redis 확장 설치 (각 컨테이너에 세션 공유용)
  • 3개 컨테이너가 redis로 세션을 공유해야 무상태(stateless)가 됨 → 각 컨테이너의 PHP에 redis 확장을 설치해야 함 (실습 8~9에서 mytaskapi · myuserapi 에 했던 것과 동일)
  • 새로 만든 3개 컨테이너(mytaskapi_1 · 2 · 3) 각각에 아래 과정을 반복

# mytaskapi_1 에 redis 확장 설치
docker exec -it mytaskapi_1 /bin/bash
pecl install redis
docker-php-ext-enable redis
exit

# mytaskapi_2 · mytaskapi_3 도 동일하게 반복
docker exec -it mytaskapi_2 /bin/bash
pecl install redis
docker-php-ext-enable redis
exit

docker exec -it mytaskapi_3 /bin/bash
pecl install redis
docker-php-ext-enable redis
exit
  • PECL : C 기반 PHP 확장 저장소·설치 도구 (pip·npm 과 유사)
  • pecl install redis : PHP Redis 확장(phpredis) 설치
  • docker-php-ext-enable redis : redis 확장 활성화 (공식 PHP 이미지 제공 스크립트)
    • conf.d 에 확장 로드용 설정파일 생성 : docker-php-ext-redis.ini
    • 내용 : extension=redis.so → PHP 시작 시 자동 로드

5. 네트워크 별칭 정보 확인
  • 별칭이 실제로 3개 컨테이너 IP로 풀리는지 확인

# 별칭이 어떤 IP로 풀리는지 (myproxy 안에서 조회)
docker exec myproxy getent hosts taskapi_pool

# 특정 컨테이너에 붙은 별칭 확인
docker inspect mytaskapi_1 --format '{{json (index .NetworkSettings.Networks "php-mysql").Aliases}}'

# php-mysql 네트워크의 컨테이너 - IP 매핑
docker network inspect php-mysql --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'

gclb_alias.png
  • getent 결과의 IP 3개 = 컨테이너 3개. nginx는 이 이름을 매 요청마다 다시 조회 → 다른 IP로 연결 = 분산

6. Nginx Proxy Manager Custom Locations 라우팅
  • 프록시 호스트 포워드 호스트 = 컨테이너 명(myfront) + 80포트 (IP 대신). 같은 php-mysql 네트워크라 컨테이너 이름으로 통신 가능
gclb_npm_list.png
gclb_npm_details.png
  • Custom Locations 탭에서 경로별 라우팅 추가
  • /task/ → Forward taskapi_pool/ : 80 (별칭 → 3개 컨테이너 라운드로빈)
  • /user/ → Forward myuserapi/ : 80 (FastAPI 회원 API)
  • Forward Hostname 끝 슬래시(/) = 접두어 제거용. /task/tasks-list.php → 컨테이너의 /tasks-list.php 로 매핑
gclb_customloc.png
  • 참고 : Nginx Proxy Manager에는 nginx의 upstream 블록을 만드는 UI가 없음. 그래서 같은 네트워크 별칭(taskapi_pool) + Custom Locations 조합으로, UI만으로 로드밸런싱 구현

7. 동작 확인
  • 윈도우 hosts 파일에 도메인 등록 (C:\Windows\System32\drivers\etc\hosts)

192.9.202.243 gctask.com
  • http://gctask.com/ 접속 → 로그인 → 할일 목록 정상 출력 (요청은 3개 컨테이너에 분산, redis 세션 공유로 동일 동작)
gclb_app_login.png
gclb_app_tasklist.png

8. 라운드로빈(round-robin) 검증 — ping_check.sh
  • 주의 : gctask.com은 윈도우 hosts에만 등록됨. 테스트는 도커가 도는 우분투에서 하므로, 우분투 /etc/hosts에도 등록 필요 (127.0.0.1 gctask.com)
  • 아래 ping_check.sh = hosts 자동 등록 + 프록시로 N번 요청 + 컨테이너별 처리 건수 집계 (요청 전/후 로그 차이로 정확 집계)

#!/bin/bash
DOMAIN=gctask.com
N=${1:-12}
CONTAINERS="mytaskapi_1 mytaskapi_2 mytaskapi_3"

# gctask.com 은 윈도우 hosts에만 등록됨 → 이 우분투에도 등록 (없을 때만)
if ! grep -q "$DOMAIN" /etc/hosts; then
  echo "127.0.0.1 $DOMAIN" | sudo tee -a /etc/hosts >/dev/null
fi
echo "[*] hosts : $(grep "$DOMAIN" /etc/hosts)"
echo "[*] $DOMAIN 응답코드 : $(curl -s -o /dev/null -w '%{http_code}' http://$DOMAIN/)"

# 요청 전 컨테이너별 누적 처리수 기록
declare -A before
for c in $CONTAINERS; do
  before[$c]=$(docker logs "$c" 2>&1 | grep -c "GET /tasks-list.php")
done

# 프록시로 N번 요청
echo "[*] http://$DOMAIN/task/ 로 $N 번 요청..."
for i in $(seq 1 "$N"); do curl -s "http://$DOMAIN/task/tasks-list.php" -o /dev/null; done

# 요청 후 - 전 차이 = 이번 분산 결과
echo "[*] 컨테이너별 처리 건수 (round-robin):"
for c in $CONTAINERS; do
  after=$(docker logs "$c" 2>&1 | grep -c "GET /tasks-list.php")
  printf "    %-12s : %s\n" "$c" "$((after - ${before[$c]}))"
done
  • 실행

chmod +x ping_check.sh
./ping_check.sh

gclb_roundrobin.png
  • 3개 컨테이너에 고르게 분산(4 / 4 / 4) → 로드밸런싱 성공

9. 장애 대응(failover) — 컨테이너가 죽으면?
  • 별칭/DNS 방식은 컨테이너가 내려가면 도커 DNS가 별칭에서 그 IP를 즉시 제거 → NPM이 살아있는 컨테이너로만 분산 (자동 failover)
  • 실습 : mytaskapi_2 중지 후 20회 요청 → 살아있는 2개(10·10)로만 분산, 실패 0건

docker stop mytaskapi_2   # 컨테이너 1개 강제 중지
docker exec myproxy getent hosts taskapi_pool   # .5 빠진 것 확인
./ping_check.sh 20   # 20회 요청

gclb_failover.png
  • 복구 : docker start mytaskapi_2 → 별칭에 자동 재합류(다시 3개 분산)
  • 한계(중요) : 이 자동 제외는 컨테이너가 완전히 내려갔을 때만 동작. 컨테이너는 떠 있는데 앱만 먹통(행·500)이면 IP가 DNS에 남아 그쪽 요청은 실패 → 별칭/DNS 방식은 능동 헬스체크가 없음
  • "살아는 있는데 고장난" 경우까지 자동 제외하려면 nginx upstream의 max_fails / fail_timeout (아래 10번 설정파일 방식) 필요

10. (참고) 가중치(%) · 헬스체크 — upstream 방식
  • 별칭/DNS 방식 = 균등(round-robin) 분산만 가능 + 능동 헬스체크 없음
  • 비율(가중치)이나 헬스체크가 필요하면 nginx upstream 사용 : weight=분배 비율, max_fails·fail_timeout=응답 실패 시 일시 제외(수동 헬스체크)

upstream taskapi_pool {
   server mytaskapi_1:80 weight=5 max_fails=2 fail_timeout=10s;
   server mytaskapi_2:80 weight=3 max_fails=2 fail_timeout=10s;
   server mytaskapi_3:80 weight=2 max_fails=2 fail_timeout=10s;
}
  • 단, 이 upstream은 NPM UI에 없음 → myproxy의 /data/nginx/custom/http_top.conf (Custom Nginx Configuration)로만 설정 가능. UI 우선·균등 분산이면 별칭 방식 사용

정리
  • 세션을 redis로 공유 → 컨테이너 무상태화 → 동일 컨테이너 복제로 수평 확장(로드밸런싱)
  • NPM은 upstream UI가 없는 대신, 도커 네트워크 별칭 + Custom Locations만으로 로드밸런서 역할
  • 백엔드 연결 = 호스트 IP가 아닌 컨테이너 명 + 80포트 권장 (간결·이식성)
  • 컨테이너가 죽으면 도커 DNS가 자동 제외 → 무중단(failover). 단 "앱만 먹통"은 못 거름 → 비율·헬스체크는 upstream(weight·max_fails)

댓글목록0

등록된 댓글이 없습니다.
게시판 전체검색