[TIL] CI/CD - Github Actions & Docker

phdljr·2024년 2월 6일
0

TIL

목록 보기
62/70

최종 프로젝트에서 백엔드 서버의 CI/CD를 SSH로 간단하게 구축했었다.
그러나, 가끔 scp나 ssh 명령어에서 오류가 뜨는 현상을 종종 직면하게 됐다.

조언을 들어본 바, scp나 ssh로 파일을 전송하거나 작업을 할 때, 메모리 부족 현상으로 위와 같은 오류가 뜰 수도 있다고 한다.

ssh의 에러코드 137번에 대해 검색해본 결과, 메모리 부족 현상이 맞는 것 같았다.

그래서 CI/CD가 항상 제대로 작동할 수 있도록, ssh와 scp 대신 Docker로 바꾸는 작업을 하게 되었다.

이러한 작업 과정에 대해 기록해보는 시간을 가져보겠다.


Docker를 선택하게 된 이유

  • 컨테이너를 통해 독립된 환경을 제공해주기 때문
  • Github Actions의 Runner에 기본적으로 Docker가 설치돼있음
  • Docker Hub가 존재하기 때문
    • 이미지를 만들어서 Docker Hub에 올려두고, 배포된 서버에서 만들어진 이미지를 다운로드하는 방식으로 진행
    • Github Actions에서는 이미지 파일을 만들고, 이를 Docker Hub에 push하는 방식으로 구성
    • push가 정상적으로 됐다면, SSH로 EC2에 접속한 다음, Docker Hub로부터 이미지를 pull하고 컨테이너를 생성해서 실행시키는 순서로 구성

workflows 생성

name: CI/CD

on:
  push:
    branches:
      - main

jobs:
  cicd:
    runs-on: ubuntu-latest
    steps:
      - name: Set up CI server
        uses: actions/checkout@v4

      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          distribution: 'corretto'
          java-version: '21'

      # Gradle caching
      - name: Gradle Caching
        uses: actions/cache@v4
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      - name: Make application-prod.yml
        run: |
          cd ./src/main/resources
          touch ./application-prod.yml
          echo "${{ secrets.YML_PROD }}" > ./application-prod.yml
        shell: bash

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

      - name: gradlew bootJar
        run: ./gradlew bootJar

      - name: Docker build & push
        run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -t one-click-recipe:latest .
          docker tag one-click-recipe:latest ${{ secrets.DOCKER_USERNAME }}/one-click-recipe:latest
          docker push ${{ secrets.DOCKER_USERNAME }}/one-click-recipe:latest

      - name: SSH Commands
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ec2-user
          key: ${{ secrets.SSH_KEY }}
          port: 22
          script_stop: false
          script: |
            sudo docker pull ${{ secrets.DOCKER_USERNAME }}/one-click-recipe
            sudo docker stop one-click-recipe
            sudo docker rm one-click-recipe
            sudo docker run -d -p 8080:8080 --name one-click-recipe ${{ secrets.DOCKER_USERNAME }}/one-click-recipe
            sudo docker image prune -f

Make application-prod.yml

      - name: Make application-prod.yml
        run: |
          cd ./src/main/resources
          touch ./application-prod.yml
          echo "${{ secrets.YML_PROD }}" > ./application-prod.yml
        shell: bash
  • 배포할 때 쓰이는 설정 값들을 github에 저장된걸로 가져와서 사용하는 부분

Docker build & push

      - name: Docker build & push
        run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -t one-click-recipe:latest .
          docker tag one-click-recipe:latest ${{ secrets.DOCKER_USERNAME }}/one-click-recipe:latest
          docker push ${{ secrets.DOCKER_USERNAME }}/one-click-recipe:latest
  • Docker를 통해 이미지를 빌드하고 Docker Hub로 push하는 과정
  • github secret에 저장된 값을 불러와서 로그인을 진행
  • 이미지를 빌드한 뒤, push

SSH Commands

      - name: SSH Commands
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ec2-user
          key: ${{ secrets.SSH_KEY }}
          port: 22
          script_stop: false
          script: |
            sudo docker pull ${{ secrets.DOCKER_USERNAME }}/one-click-recipe
            sudo docker stop one-click-recipe
            sudo docker rm one-click-recipe
            sudo docker run -d -p 8080:8080 --name one-click-recipe ${{ secrets.DOCKER_USERNAME }}/one-click-recipe
            sudo docker image prune -f
  • push된 도커 이미지를 pull해서 실행시키는 과정
  • 이미 실행되고있는 컨테이너를 정지한 뒤에 삭제
  • 컨테이너를 새로 만들어서 실행시킨 뒤, 이미지를 삭제
  • script_stop: false 로 설정을 해둬야 스크립트가 모두 실행됨
    • true로 설정돼있다면, 중간에 하나라도 스크립트가 실패하면 그 뒤의 스크립트는 실행이 안됨

깨알 정보

  • 컨테이너 로그 확인하기
    • docker logs (컨테이너 이름)

참조

https://specificlanguages.com/posts/2022-07/14-exit-code-137/
https://velog.io/@leeeeeyeon/Github-Actions%EA%B3%BC-Docker%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-CICD-%EA%B5%AC%EC%B6%95

profile
난 Java도 좋고, 다른 것들도 좋아

0개의 댓글