자동 배포를 하는 이유

개발 첫 주 차에는 마지막 금요일에 배포를 성공하고 말았었는데, 점점 갈수록 만들어야 하는 API도 많이 생기고 클라이언트에서 급하게 요구하는 수정 사항, 추가 사항들이 생긴다.

그 부분들을 모두 커버하고 배포하는 과정에서 반복적으로 배포를 해야 하는 불필요한 중복이 생기므로 굉장히 귀찮기도 하고 시간도 많이 들뿐더러 배포 과정에서 실수가 생긴다면 다시 인프라에 잡혀서 시간을 써야 한다.

그래서 실수도 줄여주고 반복적인 배포과정을 알아서 해주는 자동 배포를 비교적 프로젝트 초반인 지금 하기로 결정했다.

GitHub Actions 로 결정한 이유

자동 배포 도구에는 대표적으로 Jenkins, GitHub Actions 가 있다.

그 중 Jenkins 는 플러그인이 굉장히 많지만, 설정과 유지가 비교적 어려워서 자동 배포 구현에 큰 시간을 들일 수 없는 현재 상황에서 부담스러웠고 GitHub Wiki, Project, Issue 등 GitHub 를 적극적으로 사용하면서 많은 일들을 처리하고 있어서 GitHub Actions 를 사용하는 것이 장점이 많아보였다.

따라서 GitHub Actions 를 선택하게 되었다.

GitHub actions yml 파일 설정

name: Server CD

on:
  pull_request:
    branches: [ "develop", "main" ]
    paths:
      - "server/**"
    types:
      - closed
    
jobs:
  deploy:
    runs-on: ubuntu-20.04

    strategy:
      matrix:
        node-version: [20.x]

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3
      
      - name: Set up Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
          cache-dependency-path: '**/package-lock.json'
  
      - name: Install depenencies
        run: |
            cd server
            npm install

      - name: Create prod.env file
        env:
            DB_HOST_IP: ${{ secrets.SERVER_ENV_DB_HOST_IP }}
            DB_PORT: ${{ secrets.SERVER_ENV_DB_PORT }}
            DB_USER_NAME: ${{ secrets.SERVER_ENV_DB_USER_NAME }}
            DB_PASSWORD: ${{ secrets.SERVER_ENV_DB_PASSWORD }}
            DB_DATABASE_NAME: ${{ secrets.SERVER_ENV_DB_DATABASE_NAME }}
            ACCESS_ID: ${{ secrets.SERVER_ENV_ACCESS_ID }}
            SECRET_ACCESS_KEY: ${{ secrets.SERVER_ENV_SECRET_ACCESS_KEY }}
            JWT_SECRET_KEY: ${{ secrets.SERVER_ENV_JWT_SECRET_KEY }}
        run: |
            cd server
            touch prod.env
            echo "DB_HOST_IP=$DB_HOST_IP" >> prod.env
            echo "DB_PORT=$DB_PORT" >> prod.env
            echo "DB_USER_NAME=$DB_USER_NAME" >> prod.env
            echo "DB_PASSWORD=$DB_PASSWORD" >> prod.env
            echo "DB_DATABASE_NAME=$DB_DATABASE_NAME" >> prod.env
            echo "ACCESS_ID=$ACCESS_ID" >> prod.env
            echo "SECRET_ACCESS_KEY=$SECRET_ACCESS_KEY" >> prod.env
            echo "JWT_SECRET_KEY=$JWT_SECRET_KEY" >> prod.env

      - name: Build Docker image
        run: docker build --platform linux/amd64 ./server -t ${{ secrets.NCP_REGISTRY }}/catchy-tape:latest

      - name: Login NCP container registry
        run: docker login ${{ secrets.NCP_REGISTRY }} -u ${{ secrets.NCP_DOCKER_ACCESS_KEY_ID }} -p ${{ secrets.NCP_DOCKER_SECRET_KEY }}

      - name: Push Docker image to registry
        run: docker push ${{ secrets.NCP_REGISTRY }}/catchy-tape:latest

      - name: SSH into Server
        uses: appleboy/ssh-action@master
        with:
            host: ${{ secrets.SERVER_SSH_HOST }}
            username: ${{ secrets.SERVER_SSH_USER }}
            password: ${{ secrets.SERVER_SSH_PASSWORD }}
            port: ${{ secrets.SERVER_SSH_PORT }}
            script: |
                docker login ${{ secrets.NCP_REGISTRY }} -u ${{ secrets.NCP_DOCKER_ACCESS_KEY_ID }} -p ${{ secrets.NCP_DOCKER_SECRET_KEY }}
                docker pull ${{ secrets.NCP_REGISTRY }}/catchy-tape:latest
                docker stop catchy-tape-latest
                docker rm catchy-tape-latest
                docker run -d -p 3000:3000 --name catchy-tape-latest ${{ secrets.NCP_REGISTRY }}/catchy-tape:latest
								curl -X POST -H 'Content-type: application/json' --data '{"text":"서버 배포 성공!"}' ${{ secrets.SLACK_WEBHOOK_URL }}

위 yaml 파일을 보면 어떤 순서로 배포가 이루어지는지 대충 알 수 있다.

Checkout repository → Set up Node.js → Install depenencies → Create prod.env file → Build Docker image → Login NCP container registry → Push Docker image to registry → SSH into Server 의 단계로 이루어져 있다.

1. Checkout repository