나 JAVA 봐라

Github Actions 로 배포 자동화하기 본문

DevOps, MLOps

Github Actions 로 배포 자동화하기

cool_code 2024. 6. 5. 00:16

Spring Boot 프로젝트를 EC2에 배포하고 보니, 아직 완성 안된 기능들을 추가해야할 때 jar 파일 빌드 → image로 생성하여 docker hub에 업로드 → EC2에서 프로젝트 이미지 pull → 컨테이너 생성 및 실행…

과 같이 하나하나 해줘야하는 것들이 많고 이 과정이 자동화가 된다면 서비스를 유지보수 하는 것에 있어 편리하겠다는 생각을 했다. 왜 사람들이 Github Action을 쓰는지 이해했다.


GitHub Actions 동작 순서

  1. Github Repository에 프로젝트의 추가사항이나 변경사항을 push 혹은 merge한다.
  2. 테스트 코드를 통과하면 push 혹은 merge가 된다.
  3. Github Actions에서 push 혹은 merge가 된 것을 확인한다.
  4. Project 빌드에 필요한 application.yml 파일 생성한다.
  5. Update된 Project를 빌드한다.
  6. 빌드한 결과로 Docker Image 생성한다.
  7. Docker Hub에 로그인한다.
  8. 생성한 Docker Image를 Docker Hub에 업로드한다.
  9. EC2 Instance에 SSH로 접속한다.
  10. Docker Hub에 업로드 된 이미지를 pull 받아 컨테이너 생성 및 실행
  • 위의 과정을 수행하기 위해서는 Docker Hub 회원 가입, EC2 인스턴스 생성 및 Docker 설치가 진행되어야 한다.

빌드에 필요한 application.yml 생성

우리 프로젝트의 application.yml에는 DB에 연결하기 위한 URL, username, password 등의 민감한 정보가 포함되어 있어 gitignore에 추가되어 있다. 그렇기 때문에 로컬 개발 환경에서는 사용 가능하지만, Github Actions을 통해 빌드하면 프로젝트에 꼭 필요한 정보들이 포함되지 않은 채로 빌드 된다.

그렇다고 application.yml에 있는 파일을 같이 push 할 수없으니 github action에서 어떻게 추가해야할까?

 

Github Actions에는 secret 값을 설정할 수 있는 기능이 있다. 해당 기능 통해 secret 값을 설정하면 민감한 정보를 secret을 통해 가져올 수 있다. 나는 APPLICATION 이름을 가진 secret을 생성했다. 값으로는 application.yml 파일을 복붙했다.

## application.yml

spring:
  datasource:
    url: 링크 ~~
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: 아이디 ~
    password: 비번 ~

 

 

 

secret 값을 생성했다면 GitHub Actions에서 프로젝트가 빌드되기 전에 해당 값을 가져와야 한다.

## .github/workflows/github-action.yml
## application.yml 생성 후 secret 값 복붙

    - uses: actions/checkout@v3
    - run: touch ./src/main/resources/application.yml
    - run: echo "${{ secrets.APPLICATION }}" > ./src/main/resources/application.yml
    - run: cat ./src/main/resources/application.yml

 

 

로컬 환경과 똑같은 경로에 application.yml을 생성한 후, APPLICATION 이라는 이름을 가진 secret의 값을 넣어준다. 이 후 Build를 진행한다.

 

전체 코드는 아래와 같다. 코드의 순서는 제일 위에 작성된 Github Actions 동작순서를 참고하면 된다 !

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle

name: Java CD with Gradle

on:
  push:
    branches: [ "main" ]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest

    steps:

    ## jdk setting
    - uses: actions/checkout@v3
    - name: Set up JDK 11
      uses: actions/setup-java@v3
      with:
        java-version: '11'
        distribution: 'temurin'

    ## application.yml 생성 후 secret 값 복붙
    - uses: actions/checkout@v3
    - run: touch ./src/main/resources/application.yml
    - run: echo "${{ secrets.APPLICATION }}" > ./src/main/resources/application.yml
    - run: cat ./src/main/resources/application.yml

    # Gradle Build를 위한 권한 부여
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew

    # Gradle Build (test 제외)
    - name: Build with Gradle
      run: ./gradlew clean build -x test

    # DockerHub 로그인
    - name: DockerHub Login
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_PASSWORD }}

    # Docker 이미지 빌드
    - name: Docker Image Build
      run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }} . --platform=linux/amd64

    # DockerHub Push
    - name: DockerHub Push
      run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}

    # EC2 인스턴스 접속 및 애플리케이션 실행
    - name: Application Run
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_HOST }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_KEY }}

        script: |
          sudo docker kill ${{ secrets.PROJECT_NAME }}
          sudo docker rm -f ${{ secrets.PROJECT_NAME }}
          sudo docker rmi ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}
          sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}

          sudo docker run -p ${{ secrets.PORT }}:${{ secrets.PORT }} -v mysql-volume:/var/lib/mysql --name ${{ secrets.PROJECT_NAME }} -d ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}

또 다른 방법들

해당 배포 방법에 대해 다른 사람들의 피드백을 받았다.

  • jasypt를 사용하여 .yml 을 암호화하기
  • 서브 모듈 생성하기
    • 키 값을 들고있는 private repository 생성

참고)

GitHub-Actions로 CI/CD 구축하기(AWS, Docker, SpringBoot)