새소식

CI&CD

[CI/CD] Github Actions -> AWS EC2 -> Docker 자동화 해보기

  • -
728x90


이전 글로 Docker에 대해 포스팅 해 보았는데요.

 

먼저 이런 CI/CD에 대한 공부를 하게 된 이야기를 먼저 하고 싶습니다. 이유는 최근 투스 프로젝트를 진행하면서 제일 힘들었던 부분이 있었습니다. FE가 BE의 API를 활용해야 하기 때문에 BE에 지나치게 종속적이었던 것입니다. 물론 mocking API를 이용하여 종속관계를 '완화' 할 수는 있었지만, 제 배움이 부족했던 탓인지 완전한 해결책이 되진 못했습니다. 예를들면 방 목록에 필터 기능을 사용하여 원하는 방만을 출력해야하는 상황에 백엔드의 필터 알고리즘을 고려하여 작동되는지 확인이 어려웠던 점 등이 있습니다.

 

프로젝트를 시작하고 약 2-3달 간 이러한 문제로 끙끙 앓다가 나온 해결책으로 '개발 참여 집중 시간'을 만들었던 기억이 있습니다. 프로젝트 팀원 전부가 언제든 call하면 대화가 가능한 시간대를 정해두는 것이었습니다. 지금 생각해보면 더 좋은 방법이 있음에도 CI/CD 자동화는 어려우니 프로젝트를 끝내고 생각해보려 했던 안일함이 아니었을까 싶네요.

 

말이 길어졌네요. 아무튼 이러한 이유로 이전 프로젝트를 끝마치고 CI/CD에 대해 포스팅하고자 합니다.


앞서 저는 가장 익숙한 Next.js로 진행할 예정입니다.

진행 과정에 필요한 준비물은 아래와 같습니다.

1. Git

2. AWS EC2 인스턴스

3. 이전 글을 통해 EC2 인스턴스에 Docker 설치

4. Next.js

5. Yarn


1. 해당 과정을 위해 레포지토리를 생성해주세요.

[그림1] 레포지토리 생성

 

2. git clone을 이용해 사용하고 계신 소스 코드 편집기에 레포지토리를 연결해주세요.

(필자는 Vscode를 사용)

 

3. 아래 명령어를 통해 next 프로젝트를 생성 및 실행을 해보세요.

$ yarn create next-app [폴더명] --typescript[필수x]

$ cd [생성한 폴더명]

$ mv * ..

$ cd ..

$ rm -Rf [생성한 폴더명]

$ yarn dev

 

이후 localhost:3000 으로 접속하여 정상적으로 실행되는지 확인해주시면 됩니다.

 

디렉토리 설정의 편의를 위해 root로 생성한 폴더를 옮겼습니다.

 

4. 그리고 root 디렉토리에 Dockerfile과 .dockerignore을 생성해주세요.

확장자는 없습니다.

 

Dockerfile  ** 반드시 대문자 소문자를 구별하여 똑같이 생성해주세요. **

FROM node:latest

# 디렉토리 설정
WORKDIR /usr/src/app
ADD . /usr/src/app/

# 의존성 파일 가져오기
COPY package.json ./
COPY yarn.lock ./

# 의존성 파일 설치
RUN yarn

# 현재 디렉토리의 파일 가져오기
COPY . .

# 빌드
RUN yarn build

# 모든 아이피를 열어주고 3000번 포트로 개방
ENV HOST 0.0.0.0
EXPOSE 3000

CMD ["yarn", "start"]

 

.dockerignore

.next
node_module
README.md
.gitignore

 

여기까지 하시면 토대는 완성됐습니다. 이제 EC2에 설치된 Docker에 이미지를 올려야하는데 소스는 현재 Local에 있습니다. 어떻게 옮겨야 할까요? 제가 찾은 방식은 ghcr(github container registry) 및 self-hosted runner를 이용하는 방식입니다.

ghcr을 사용하기 위해선 github token이 필요합니다. 먼저 token 발급부터 해보겠습니다.

 

5. 아래와 같이 Token을 받아주세요.

[그림 2-1] Setting 접속
[그림2-2] 왼쪽 메뉴 중 제일 하단
[그림2-3]
[그림 2-4]

이렇게 토큰을 생성하고나면 암호화된 토큰을 줄텐데 절대 혼자만 알 수 있는 경로로 보관하신 후에 현재 프로젝트 레포지토리로 갑니다.

아래와 같이 토큰 Name은 ghcr_token으로 해주시고 Value에 아까 저장해둔 토큰을 입력해주세요.

이러면 ghcr에 접속할 수 있는 설정은 완료했습니다.

 

이제 workflow를 작성하여 github actions가 저희가 원하는 행동을 하도록 만들어줘야 합니다.

 

6. Workflow 작성

소스 코드 편집기에서 root Dir에 .github 폴더를 생성하시고 .github 폴더에서 workflows 라는 폴더를 생성해주세요.

이후 workflows라는 폴더안에 [원하는 파일명].yml 파일을 생성한 후 아래 내용을 작성해주세요.

name: CI/CD

# branch 중 main을 향한 push 행동이 일어났을 시 실행됩니다.
on:
  push:
    branches: [main]

# 환경변수 입니다.
# DOCKER_IMAGE는 ghcr가 적용되는 경로입니다.
env:
  DOCKER_IMAGE: ghcr.io/${{ github.actor }}/[내 레포지토리 명]
  VERSION: ${{ github.sha }}
  NAME: go_cicd

# 하나의 함수라고 보시면 될 것 같습니다. jobs안에 등록된 steps들을 하나하나 실행합니다.

jobs:
  build:
    name: Build
    
    # ubuntu 환경으로 실행하겠다는 의미입니다.
    runs-on: ubuntu-latest

    steps:
    # 내 소스코드들을 먼저 checkout해서 가져옵니다.
      - name: Check out source code
        uses: actions/checkout@v2

	# Node.js 16버전을 설치해줍니다.
      - name: Install Node.js 16
        uses: actions/setup-node@v2
        with:
          node-version: '16'
          cache: 'yarn'

	# 이 부분은 테스트 코드를 실행하기 앞서 의존성 파일을 설치하기 위해 실행합니다.
    # 따라서 테스트 코드 자동화가 필요하지 않다면 지우셔도 됩니다.
      - name: Install dependencies
        run: yarn
      - name: Run test
        run: yarn test

	# 여기부터 docker를 위한 작업이 시작됩니다.
      - name: Set up docker buildx
        id: buildx
        uses: docker/setup-buildx-action@v1

	# docker layer를 캐시해서 좀 더 빠르게 작동하게 합니다.
      - name: Cache docker layers
        uses: actions/cache@v2
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ env.VERSION }} # runner 설정에서 읽어들일거에요.
          restore-keys: |
            ${{ runner.os }}-buildx-

	# ghcr로 접속하구요.
      - name: Login to ghcr
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GHCR_TOKEN }}
     
	# 이 부분은 github Name이 대문자인 경우 오류가 납니다.
    # 필요없다면 지우셔도 됩니다. 다만 IMAGE_REPOSITORY가 사용되는 부분은
    # Env에 저장된 DOCKER_IMAGE로 대체하시면 됩니다. 수정이 어려우시다면 그냥 쓰셔도 무방합니다.
      - name: set lower case repo name
        run: |
          echo IMAGE_REPOSITORY=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV

	# 빌드와 푸시를 시작합니다.
      - name: Build and push
        id: docker_build
        uses: docker/build-push-action@v2
        with:
          builder: ${{ steps.buildx.outputs.name }}
          push: true
          tags: ghcr.io/${{ env.IMAGE_REPOSITORY }}/aws-ec2-docker-github-actions:latest

  # 이건 self-hosted runner를 이용해 docker container까지 실행하는 CD 절차입니다.
  deploy:
    needs: build
    name: Deploy
    runs-on: [self-hosted, label-go]
    steps:
      - name: Login to ghcr
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GHCR_TOKEN }}

      - name: set lower case repo name
        run: |
          echo IMAGE_REPOSITORY=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV

      - name: Docker run
        run: |
          docker stop ${{ env.NAME }} && docker rm ${{ env.NAME }} && docker rmi ghcr.io/${{ env.IMAGE_REPOSITORY }}/aws-ec2-docker-github-actions:latest
          docker run -d -p 80:3000 --name ${{ env.NAME }} --restart always ghcr.io/${{ env.IMAGE_REPOSITORY }}/aws-ec2-docker-github-actions:latest

위 workflow 기준으로는 Host가 80으로 접속하면 3000번 Container로 포트 포워딩 하게 되어있으니 필요에 맞게 수정하셔서 사용해주세요.

 

참고로 여기서 push하셔서 Github Actions를 실행하셔도 배포가 되지 않습니다!

아직 러너를 작동시키지 않았기 때문입니다.

 

7. Self-hosted runner 적용하기

Git에서 해당 레포지토리 Setting - Action - Runner로 접속하셔서 new self-hosted runner를 눌러주세요!

 

[그림 3-1]

 

위와 같은 화면이 출력될텐데요. 자신의 서버가 사용하는 OS를 선택하시면 됩니다.

변경 후에 더 아래를 EC2에 접속하여 한줄씩 입력해주세요.

여기서 주의하실 점은 Configure 절차에서 러너의 이름과 라벨 명 등을 묻는데요.

라벨 명을 자신이 하고싶은 명으로 정하셔서 workflow의 deploy에서 runs-on에 배열식으로 이름을 적어주시면 됩니다.

[그림 3-2]

Github 에서 명령어까지 친절하게 복사도 할 수 있게 해놓았네요! 참고해주시면 됩니다.

*** ./run.sh 대신에 nohup ./run.sh & 을 입력하셔야 백그라운드에서 돌아갑니다. ***

 

[그림 3-3]

 

Status를 통해 돌아가고 있다는 것을 확인할 수 있습니다.

 

이제 main push를 통해 github actions를 실행시켜 보시면 Actions 탭에서 CI/CD 과정을 확인하실 수 있습니다!

정상 작동 하셨다면 인스턴스의 public IP를 통해 접속해보시면 원하시는 화면이 나타납니다 ㅎㅎ 수고하셨습니다.

 

 

 

[ 포스팅에 오류가 있다면 댓글로 남겨주세요. 확인 후 정정하겠습니다. ]

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.