본문 바로가기

🧑🏻‍💻 Dev/Infra

[Github Actions x Docker] 자동화 배포 환경 도커로 변경하기

1. 기존 아키텍처와 배포 스크립트 분석


 

프로젝트 진행 과정에서 초반에 간단한 인프라 설계를 위처럼 진행했습니다.

먼저 EC2(Ubuntu) 내부에 Node 16 버전을 포함해서 필요한 것들을 모두 수동으로 설치했습니다.

그 후 deploy.sh라는 스크립트 파일을 만들어 배포 스크립트를 작성했습니다.

이제 Github Actions에서 SSH 연결을 통해 EC2에 접근하여 deploy.sh를 실행하여 자동화 배포가 되도록 설정하여 완료했습니다.

 

기존의 스크립트는 아래와 같이 git 저장소에서 프로젝트 최근 파일을 가져온 후 추가된 npm 라이브러리를 확인하고 설치합니다.

프로젝트를 build 하고, 빌드가 성공하면 현재 실행 중인 프로세스를 확인하고 중지시켜 줍니다.

그 후 백그라운드 실행을 위해서 nohup을 통해서 application을 실행시켜 줬습니다.

# deploy.sh

echo "1. project root로 이동"
cd ~/server/ginger-hotel-server

echo "2. git pull"
git pull origin develop

echo "3. 추가된 라이브러리 설치"
npm install

echo "4. project build"
npm run build

if [ $? -eq 0 ]; then
  # 빌드 성공 시
  echo "5. 실행중인 프로세스 확인"
  CURRENT_PID=$(pgrep -f node)

  echo "6. 실행중인 프로세스 중지"
  sudo kill -15 $CURRENT_PID

  echo "7. 홈 경로 이동 후 프로젝트 재실행"
  nohup npm run start:prod > ~/nohup.out 2> ~/nohup.err < /dev/null &
else
  echo "프로젝트 Build 실패"
  exit 1
fi

 

이 상태로도 자동화 배포환경에는 문제가 없었고, 개발 단계에서 크게 불편한 점은 못 느꼈었습니다.

개발이 거의 마무리될 때쯤 두 가지 의문점이 들었습니다.

서버(EC2)가 증설되었을 때, EC2에 필요한 환경(특정 버전 Node)을 모두 수동으로 설치해줘야 하는 건가? Node 말고, 환경이 추가된다면?

실제 서비스를 운영중일 때, Application이 실행되고 있는 서버(EC2)에서 Git Pull을 하고, Build를 하는 과정이 부하를 주어 서버 성능에 영향을 주지는 않을까?

 

이러한 불편했던 점을 해결하기 위해서 "도커"를 활용하여 필요한 환경까지 함께 이미지로 만들고, Build 환경을 운영 서버와 분리해 보자는 생각을 하게 되었습니다.

 

 

 

2. Ubuntu에 도커 설치하기


1. ubuntu 패키지를 먼저 업데이트합니다.

sudo apt-get update

 

2. Docker 설치에 필요한 패키지를 설치합니다.

sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common -y

 

3. Docker 공식 GPG 키를 추가합니다.

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

 

4. Docker 공식 apt 저장소를 추가합니다.

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

 

5. 시스템 패키지를 다시 업데이트합니다.

sudo apt-get update

 

6. Docker를 설치합니다.

sudo apt-get install docker-ce docker-ce-cli containerd.io -y

 

7. Docker의 실행 상태를 확인합니다.

sudo systemctl status docker

 

8. 도커 허브 로그인을 진행합니다.

sudo docker login
  • Docker Hub에서 가입한 이메일과 비밀번호로 로그인을 진행할 수 있습니다.
  • Docker Hub는 무료로 계정 1개당 1개의 Private 저장소를 사용할 수 있도록 제공해 줍니다.
  • 저는 이전에 Private 저장소를 쓴 적이 없기 때문에 이번 프로젝트에서 Private 저장소를 만들어 사용했습니다.

 

 

 

3. 프로젝트의 Root 경로에 DockerFile 생성


여기서 DockerFile은 도커에 존재하는 이미지를 기반으로 하여 내가 작성한 스크립트 파일을 통해 나만의 이미지를 생성할 수 있는 파일입니다. 즉, 저는 여기에 필요한 Docker 이미지를 골라 담아서 제가 만들고 있는 서버에 필요한 이미지를 생성하는 DockerFile을 생성했습니다.

# Node Base Image
FROM node:16-alpine

# RUN mkdir -p /app
WORKDIR /app

# Current Local . to /app/
ADD . /app/

# install Library
RUN npm install

# Build
RUN npm run build

# PORT
EXPOSE 8080

# Start
ENTRYPOINT npm run start:prod
  • 먼저 저는 Node 16 버전을 Base로 해서 Application을 실행시키고 있기 때문에 Base 이미지를 Node의 16 버전을 사용합니다.
  • RUN, CMD, COPY 등이 실행되는 기본 경로를 WORKDIR로 "/app"으로 설정합니다.
  • ADD 명령어를 통해 현재 프로젝트 경로(.)에 있는 소스파일을 WORKDIR로 설정한 도커의 경로(/app/)로 복사합니다.
  • 이제 순서대로 npm 라이브러리를 설치하고, build를 진행합니다.
  • EXPOSE를 통해서 도커 컨테이너가 실행되었을 때, 요청을 기다리고 있을 Listen 포트를 8080으로 지정합니다.
  • 마지막으로 ENTRYPOINT 명령어를 통해서 컨테이너가 생성되고, 최초로 실행될 때 수행하는 명령어를 설정하고 마무리합니다.

 

 

 

4. EC2 서버에서 실행할 스크립트 파일 생성


기존에 작성했던 배포 스크립트에서는 Git을 통해 EC2로 직접 Pull 하여 코드를 통합하고, Build 하는 과정도 모두 EC2에서 진행했었습니다. 하지만 이제 코드 통합과 Build는 DockerFile에 설정된 대로 Github Actions에서 진행하여 Docker Hub의 Private 저장소로 저장하게 됩니다.

 

즉, 이제 EC2에서는 이 Private Docker Hub 저장소에 있는 이미지를 가져와서 실행만 시켜 Deploy 과정을 마무리하는 스크립트를 실행하면 완료됩니다.

# start-docker.sh

echo "1. Pull Docker Image"
sudo docker pull khsrla9806/ginger-hotel:lastest

echo "2. Stop and Remove Container / Remove <none> Image"
sudo docker stop ginger-hotel
sudo docker rm -f $(sudo docker ps -qa -f status=exited)
sudo docker rmi -f $(sudo docker images -qa --filter "dangling=true")

echo "3. 새로운 이미지 실행"
sudo docker run -d --name ginger-hotel -p 80:8080 -v ./logs:/app/logs khsrla9806/ginger-hotel:lastest
  • Github Actions에서 코드 통합, Build를 거친 새로운 Docker Image를 가져옵니다.
  • 현재 실행되고 있는 컨테이너는 종료하고, 기존의 컨테이너와 이미지는 모두 삭제합니다. (계속 누적되는 것을 방지)
  • 새롭게 가져왔던 이미지를 실행시키고 배포를 완료합니다.

 

 

 

5. Github Actions WorkFlow 설정


이제 DockerFile까지 설정을 완료했기 때문에 Github Actions를 통해서 제가 설정한 DockerFile대로 이미지를 만들어 제 Private Docker Hub 저장소에 이미지를 올리고, SSH로 EC2에 접속하여 새롭게 작성한 start-docker.sh 스크립트 파일을 실행할 수 있도록 Github Actions WorkFlow를 설정해야 합니다.

name: GINGER PRODUCTION SERVER DEPLOY

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest

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

      - name: Nodejs 16
        uses: actions/setup-node@v3
        with:
          node-version: 16.20.2
          cache: 'npm'

      - name: Setting Production Env
        run: echo "${{ secrets.PRODUCTION_ENV }}" >> .env.prod

      - name: Login Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      - name: Build and Push Docker Image
        uses: docker/build-push-action@v4.0.0
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: ${{ secrets.DOCKERHUB_REPO }}/ginger-hotel:lastest

      - name: SSH Remote Command
        uses: appleboy/ssh-action@v0.1.4
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.KEY }}
          port: ${{ secrets.PORT }}
          timeout: 40s

          script: ./start-docker.sh

 

위에서 ${{ secrets.USERNAME }} 이런 부분들은 Github Actions에서 사용할 수 있는 Secret 변수들을 설정해 둘 수 있습니다.

 

 

 

6. 동작 확인


이제 main 브랜치에 Merge가 되는 순간 작성해 둔 WorkFlow대로 Github Actions가 실행됩니다.

 

모든 Flow가 완료되면 자동화 배포 작업이 완료됩니다.

 

이렇게 변경한 후의 아키텍처는 다음과 같습니다.

 

 

지금까지 프로젝트의 CI/CD를 할 때, 처음에 소개했던 것 처럼 EC2 서버에게 너무 많은 책임을 부여했던 것 같아서 늘 도커로 해보자라고 생각을 했었는데 드디어 해볼 수 있게 되어 값진 경험을 가져간 것 같습니다.

 

 

 

 

🔗 Reference

https://velog.io/@osk3856/Docker-Ubuntu-22.04-Docker-Installation

 

[Docker] Ubuntu 22.04 Docker 설치

Ubuntu 22.04 에 Docker 설치하기

velog.io

https://blockmonkeys.tistory.com/189

 

Nestjs & Docker & Github Action 활용 자동 배포 구축하기

📌 CICD 란 ? CI (Continuous Integration) : 지속적 통합. 생성 및 수정 코드들이 자동으로 Test → Build 하는 과정을 자동화 CD (Continuous Deploy) : 지속적 배포. CI 이후 배포 환경까지 전달해 자동 배포하는 과

blockmonkeys.tistory.com

https://kuckjwi0928.tistory.com/11

 

docker images 확인시 none 제거 방법

docker image를 빌드 하다보면 실패하거나, 이미지 빌드 시 태그명이 중복되다 보면 아래의 이미지와 같이 REPOSITORY, TAG에 none으로 저장된다. 이런경우 docker에서는 dangling이 된 경우라고 한다. 깔끔하

kuckjwi0928.tistory.com