Git & CS

Github Actions를 이용한 CI/CD 구축기

승민아 2024. 6. 21. 16:04

기존 수동 배포 과정

이전의 프로젝트 아키텍처를 보자.

이전의 프로젝트 아키텍처

 

빌드와 배포 모두 수동으로 진행했었다.

테스트 코드도 돌려보고 빌드하고 이미지로 만들어서 EC2에서 구동시키는 방법이였다.

 

이제 이 과정들을 자동화해보고자 한다.

 

목표

Github Push -> 테스트 코드 실행 -> Docker 이미지 build, push -> EC2에서 pull -> 이미지 동작

 

구축

Github Repository

깃허브 리포지토리에 Actions 탭이 있다.

 

Gradle

현재 프로젝트는 Gradle로 빌드한다. Configure를 누르자.

 

script

.github/workflows 경로에 Actions를 위한 스크립트를 작성해야한다.

 

필자는 다음과 같이 구축했다.

name: CI/CD

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read

    services:
      mysql:
        image: mysql:8.0.33
        ports:
          - 3306:3306
        env:
          MYSQL_DATABASE: bucket_list
          MYSQL_ROOT_PASSWORD: ${{secrets.MYSQL_ROOT_PASSWORD}}

    steps:
    - uses: actions/checkout@v4

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

    - name: Setup Gradle
      uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0

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

    - name: MySQL Init
      run: |
        sudo mysql -h 127.0.0.1 -P 3306 -uroot -p${{secrets.MYSQL_ROOT_PASSWORD}} < ./init-tables/init-table.sql

    - name: Build with Gradle Wrapper
      run: ./gradlew build

    - name: Login DockerHub
      uses: docker/login-action@v1
      with:
        username: ${{secrets.DOCKERHUB_USERNAME}}
        password: ${{secrets.DOCKERHUB_TOKEN}}

    - name: Docker Build & Push
      uses: docker/build-push-action@v2
      with:
        context: .
        file: ./Dockerfile
        platforms: linux/amd64
        push: true
        tags: ${{secrets.DOCKERHUB_TAG}}

  dependency-submission:

    runs-on: ubuntu-latest
    permissions:
      contents: write

    steps:
    - uses: actions/checkout@v4

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

    - name: Generate and submit dependency graph
      uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0

  deploy:

    runs-on: ubuntu-latest
    needs: build

    steps:
      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{secrets.AWS_HOST}}
          port: 22
          key: ${{secrets.AWS_SSH_KEY}}
          username: ubuntu
          script: |
            docker pull ${{secrets.DOCKERHUB_TAG}}
            docker stop bucket_list_server
            docker rm bucket_list_server
            docker image prune -f
            docker run -d \
              --name bucket_list_server \
              -p 8080:8080 \
              -e SPRING_DATASOURCE_URL=${{secrets.SPRING_DATASOURCE_URL}} \
              -e SPRING_DATASOURCE_PASSWORD=${{secrets.MYSQL_ROOT_PASSWORD}} \
              ${{secrets.DOCKERHUB_TAG}}

${{secrets.키}}는 중요한 정보는 따로 Key, Value로 관리하여 변환한다. 추가 방법은 나중에~

 

스크립트에 대한 설명은 생략하고 대략적인 설명 및 동작은 다음과 같다.

  1. Make application.properties
    1. 현재 프로젝트의 .gitignore에 application.properties가 추가되어 있다.
    2. 그래서 프로젝트를 빌드할때 properties 파일을 생성해줄 필요가 있다.
  2. MySQL Init : 테스트 코드를 동작하기위해 테이블을 생성하고 세팅한다.
  3. Login DockerHub : 이미지 build, push를 위한 로그인
  4. Docker Build & Push : 도커 이미지 빌드 & 푸쉬
  5. Deploy to server : EC2 서버에 접속하여 도커 컨테이너 실행

 

MySQL Init - 디렉터리 구조

테이블 세팅을 위한 .sql 파일의 위치는 위와 같다.

 

CREATE DATABASE IF NOT EXISTS bucket_list;
USE bucket_list;

CREATE TABLE IF NOT EXISTS member (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    login_id VARCHAR(255),
    login_pwd VARCHAR(255)
);

위와 같이 테이블 세팅에 필요한 스크립트를 작성해주자.

 

${{secrets}} 설정

프로젝트의 Settings 탭에 들어가보자.

Security -> Secrets And variables -> Actions

Actions를 누르고

 

New repository secret를 누르면

위와 같이 Name은 Key로 Secret는 Value로 사용할 값을 설정할 수 있다.

Name에 Hello를 쓰면 gradle.yml에서 ${{secrets.Hello}}로 값을 사용할 수 있다.

 

결과

main 브랜치에 push가 발생할 경우

스크립트를 동작하며

Actions 탭에서 Workflow의 결과를 볼 수 있다.

 

workflow 결과

 

이제 내 리포지토리에 push함으로써 빌드와 테스트, 배포까지 자동화할 수 있다.!!

너무 편하다.