[실습] 도커 실습 10 - Nginx Proxy Manager 로드밸런싱
필기자
2025-11-04 18:40
196
0
본문
목차
1. 로드밸런싱(Load Balancing)이란?
2. 네트워크 별칭(network-alias) 개념
3. mytaskapi 컨테이너 3개로 확장 (별칭 부여)
4. Redis 확장 설치 (각 컨테이너에 세션 공유용)
5. 네트워크 별칭 정보 확인
6. Nginx Proxy Manager Custom Locations 라우팅
7. 동작 확인
8. 라운드로빈(round-robin) 검증 — ping_check.sh
9. 장애 대응(failover) — 컨테이너가 죽으면?
10. (참고) 가중치(%) · 헬스체크 — upstream 방식
정리
- 로드밸런싱(Load Balancing)이란?
- 네트워크 별칭(network-alias) 개념
- mytaskapi 컨테이너 3개로 확장 (별칭 부여)
- Redis 확장 설치 (세션 공유용)
- 네트워크 별칭 정보 확인
- Nginx Proxy Manager Custom Locations 라우팅
- 동작 확인
- 라운드로빈(round-robin) 검증
- 장애 대응(failover) — 컨테이너가 죽으면?
- (참고) 가중치(%) · 헬스체크 (upstream 방식)
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번)
- 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
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}}'
- getent 결과의 IP 3개 = 컨테이너 3개. nginx는 이 이름을 매 요청마다 다시 조회 → 다른 IP로 연결 = 분산
6. Nginx Proxy Manager Custom Locations 라우팅
- 프록시 호스트 포워드 호스트 = 컨테이너 명(myfront) + 80포트 (IP 대신). 같은 php-mysql 네트워크라 컨테이너 이름으로 통신 가능

- Custom Locations 탭에서 경로별 라우팅 추가
- /task/ → Forward taskapi_pool/ : 80 (별칭 → 3개 컨테이너 라운드로빈)
- /user/ → Forward myuserapi/ : 80 (FastAPI 회원 API)
- Forward Hostname 끝 슬래시(/) = 접두어 제거용. /task/tasks-list.php → 컨테이너의 /tasks-list.php 로 매핑
- 참고 : 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 세션 공유로 동일 동작)

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
- 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회 요청
- 복구 : 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