본문 바로가기
개발

[배포] github actions, EC2, nginx를 통한 무중단 배포

by 주주병 2024. 10. 9.
728x90
반응형

github actions, EC2, nginx를 통한 무중단 배포

지금은 익숙한 프로세스이지만 누구나 처음 배포하는 과정은 매우 어려울 것 같습니다. 

그렇지만 미래를 상상하면서 지금의 과정을 즐기는 것도 나쁘지 않지 않을까요?

 

IAM 설정

 

본격적으로 sh파일과 yml 파일을 작성 전에 해야할 단계가 있습니다.
바로 aws의 IAM 설정과 보안그룹 설정입니다.

 

위처럼 사용자를 만들 때 생성되는 secret key access key를 잘 보관해놔야합니다.
이후에 아래의 권한정책에서 4개를 추가해주면 됩니다.

 

각각 배포, ec2접근, S3 사용에 관한 권한입니다.

사실 제 코드에서는 S3를 사용하진 않는데 지금 보니 GPT가 참 멍청하네요.

 

github secret repository 설정

setting에 들어간 뒤

secrets 탭에서 actions에 들어간 뒤에

위처럼 키를 만들어주면 됩니다.

access키 secret키에는 아까 iam에서 생성할 때 저장해 둔 키를 사용하면 되고

 

보통 ec2를 사용하면 인스턴스 서버에 접속할 때,

ssh -i ???.pem USERNAME@HOST

 

이러한 양식으로 접속하게 되는데 이 양식에서


EC2_USERNAME = USERNAME
EC2_HOST = HOST
SSH_PRIVATE_KEY = pem키를 cat ???.pem 했을 경우 pem키의 내용을 복붙

 

nginx 설정

맨 처음에 nginx를 왜 이용하지? 라는 생각이 들었습니다.

 

지피티와 사투끝에 내제가 깨닳은 바는 우리가 배포할 때 기존의 포트를 kill하고 새로 업데이트한 jar파일을 배포하게 되는데

 

이 경우, 기존의 서비스를 이용하던 사용자들은 갑자기 서버가 끊기는 동안 서비스를 못 이용하는 불편함을 겪게 됩니다.

 

그렇기에 그렇다면 사용자가 서버가 끊기는 것을 모르도록 배포하면 되지 않을까?의 해결책으로 나온게 nginx입니다.

 

nginx 다운로드

sudo apt update
sudo apt install nginx

 

nginx 시작

sudo systemctl start nginx
sudo systemctl enable nginx

 

방화벽 설정

sudo ufw allow 22
sudo ufw allow 22/tcp
sudo ufw allow 8080/tcp
sudo ufw allow 80/tcp
sudo ufw allow 'Nginx Full'
sudo ufw enable

22 포트를 방화벽을 활성화 전에 열어주는 이유는 ssh -i 이렇게 접속할 때 22포트로 요청을 하기에 22포트를 허가 안 하고 enable을 하면 인스턴스 서버를 원격에서 접속 못 하는 대참사가 일어납니다.

 

필자는 이거를 한번 겪고 터미널에서 접속을 못하게 돼서 aws 사이트에서 따로 들어가서 22포트를 다시 열어주는 수고를 겪었습니다.

 

nginx 기본 설정 변경

sudo nano /etc/nginx/sites-available/default

파일을 열어서 안의 내용을 다 지운 뒤에

 

upstream backend {
    server 127.0.0.1:8081;
    server 127.0.0.1:8082;
}

server {
    listen 80;
    server_name your_domain_or_ip;

    location / {
        proxy_pass http://backend;
        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;
    }
}

이렇게 설정해 주면 됩니다.
여기서는 로드밸런싱이라는 기법을 사용하는데 이게 무엇이냐? 

 

코드를 수정하고 다시 배포를 할 때 그냥 kill PID 하고 다시 배포를 하면 기존의 유저가 잘 사용하던 서비스가 갑자기 끊기면 문제가 생길 거에요.

 

이거를 방지하기위해 기존에 8081을 사용하고 있는 경우에는 8082포트에 새로운 배포버전을 올려서 기존의 8081 이용자들을 스무스하게 옮기는 방법을 로드 밸런싱이라고 합니다.

 

728x90

nginx 업데이트 및 재시작

sudo nginx -t
sudo systemctl restart nginx

 

deploy_nginx.sh 설정

#!/bin/bash

APP_NAME=""
JAR_NAME="$APP_NAME.jar"
DEPLOY_DIR="/home/ubuntu"
CURRENT_PORT=$(sudo lsof -i -P -n | grep LISTEN | grep 808 | awk '{print $9}' | sed 's/[^0-9]*//g')
TARGET_PORT=0

echo "> 현재 구동중인 애플리케이션 포트: $CURRENT_PORT"

if [ "$CURRENT_PORT" == "8081" ]; then
  TARGET_PORT=8082
elif [ "$CURRENT_PORT" == "8082" ]; then
  TARGET_PORT=8081
else
  TARGET_PORT=8081
fi

echo "> 배포할 포트: $TARGET_PORT"

# 새로운 애플리케이션 배포
nohup java -jar $DEPLOY_DIR/$JAR_NAME --server.port=$TARGET_PORT > $DEPLOY_DIR/nohup_$TARGET_PORT.out 2>&1 &

# 새로운 애플리케이션이 구동될 때까지 대기
sleep 10

# 새로운 애플리케이션이 정상적으로 구동되었는지 확인
echo "> 새로운 애플리케이션 구동 확인"
NEW_PID=$(lsof -t -i:$TARGET_PORT)

echo "> Nginx 설정 변경"
sudo sed -i "s/proxy_pass http:\/\/localhost:808[0-9];/proxy_pass http:\/\/localhost:$TARGET_PORT;/" /etc/nginx/sites-available/default

echo "> Nginx 리로드"
sudo systemctl reload nginx

echo "> 기존 애플리케이션 종료"
if [ "$CURRENT_PORT" != "0" ]; then
  sudo fuser -k -n tcp $CURRENT_PORT
fi

echo "> 배포 완료"

 

github actions 작성

여기서 새로운 workflows를 만들고 이름은 임시로 ci-cd.yml으로 만든 다음에

 

name: CI/CD Pipeline

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          distribution: 'adopt'
          java-version: '17'

      - name: Grant execute permission for gradlew
        run: cd runtale && chmod +x gradlew

      - name: Build with Gradle
        run: cd runtale && ./gradlew clean -x test build

      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: build-artifacts
          path: runtale/build/libs/runtale-0.0.1-SNAPSHOT.jar

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Download build artifacts
        uses: actions/download-artifact@v3
        with:
          name: build-artifacts
          path: runtale/build/libs

      - name: Deploy to AWS EC2
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: 'ap-northeast-2'
          SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
        run: |
          echo "${SSH_PRIVATE_KEY}" > private_key.pem
          chmod 600 private_key.pem
          scp -i private_key.pem -o StrictHostKeyChecking=no runtale/build/libs/runtale-0.0.1-SNAPSHOT.jar ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }}:/home/${{ secrets.EC2_USERNAME }}/api/runtale.jar
          ssh -i private_key.pem -o StrictHostKeyChecking=no ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }} "chmod +x /home/${{ secrets.EC2_USERNAME }}/api/deploy_nginx.sh && /home/${{ secrets.EC2_USERNAME }}/api/deploy_nginx.sh"
          rm -f private_key.pem

위처럼 복붙을 해주면 됩니다.

 

필자의 코드에서 주의할 점은 루트 디렉토리 위에 api라는 폴더가 하나 더 있어서 디렉토리 경로를 독자의 기준으로 수정해주면 좋을 것 같습니다.

간략히 코드 설명을 하자면 git의 가상서버나의 최신의 프로젝트 코드를 bulid하여서 임시로 저장한 후 깃의 가상서버에서 

scp 명령어를 이용해서 빌드한 jar파일을 가져온 뒤, ssh로 인스턴스 서버를 접속한 뒤에 가져온 jar파일을 통해서 내가 미리 서버에 작성해둔 sh파일과 함께 배포하는 과정이라고 생각하면 됩니다.

파일 내용

ec2 보안 그룹 설정

모든 설정을 거의 다 마쳤습니다.

 

우리는 사용자가 들어올 경우 제가 열어둔 포트로 리버스 프록싱이 될 것입니다.

우리는 8081과 8082 포트를 사용하는데 그러기 위해서는 ec2에서 80포트를 열어둬야 합니다.

 

728x90
반응형