Stockey 리팩토링 [PDA 5h]

chaean·2025년 1월 16일
0

프로젝트 - Stockey

목록 보기
3/4

Stockey 프로젝트를 진행하고 리팩토링을 진행했고, 변경사항을 정리해보려고 합니다.

변경사항

기존 : 하나의 서버에서 Nginx(React), Express.js, MySQL를 전부 실행
변경 : 각각의 서버에서Nginx(React), Express.js 서버 2개, MySQL, Elasticsearch를 실행

0. 아키텍처

1. Elasticsearch 적용

Keyword를 Elasticsearch에 인덱스하고 DB가 아닌 Elasticsearch에서 키워드를 가져오며 성능을 향상시킬 수 있었습니다.

2. 부하 분산을 위한 서버 아키텍처 변경

2-1. GitHub Actions 배포 설정 파일

name: CI/CD Pipeline for Backend

on:
  push:
    branches:
      - main # 배포할 브랜치

jobs:
  build-and-push-iamge:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Code
        uses: actions/checkout@v3

      # Docker Hub 로그인
      - name: Log in to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      # Docker 이미지를 빌드하고 푸시
      - name: Build and Push Docker Image
        run: |
          # Docker 이미지를 빌드
          docker build -t ${{ secrets.DOCKER_USERNAME }}/stockey-express:latest .
          # Docker 이미지를 Docker Hub에 푸시
          docker push ${{ secrets.DOCKER_USERNAME }}/stockey-express:latest


  deploy-server1:
    name: Delpoy Server 1
    runs-on: ubuntu-latest
    needs: build-and-push-iamge

    steps:
      - name: Delpoy Server 1
        uses: appleboy/ssh-action@v0.1.7
        with:
          host: ${{ secrets.BACK_SERVER_HOST_01 }}
          username: ${{ secrets.BACK_SERVER_USER_01 }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /home/ubuntu

            # .env 파일 생성
            echo "SLACK_API_TOKEN=${{ secrets.SLACK_API_TOKEN }}" >> .env
            echo "DB_HOST=${{ secrets.DB_HOST }}" >> .env
            echo "DB_USER=${{ secrets.DB_USER }}" >> .env
            echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> .env
            echo "DB_DATABASE=${{ secrets.DB_DATABASE }}" >> .env
            echo "APP_KEY=${{ secrets.APP_KEY }}" >> .env
            echo "APP_SECRET=${{ secrets.APP_SECRET }}" >> .env
            echo "TZ=Asia/Seoul" >> .env
            echo "ES_SERVER=${{ secrets.ES_SERVER }}" >> .env

            # 도커 이미지 pull
            docker pull ${{ secrets.DOCKER_USERNAME }}/stockey-express:latest

            # 기존에 실행 중인 컨테이너가 있으면 중지하고 제거
            docker stop stockey-express || true
            docker rm stockey-express || true

            # 컨테이너 실행
            docker run -d --name stockey-express --env-file .env -p 3000:3000 ${{ secrets.DOCKER_USERNAME }}/stockey-express:latest

  deploy-server2:
    name: Delpoy Server 2
    runs-on: ubuntu-latest
    needs: build-and-push-iamge

    steps:
      - name: Delpoy Server 2
        uses: appleboy/ssh-action@v0.1.7
        with:
          host: ${{ secrets.BACK_SERVER_HOST_02 }}
          username: ${{ secrets.BACK_SERVER_USER_02 }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /home/ubuntu

            # .env 파일 생성
            echo "SLACK_API_TOKEN=${{ secrets.SLACK_API_TOKEN }}" >> .env
            echo "DB_HOST=${{ secrets.DB_HOST }}" >> .env
            echo "DB_USER=${{ secrets.DB_USER }}" >> .env
            echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> .env
            echo "DB_DATABASE=${{ secrets.DB_DATABASE }}" >> .env
            echo "APP_KEY=${{ secrets.APP_KEY }}" >> .env
            echo "APP_SECRET=${{ secrets.APP_SECRET }}" >> .env
            echo "TZ=Asia/Seoul" >> .env
            echo "ES_SERVER=${{ secrets.ES_SERVER }}" >> .env

            # 도커 이미지 pull
            docker pull ${{ secrets.DOCKER_USERNAME }}/stockey-express:latest

            # 기존에 실행 중인 컨테이너가 있으면 중지하고 제거
            docker stop stockey-express || true
            docker rm stockey-express || true

            # 컨테이너 실행
            docker run -d --name stockey-express --env-file .env -p 3000:3000 ${{ secrets.DOCKER_USERNAME }}/stockey-express:latest

배포 파이프라인 설정 파일을 수정하여 main branch에 push하게되면 두 개의 서버에 각각 배포되도록 수정하였습니다.

2-2. Nginx 로드 밸런싱 적용

# 필수 이벤트 블록
events {}

http {
    # MIME 타입 설정 파일 포함
    include /etc/nginx/mime.types; 
    # MIME 타입을 알 수 없는 파일에 대해 기본적으로 사용될 MIME 타입을 지정
    # 이진 데이터의 기본 MIME 타입
    default_type application/octet-stream;
    
    upstream stockey-backends {
        server 서버1IP:3000;  # 첫 번째 백엔드 서버
        server 서버2IP:3000;  # 두 번째 백엔드 서버
    }

    server {
        listen 80;

        # 서버 이름 설정
        server_name localhost;

        # 정적 파일의 기본 경로 설정
        root /usr/share/nginx/html;

        # SPA를 위한 기본 라우팅 설정
        location / {
            try_files $uri /index.html;
        }

        # 백엔드 (Express) API 요청 처리
        location /api/ {
            proxy_pass http://stockey-backends;  # Express 백엔드 컨테이너로 요청 전달
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        # WebSocket 요청 프록시
        location /socket.io/ {
            proxy_pass http://IP:3000; # Express 서버로 전달
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

기존 리버스 프록시 서버를 구성하는 데 추가로 로드밸런싱을 통하여 트래픽을 여러 백엔드 서버로 분산시키는 코드를 추가하였습니다.

3. 인바운드 규칙 변경

AWS EC2 프리티어에서는 Elasticsearch를 실행시킬 스펙이 되지 않아 비용 이슈로 Elasticsearch서버만 GCP를 통해서 실행시키도록 변경했습니다.
이에 따라 백엔드 서버에서만 접근할 수 있도록 방화벽 설정을 해주었습니다(인바운드 규칙)

4. 후기

리팩토링을 마치고 적용할 기술이 굉장히 많다는 것을 실감했습니다.

  1. 소켓 통신을 여러 백엔드 서버 간에 원활하게 처리하기 위한 RabbitMQKafka와 같은 Pub/Sub 구조 메시징 시스템

  2. 여러 서버를 효율적으로 관리하기 위한 Kubernetes

  3. 성능 향상을 위한 캐싱 기술 Redis

리팩토링을 통해 시스템을 확장 가능하고 안정적으로 만드는 방법에 대해 더 깊이 이해할 수 있었습니다.
각 기술을 잘 조합하여 성능을 최적화하고, 향후 시스템 유지보수와 확장이 용이하도록 설계하는 것이 중요하다는 점을 배웠습니다.
앞으로도 새로운 기술을 학습하고 적용하면서 지속적으로 개선하고 발전시킬 수 있는 능력을 키워나가보려 합니다.. 😬

profile
백엔드 개발자

0개의 댓글