개발/java

Github Action으로 Spring Boot Docker 이미지 배포하기

코딩하는꽃개 2023. 5. 3. 22:37
반응형

 

목적

Github는 Github Action이라는 레포지토리를 연동한 CI/CD를 제공합니다.
 
Github에서 제공하는 Runner를 이용하여 코드 배포시 도커 이미지를 만들어 서버에 배포하는작업을 자동화 하고자 합니다.
 
 

방법

Github Action으로 CI/CD를 하기 위해서는 .github/workflows 내에 yml 파일을 만들어야 합니다.
 

1. 조건 설정

우선 적당한 이름으로 yml 파일을 만들어 준뒤 이름과 조건을 설정합니다.
이번 예제에서는 workflow.yml로 하였습니다.
 
name: Docker Build and Deploy

on:
  # Workflow Manual
  workflow_dispatch:
    inputs:
      isRequiredRemoteDeploy:
        type: boolean
        description: 원격 서버 배포

  # Workflow Event
  #push:
  #  branches: [ master ]
  #pull_request:
  #  branches: [ master ]
 
push 및 pull_request 필드 밑에 브랜치를 설정하여 브랜치를 한정할 수 있으며, 액션 수행 브랜치를 특정할 수 있습니다.
 
또한 input을 이용해 여러 입력을 제어할 수 있습니다.
(이번 예제에서는 원격 서버에 배포를 할지 여부를 선택할 수 있도록 하였습니다.)
 
 

2. 환경 변수 설정

yml 파일 내에서 사용할 환경 변수들을 지정합니다.
별도로 지정하지 않고 직접 쓸 수도 있지만 반복 사용되거나 한곳에서 관리하기 편하게 하기 위해서 지정해주는 것이 좋습니다.
env:
  CACHED_BUILD_PATHS: ${{ github.workspace }}/build
  BUILD_CACHE_KEY: ${{ github.sha }}
  REGISTRY_URL: registry.gitlab.com/codingflowerdog/dev
  REGISTRY_NAME: spring-boot-example

3. 잡 설정

job은 Github Action의 수행 액션을 지정하는 필드입니다.
 
아래 예제에서는 다음과 같이 3단계로 나누어 설정했습니다.
 
  • gradle-build : JAVA 빌드
  • docker-build : 도커 이미지 빌드
  • deploy : 이미지 배포
 
또한 각 단계에서 슬랙으로 결과를 전송하도록 하였으며 사용한 패키지는 8398a7/action-slack@v3 입니다.
 
 
jobs:
  # Gradle Build
  gradle-build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      
      # JAVA 설정
      - name: Set up JDK 11
        uses: actions/setup-java@v2
        with:
          java-version: '11'
          distribution: 'temurin'

      # Gradle build
      - name: Build with Gradle
        id: gradle-build
        run: |
          chmod +x gradlew
          ./gradlew build

      # 빌드 결과 업로드
      - name: Upload Build File
        uses: actions/upload-artifact@v2
        with:
          name: build
          path: build/libs/app.jar
      
      # 빌드 결과 알림
      - name: Notification
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          author_name: Github Action Notification
          fields: repo, message, commit, workflow, job, took
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
  
  # Docker Build
  docker-build:
    runs-on: ubuntu-latest
    needs: gradle-build
    
    steps:
      - uses: actions/checkout@v2
      
      # 빌드 결과 다운로드
      - name: Download Build File
        uses: actions/download-artifact@v2
        with:
          name: build

      # 빌드 파일 경로 설정
      - name: Create Build Directory
        run: |
          mkdir -p build/libs 
          mv app.jar build/libs/app.jar

      # Docker 빌드
      - name: Docker Build
        run: |
          docker build -t spring-boot-example .
          docker tag spring-boot-example:latest ${{ env.REGISTRY_URL }}/${{ env.REGISTRY_NAME }}:${{ github.run_number }}

      # Upload Docker Image Artifact
      - name: Upload Docker Image Artifact
        uses: ishworkh/docker-image-artifact-upload@v1
        with:
          image: ${{ env.REGISTRY_URL }}/${{ env.REGISTRY_NAME }}:${{ github.run_number }}

      # 빌드 결과 알림
      - name: Notification
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          author_name: Github Action Notification
          fields: repo, message, commit, workflow, job, took
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
  # Deploy
  deploy:
    runs-on: ubuntu-latest
    needs: docker-build

    steps:
      # Download Docker Image Artifact
      - name: Download Docker Image Artifact
        uses: ishworkh/docker-image-artifact-download@v1
        with:
          image: ${{ env.REGISTRY_URL }}/${{ env.REGISTRY_NAME }}:${{ github.run_number }}

      # Gitlab 로그인
      - name: Gitlab Login
        run: docker login https://registry.gitlab.com/my_registry -u ${{ secrets.GITLAB_USER_NAME }} -p ${{ secrets.GITLAB_CONTAINER_ACCESS_TOKEN }}

      # Docker Push
      - name: Gitlab Push
        run: docker push ${{ env.REGISTRY_URL }}/${{ env.REGISTRY_NAME }}:${{ github.run_number }}
      
      # 배포 결과 알림
      - name: Notification
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          author_name: Github Action Notification
          fields: repo, message, commit, workflow, job, took
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
          
      # Remote Deploy
      - name: Remote Deploy
        if: ${{ github.event.inputs.isRequiredRemoteDeploy }} == 'true'
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.REMOTE_HOST }}
          username: ${{ secrets.REMOTE_SSH_ID }}
          key: ${{ secrets.REMOTE_SSH_KEY }}
          port: ${{ secrets.REMOTE_SSH_PORT }}
          script: |
            bash --login <<EOF
            docker pull ${{ env.REGISTRY_URL }}/${{ env.REGISTRY_NAME }}:${{ github.run_number }}
            docker run -it -d -p ${{ secrets.DOCKER_PORT }}:${{ secrets.APPLICATION_PORT }} --name ${{ env.REGISTRY_NAME }} ${{ env.REGISTRY_URL }}/${{ env.REGISTRY_NAME }}:${{ github.run_number }}
            EOF
            
      # 원격 서보 배포 결과 알림
      - name: Notification
        if: ${{ github.event.inputs.isRequiredRemoteDeploy }} == 'true'
        uses: 8398a7/action-slack@v3
        with:
          status: custom
          author_name: Github Action Notification
          custom_payload: |
            {
              "attachments": [{
                  color: '${{ job.status }}' === 'success' ? 'good' : '${{ job.status }}' === 'failure' ? 'danger' : 'warning',
                  "blocks": [
                    {
                      "type": "header",
                      "text": {
                        "type": "plain_text",
                        "text": "Remote Deploy :computer:",
                        "emoji": true
                      }
                    },
                    {
                      "type": "section",
                      "fields": [
                        {
                          "type": "mrkdwn",
                          "text": "*Host*\n${{ secrets.REMOTE_HOST }}"
                        },
                        {
                          "type": "mrkdwn",
                          "text": "*Registry*\n${{ env.REGISTRY_URL }}"
                        },
                        {
                          "type": "mrkdwn",
                          "text": "*Docker Image*\n${{ env.REGISTRY_NAME }}"
                        },
                        {
                          "type": "mrkdwn",
                          "text": "*Tag*\n${{ github.run_number }}"
                        }
                      ]
                    }
                  ]
              }]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
 
한 가지 주의할 점은 위 코드처럼 Job을 분리하면 각 Job들의 수행 결과가 공유되지 않는다는 것입니다.
 
예제에서는 도커 이미지 빌드 결과를 전달하기 위해 artifact에 업로드 후 다음 job에서 다운로드 하여 사용하고 있습니다.
 

업로드

# Upload Docker Image Artifact
- name: Upload Docker Image Artifact
  uses: ishworkh/docker-image-artifact-upload@v1
  with:
    image: ${{ env.REGISTRY_URL }}/${{ env.REGISTRY_NAME }}:${{ github.run_number }}

다운로드

# Download Docker Image Artifact
- name: Download Docker Image Artifact
  uses: ishworkh/docker-image-artifact-download@v1
  with:
    image: ${{ env.REGISTRY_URL }}/${{ env.REGISTRY_NAME }}:${{ github.run_number }}

 

결과

워크플로우 실행 화면

 

워크플로우 실행 결과

 

artifact 업로드 결과

 

 
반응형