IT 끄적이기

nginx loadbalance 설정하기

미르아 2024. 12. 27. 18:01
728x90

환경

linux - ubuntu 20.04 LTS

react, node, docker, docker-compose, nginx, npm 설치 상태

방식

react 프론트 서버는 도커 이미지로 빌드하여 개별 실행

nodejs 백 서버는 도커컴포즈를 이용하여 3개 실행

8081,8082,8083 포트를 8002번과 매핑

컨테이너로 실행 된 nginx가 react의 요청을 받아 node 서버로 분산

FRONT 서버 설정 방법

git clone을 이용해 깃허브에 저장 된 소스를 복제

lapoem_front 폴더에서 도커 빌드 실행

$ docker build -t lapoem_front . 명령어로 리눅스 해당 프로젝트 폴더에서 실행

Dockerfile 코드

# 빌드 스테이지
FROM node:18 as build

# 작업 디렉토리 설정
WORKDIR /app

# package.json 파일과 의존성 설치
COPY package.json package-lock.json ./
RUN npm install && npm cache clean --force
RUN npm install --save-dev @babel/plugin-proposal-private-property-in-object && npm install

# 나머지 소스코드 복사 및 빌드
COPY . .
RUN npm run build

# 최종 실행 이미지 (Nginx)
FROM nginx:1.23-alpine

# Nginx 설정 디렉토리 설정
WORKDIR /usr/share/nginx/html

# 기존 Nginx 기본 파일 삭제
RUN rm -rf ./*

# 빌드된 정적 파일만 복사
COPY --from=build /app/build .

# Nginx 설정 파일 복사
COPY nginx.conf /etc/nginx/conf.d/default.conf

# 포트 설정
EXPOSE 80

# Nginx 데몬 실행
ENTRYPOINT ["nginx", "-g", "daemon off;"]

docker run -d -p 3002:80 lapoem_front 명령어로 실행

9seebird.site:3002로 접속하면 프론트 페이지 접속 완료

BACK 서버 설정 방법

nodejs로 작성

react와 마찬가지로 git clone을 통해 깃허브 레포지토리에 있는 소스를 복제

Dockerfile 작성

Dockerfile 내용

# 베이스 이미지 설정
FROM node:alpine3.18 as build

# 작업 디렉토리 설정
WORKDIR /app

# package.json 및 package-lock.json 복사 및 의존성 설치
COPY package.json package-lock.json ./  
RUN npm install --only=production && npm cache clean --force

# .env 파일을 컨테이너로 복사 (필요한 경우)
COPY .env ./

# 애플리케이션 소스 코드 복사
COPY . .

# 애플리케이션에서 사용하는 포트를 노출
EXPOSE 8002

# 애플리케이션 시작 명령어
CMD ["npm", "start"]

docker-compose.yml 파일 작성

docker-compose.yml 코드

version: '3.3'  # Docker Compose 파일 버전 정의

services:
  # Node.js 애플리케이션 컨테이너 정의
  node-app1:
    build:
      context: .  # Dockerfile이 있는 현재 디렉터리를 컨텍스트로 설정
      dockerfile: Dockerfile  # 빌드에 사용할 Dockerfile 지정
    ports:
      - "8081:8002"  # 호스트 포트 8081 -> 컨테이너 내부의 8002 포트로 매핑
    networks:
      - app-network  # 컨테이너가 연결될 네트워크 지정

  node-app2:
    build:
      context: .  # Dockerfile이 있는 현재 디렉터리를 컨텍스트로 설정
      dockerfile: Dockerfile  # 빌드에 사용할 Dockerfile 지정
    ports:
      - "8082:8002"  # 호스트 포트 8082 -> 컨테이너 내부의 8002 포트로 매핑
    networks:
      - app-network  # 컨테이너가 연결될 네트워크 지정

  node-app3:
    build:
      context: .  # Dockerfile이 있는 현재 디렉터리를 컨텍스트로 설정
      dockerfile: Dockerfile  # 빌드에 사용할 Dockerfile 지정
    ports:
      - "8083:8002"  # 호스트 포트 8083 -> 컨테이너 내부의 8002 포트로 매핑
    networks:
      - app-network  # 컨테이너가 연결될 네트워크 지정

  # Nginx 로드밸런서 컨테이너 정의
  nginx:
    image: nginx:latest  # 사용할 Nginx 이미지를 Docker Hub에서 가져옴
    ports:
      - "8002:80"  # 호스트 포트 8002 -> 컨테이너 내부의 80 포트로 매핑
    volumes:
      - ./test-nginx.conf:/etc/nginx/nginx.conf  # Nginx 설정 파일을 컨테이너 내부로 마운트
    networks:
      - app-network  # 컨테이너가 연결될 네트워크 지정

# 네트워크 정의
networks:
  app-network:
    driver: bridge  # 네트워크 드라이버를 bridge로 설정 (컨테이너 간 통신 가능)

설명 GPT 참고

Node.js 애플리케이션 컨테이너 (node-app1, node-app2, node-app3):

각 컨테이너는 동일한 Node.js 애플리케이션을 실행
호스트 포트(8081, 8082, 8083)를 통해 각각의 컨테이너 내부 포트(8002)와 통신 가능
app-network라는 네트워크를 사용해 Nginx와 통신 가능

Nginx 로드밸런서 컨테이너 (nginx):

Nginx가 로드밸런서를 수행하며, Node.js 애플리케이션에 요청을 분산합니다
호스트 포트 8002를 통해 외부에서 접속 가능
Nginx 설정 파일(test-nginx.conf)을 컨테이너 내부의 /etc/nginx/nginx.conf에 연결하여 Nginx 설정을 정의

네트워크 (app-network):

모든 컨테이너가 동일한 네트워크(app-network)에 연결되어 서로 통신 가능
bridge 드라이버는 기본적으로 로컬 호스트에서 컨테이너 간 통신을 허용

docker-compose에서 같은 위치에 있는 nginx.conf 파일을 컨테이너 내부로 복사하기 때문에

nginx.conf 파일도 작성해주어야 함

nginx.conf 파일 코드

# events 블록: 네트워크 연결 설정 (필수지만 현재는 비어 있음)
events {}

# http 블록: HTTP 요청 처리 관련 설정
http {
  # upstream 블록: Node.js 애플리케이션 서버들을 그룹화하여 로드밸런싱
  upstream node_servers {
    server node-app1:8002;  # Docker Compose 내부 네트워크에서 Node.js 컨테이너 1 접근
    server node-app2:8002;  # Docker Compose 내부 네트워크에서 Node.js 컨테이너 2 접근
    server node-app3:8002;  # Docker Compose 내부 네트워크에서 Node.js 컨테이너 3 접근
  }

  # log_format: 요청 로그의 형식 정의
  log_format upstreamlog '$remote_addr - $remote_user [$time_local]'  # 클라이언트 IP, 사용자, 시간 정보
                        '"$request" $status $body_bytes_sent'         # 요청 URI, HTTP 상태 코드, 전송된 바이트
                        '"$http_referer" "$http_user_agent"'          # 참조 URL, 사용자 에이전트
                        'to: $upstream_addr';                         # 요청이 전달된 upstream 서버 주소
  # access_log: 로그 파일 경로와 사용할 로그 형식 설정
  access_log /var/log/nginx/upstream-access.log upstreamlog;

  # server 블록: 서버의 기본 설정
  server {
    listen 80;  # Nginx가 80 포트에서 HTTP 요청을 수신
    server_name 9seebird.site;  # 요청의 호스트명이 9seebird.site인 경우 처리

    # location 블록: 경로별 요청 처리 방식 정의
    location / {
      proxy_pass http://node_servers;  # 요청을 upstream 블록에 정의된 Node.js 서버로 전달
      proxy_http_version 1.1;          # HTTP 버전 설정
      proxy_set_header Upgrade $http_upgrade;  # WebSocket 업그레이드 요청 처리
      proxy_set_header Connection 'upgrade';  # 연결 업그레이드 처리
      proxy_set_header Host $host;  # 원래 요청의 호스트 헤더 전달
      proxy_cache_bypass $http_upgrade;  # 캐시를 우회하도록 설정
    }

    # /stub_status 경로에 대한 처리: Nginx 상태 정보를 제공
    location /stub_status {
        stub_status;  # Nginx의 간단한 상태 정보를 반환 (활성 연결, 요청 등)
        allow all;    # 모든 요청을 허용 (보안을 위해 IP 제한 설정 가능)
    }
  }
}

코드 설명 GPT 참고

upstream 블록:

Node.js 서버를 묶어 로드밸런싱을 수행
요청을 Node.js 컨테이너 중 하나로 분배

log_format 및 access_log:

요청 정보를 포맷팅하여 /var/log/nginx/upstream-access.log에 기록
요청 로그에 어떤 Node.js 컨테이너로 전달되었는지 기록($upstream_addr 포함

server 블록:

Nginx가 80번 포트에서 들어오는 HTTP 요청을 처리
server_name으로 지정된 호스트명을 기준으로 요청을 처리

location /:

기본 경로(/)로 들어오는 요청을 Node.js 서버로 전달
WebSocket 업그레이드 헤더 처리와 캐시 우회 설정도 포함

location /stub_status:

Nginx의 상태 정보를 제공하는 경로
보안을 위해 나중에 허용 IP를 제한하는 것 추천

도커 컴포즈 실행 명령어

docker-compose up -d : docker-compose.yml 파일이 있는 곳에서 실행해야 함

docker-compose.yml 파일 내용이 변경되었을 경우 docker-compose down 명령어 입력 후 다시 실행

k6로 트래픽 분산 테스트

k6를 직접 설치해서 사용해도 되고 도커 컨테이너로 사용해도 된다

Grapane, Prometheus 적용 docker-compose.yml 코드

version: '3.3'  # Docker Compose 파일의 버전 정의

services:
  # Node.js 애플리케이션 1번 컨테이너 설정
  node-app1:
    build:
      context: .  # 현재 디렉토리를 컨텍스트로 사용
      dockerfile: Dockerfile  # Dockerfile을 사용해 이미지 빌드
    ports:
      - "8081:8002"  # 호스트의 8081 포트를 컨테이너의 8002 포트에 연결
    networks:
      - app-network  # app-network 네트워크에 연결

  # Node.js 애플리케이션 2번 컨테이너 설정
  node-app2:
    build:
      context: .  # 현재 디렉토리를 컨텍스트로 사용
      dockerfile: Dockerfile  # Dockerfile을 사용해 이미지 빌드
    ports:
      - "8082:8002"  # 호스트의 8082 포트를 컨테이너의 8002 포트에 연결
    networks:
      - app-network  # app-network 네트워크에 연결

  # Node.js 애플리케이션 3번 컨테이너 설정
  node-app3:
    build:
      context: .  # 현재 디렉토리를 컨텍스트로 사용
      dockerfile: Dockerfile  # Dockerfile을 사용해 이미지 빌드
    ports:
      - "8083:8002"  # 호스트의 8083 포트를 컨테이너의 8002 포트에 연결
    networks:
      - app-network  # app-network 네트워크에 연결

  # Nginx 로드밸런서 설정
  nginx:
    image: nginx:latest  # 최신 Nginx 이미지를 사용
    ports:
      - "8002:80"  # 호스트의 8002 포트를 컨테이너의 80 포트에 연결
    volumes:
      - ./test-nginx.conf:/etc/nginx/nginx.conf  # 로컬 test-nginx.conf를 컨테이너의 Nginx 설정 파일로 매핑
    networks:
      - app-network  # app-network 네트워크에 연결

  # Prometheus 설정 (모니터링 도구)
  prometheus:
    image: prom/prometheus:latest  # 최신 Prometheus 이미지를 사용
    ports:
      - "9090:9090"  # 호스트의 9090 포트를 컨테이너의 9090 포트에 연결
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml  # 로컬 prometheus.yml 파일을 컨테이너 설정 파일로 매핑
    networks:
      - app-network  # app-network 네트워크에 연결

  # Grafana 설정 (데이터 시각화 도구)
  grafana:
    image: grafana/grafana:latest  # 최신 Grafana 이미지를 사용
    ports:
      - "3000:3000"  # 호스트의 3000 포트를 컨테이너의 3000 포트에 연결
    networks:
      - app-network  # app-network 네트워크에 연결

# 네트워크 설정
networks:
  app-network:
    driver: bridge  # 브리지 네트워크를 사용하여 컨테이너 간 통신을 가능하게 설정

설명 GPT 참고

Node.js 애플리케이션:

각각의 컨테이너가 8081, 8082, 8083 포트를 통해 로드밸런싱 대상 서버로 동작
app-network 네트워크를 통해 Nginx와 통신

Nginx:

Nginx가 8002 포트에서 요청을 받고, test-nginx.conf 설정을 사용해 로드밸런싱을 수행
Node.js 컨테이너로 요청을 분배

Prometheus:

9090 포트에서 실행되며, Prometheus 구성 파일(prometheus.yml)을 통해 메트릭을 수집
Nginx Exporter와 연동하여 Nginx 메트릭을 수집

Grafana:

3000 포트에서 실행되며, Prometheus에서 수집된 메트릭 데이터를 시각화
웹 인터페이스를 통해 데이터 대시보드를 구성

네트워크:

app-network는 모든 컨테이너가 연결되어 서로 통신할 수 있도록 설정된 브리지 네트워크

728x90