Stockey 프로젝트를 진행하고 리팩토링을 진행했고, 변경사항을 정리해보려고 합니다.
기존 : 하나의 서버에서 Nginx(React)
, Express.js
, MySQL
를 전부 실행
변경 : 각각의 서버에서Nginx(React)
, Express.js 서버 2개
, MySQL
, Elasticsearch
를 실행
Keyword를 Elasticsearch에 인덱스하고 DB가 아닌 Elasticsearch에서 키워드를 가져오며 성능을 향상시킬 수 있었습니다.
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하게되면 두 개의 서버에 각각 배포되도록 수정하였습니다.
# 필수 이벤트 블록
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;
}
}
}
기존 리버스 프록시 서버를 구성하는 데 추가로 로드밸런싱을 통하여 트래픽을 여러 백엔드 서버로 분산시키는 코드를 추가하였습니다.
AWS EC2 프리티어에서는 Elasticsearch를 실행시킬 스펙이 되지 않아 비용 이슈로 Elasticsearch서버만 GCP를 통해서 실행시키도록 변경했습니다.
이에 따라 백엔드 서버에서만 접근할 수 있도록 방화벽 설정을 해주었습니다(인바운드 규칙)
리팩토링을 마치고 적용할 기술이 굉장히 많다는 것을 실감했습니다.
소켓 통신을 여러 백엔드 서버 간에 원활하게 처리하기 위한 RabbitMQ나 Kafka와 같은 Pub/Sub 구조 메시징 시스템
여러 서버를 효율적으로 관리하기 위한 Kubernetes
성능 향상을 위한 캐싱 기술 Redis
리팩토링을 통해 시스템을 확장 가능하고 안정적으로 만드는 방법에 대해 더 깊이 이해할 수 있었습니다.
각 기술을 잘 조합하여 성능을 최적화하고, 향후 시스템 유지보수와 확장이 용이하도록 설계하는 것이 중요하다는 점을 배웠습니다.
앞으로도 새로운 기술을 학습하고 적용하면서 지속적으로 개선하고 발전시킬 수 있는 능력을 키워나가보려 합니다.. 😬