Programming/Tips, Fix, Anything Else

[TFAE] 스프링부트 프로젝트 GitHub Action CI&CD연대기 - 2

Supreme_YS 2022. 2. 18. 09:00

 

 

[TFAE] 스프링부트 프로젝트 GitHub Action CI&CD연대기 - 1

현재의 상황 아일랜드에 거주하는 한국인 유학생들을 대상으로 한 거래 플랫폼을 제작중이다. 일명 아-거 . 열심히 코드를 작성하고, 이것 저것 테스트를 마치고 이제 세상에 선보여야 할 때가

supreme-ys.tistory.com

2편을 시작하기에 앞서서, 1편을 꼭 봐주시고 오시면 좋을 것 같네요!


지난 1편에서의 문제를 요약하자면 다음과 같다.

- 깃헙에 application.properties, application.yml 파일을 업로드하면 안된다.

- 그러면 파일을 제외하면 되잖아!

- 그럼 빌드가 안되잖아.

- 엌ㅋㅋ 미안

 

우리는 고민했다. 약 100시간이 넘는 시간동안.

고민했고, 많은 방법을 시도해보았다. 먼저, 공식문서를 찾아보았다. GitHub Action Documents

공식 문서에서 원하는 답을 찾긴 어려웠고, 하나 깨달은 것은 깃헙 액션은 ./github/workflows 안에 있는 main.yml 파일에 작성된 것을 충실히 수행한다는 것을.

 

그래서 이를 잘 활용하면 되지 않을까..

github action에서 작성한 secrets 와 value

main.yml에서 활용할 수 있는 암호화된 저 값들을 잘 굴려보기로 했다. 아래의 코드는 초창기 구상했던 main.yml의 로직을 간단하게 표현해 보았다.

1. main 브랜치를 기준으로 합시다.

2. 작업할 것들 서버에 빌드할게요.
 - 우분투로요.
 - 작업경로는 여기요.
 - 자바는 이걸로요.
 - 빌드 Gradle이요.
  
3. Gradle로 빌드해주세요

4. 도커 로그인 하고, jar파일을 빌드(이미지화)하고, 도커허브에 푸쉬 부탁요!

5. EC2 연결할게요
 - 도커 허브에서 푸쉬된 걸 pull 할게요!
 - 기존 실행중이던거 멈춰!
 - 기존거 삭제해줘
 - 새로 받은거 실행해줘!
 
6. 이거 되면 슬랙으로 알려줘.

이 작업 순서라면, EC2에 실행이 되고, 슬랙으로 알람이 온다! 문제는 이제부터다. 어느 로직에 application.yml 파일을 껴넣어야 할지..고민했다. 그래서 이때부터 진짜 빌드와 배포 지옥에 빠졌다. 시도해본 도전은 다음과 같다. 깃헙 액션을 제외한 다른 기타 시도들 (S3, CodeDeploy) 등은 생략하고, 깃헙 액션에서 도전을 해본 것만 기술하도록 하겠다. 

 

application.yml 파일과 GitHub Actions의 secrets를 활용할 수 있는 방법 없나?

이 것만 해결되면, 만사 오케이가 될 것 같은 느낌을 강력하게 받았다. 근데 정작 이 전 포스팅에서 작성했듯이 EC2는 모르쇠로 일관했다. 

 

뭔가 접근이 잘못되었다.

아무래도 GitHub Actions의 secrets는 .github/workflows 안에 있는 main.yml 파일만 (예시.${{ secrets.AWS_ACCESS_KEY }})  예시처럼 적용할 수 있는 것 같다. 그래. 그럼 main.yml에서 뭔가 조치를 취해볼 수 있지 않을까?

 

main.yml에서 어떻게 application.yml 파일을 조작해볼 수 있을까..

결론부터 얘기하면 애초에 application.yml을 지웠다.

엥? 지우면 빌드 안된다면서? -> 맞다. 지우면 빌드가 안된다. 따라서, 리눅스 문법을 사용해서 빌드 전에 빌드 경로에 맞게 application.yml 파일을 생성해서 값을 넣어주었다. GitHub Actions Documents와 GitHub Actions Market Place를 뒤적뒤적 하다가 파일 업로드가 가능하게끔 어느 개발자가 upload-artifact를 구성해놨다. 이걸 활용해서 파일을 생성하고 올려서 빌드를 해보자!

 

드디어 성공.

백엔드 개발자 셋이서 아등바등 열심히 로직을 분석하면서 해결했다. main.yml의 로직은 위의 로직에서 추가된 부분(2-1,2,3)을 보겠다.

-----------기존과 동일
1. main 브랜치를 기준으로 합시다.

2. 작업할 것들 서버에 빌드할게요.
 - 우분투로요.
 - 작업경로는 여기요.
 - 자바는 이걸로요.
 - 빌드 Gradle이요.
-----------추가된 부분 

2-1. 저기에 파일 하나 application.yml을 생성할건데요.
2-2. 경로는 여기구요
2-3. 이거 파일에 업로드 해주세요.

-----------기존과 동일
3. Gradle로 빌드해주세요

4. 도커 로그인 하고, jar파일을 빌드(이미지화)하고, 도커허브에 푸쉬 부탁요!

5. EC2 연결할게요
 - 도커 허브에서 푸쉬된 걸 pull 할게요!
 - 기존 실행중이던거 멈춰!
 - 기존거 삭제해줘
 - 새로 받은거 실행해줘!
 
6. 이거 되면 슬랙으로 알려줘.

그러면 application.yml의 내용은 어디다 입력하냐구요? main.yml은 GitHub Actions의 secrets가 동작하는 곳이니까! secrets 값으로 application.yml의 내용만 알차게 담았다. 

APPLICATION에는 application.yml 의 내용이 담겨있다.

실제 구현된 코드를 보자.

name: Java CI with Gradle

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

jobs:
  build:
    runs-on: ubuntu-latest
    env :
      working-directory: ./
      APPLICATION: ${{ secrets.APPLICATION }}
      
    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: '1.8'
              
    - name: Cache Gradle packages
      uses: actions/cache@v2
      with:
        path: |
             ~/.gradle/caches
             ~/.gradle/wrapper
        key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
        restore-keys: |
              ${{ runner.os }}-gradle-
              
    - uses: actions/checkout@v2
    - run: touch ./src/main/resources/application.yml
    - run: echo "${{env.APPLICATION}}" > ./src/main/resources/application.yml
    - uses: actions/upload-artifact@v2
      with:
        name: application.yml
        path: ./src/main/resources/application.yml
    
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
      working-directory: ${{ env.working-directory }}
      
    - name: Build with Gradle
      run: ./gradlew build
      working-directory: ${{ env.working-directory }}
      
    - name: Cleanup Gradle Cache
      if: ${{ always() }}
      run: |
          rm -f ~/.gradle/caches/modules-2/modules-2.lock
          rm -f ~/.gradle/caches/modules-2/gc.properties
          
    - name: Docker build
      run: |
           docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
           docker build -t ${{ secrets.PROJECT_NAME }} .
           docker tag ${{ secrets.PROJECT_NAME }} ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7}
           docker push ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7}
           
    - name: Deploy
      uses: appleboy/ssh-action@master
      with:
       host: ${{ secrets.EC2_SERVER_HOST }}
       username: ec2-user
       key: ${{ secrets.PRIVATE_KEY }}
       envs: GITHUB_SHA
       script: |
            docker rmi $(docker images -q)
            docker pull ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7}
            docker tag ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7} ${{ secrets.PROJECT_NAME }}
            docker stop ${{ secrets.PROJECT_NAME }}
            docker rm ${{ secrets.PROJECT_NAME }}
            docker run -d --name ${{ secrets.PROJECT_NAME }} -p 80:8080 ${{ secrets.PROJECT_NAME }}
               
    - name: action-slack
      uses: 8398a7/action-slack@v3
      with:
        status: ${{ job.status }}
        author_name: Github Action Test
        fields: repo,message,commit,author,action,eventName,ref,workflow,job,took
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
      if: always()

All Secrets in GitHub Actions

전체 secrets 종류는 다음과 같다! 이로써 mapping이 원활하게 이뤄짐을 확인했고, 결과는 성공적으로 빌드가 되었고 원하는 로직대로 배포가 완료됨을 알 수 있다. 무엇보다 application.yml의 민감 정보가 노출되지 않고 암호화된 상태로 안전하게 프로젝트를 관리할 수 있다는 이점이 진짜 크다. 사실 GitHub에서 aws 프로젝트 한 거 검색해서 몇 개 뒤적뒤적하다보면 accesskey와 host들이 다 노출되어 있음을 알 수 있었다. 그래서 이 전에 aws 연습해본다 치고, 안일하게 개인정보를 방치했던 나 자신을 반성한다.

 

아래의 사진은 빌드가 완료되었고, 성공적으로 application.yml 파일이 생성되었음을 확인하는 사진이다. 

빌드 성공, 백엔드 세명이서 소리질렀다.

아래는, 원격 서버인 EC2에서 성공적으로 컨테이너가 실행중인 모습이다.


* 아무리 검색해봐도 자료가 부족하다면, 혹여나 잘못된 생각으로 접근을 하고 있는건 아닌지 의심을 해보며 노선을 바꾸곤 했다. 근데 이 방법은 정말 검색해봐도 누구도 이렇게 시도하지 않았다. 물론 아닐 수도 있지만..(레퍼런스가 적다고하자.) 그래서 혹여나 이 방법으로 배포를 시도해보고자 할 때 진짜 왕도움이 되셨으면 좋겠다. 누적 100시간 이상의 삽질이 만들어 낸 일종의 성과물이라 너무너무너무 뿌듯하다. 함께 머리 맞대고 고민해준 포터와 프랭크에게도 무한 감사를 드리며, 

 

이상, GitHub Actions CI/CD & Docker & Docker Hub & AWS EC2 안전하게 배포했던 연대기를 마칩니다.