본문 바로가기

PROGRAMMING/CI&CD

[GITHUB] SpringBoot 프로젝트 Github Action으로 EC2에 Docker 배포하기

이전 포스팅

https://dvpdvp.tistory.com/221

 

[GITHUB] Github Action으로 EC2 접근 에러 ssh.ParsePrivateKey: ssh: no key found

에러 메시지  상황Github Action으로 스크립트를 실행하여 EC2에 접근, Docker로 이미지를 끌어와서 배포하는 구조이다.EC2에 접근 시 필요한 인스턴스 정보 및 key는 Github Secret에 저장 해둔 상태. 해

dvpdvp.tistory.com

https://dvpdvp.tistory.com/220

 

[EC2] key is not available: NO_PUBKEY error

사건의 발단... ec2 인스턴스를 새로 올려서 도커부터 설치하려 yum install 명령어 실행 sudo yum install docker -y  하지만? Command 'yum' not found, did you mean: ... 요런 에러 발생 검색해보니 apt update를 먼저

dvpdvp.tistory.com

 

목적

Master Branch Push가 발생하면 Github Action으로 자동 빌드 후 AWS EC2에 접근하여 Docker Pull -> Deploy 까지 자동화

 

작업 목록

  1. AWS에 Docker 설치
  2. AWS EC2 생성
  3. Docker 계정 생성
  4. 프로젝트에 DockerFile 생성 및 작성 후 로컬에서 도커로 띄워보기
  5. Github Secret 등록
  6. Github Action에 gradle.yml 생성

Github Action Test를 위한 프로젝트 링크

 

GitHub - HWANGMA27/AWS-TEST: Repository for test aws and docker

Repository for test aws and docker. Contribute to HWANGMA27/AWS-TEST development by creating an account on GitHub.

github.com

 

예제 Dockerfile (확장자x)

FROM openjdk:17
ARG JAR_FILE_PATH=/build/libs/aws-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE_PATH} test.jar
ENTRYPOINT ["java", "-jar", "test.jar"]

 

본인 개발 환경 및 경로에 따라서 참고하여 작성하면 된다.

별건 아니지만 DockerFile이라던가 dockefile이라던가.. 이름이 달라지면 빌드가 안될 수 있다..ㅎㅎ

로컬에서 작성 후 Build & Run까지 문제 없이 되면 Push하면 된다.

 

자동배포를 위한 Action 추가

github action

Repository > Actions 탭에 Java with Gradle Configure을 클릭하면

친절하게 gradle 빌드까지 작성된 양식을 던져준다.

 

steps에서 그 뒷부분 부터 작성하면 되는데 아래는 내가 작성한 gradle.yml 전문이다.

 

# 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 CI with Gradle

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

jobs:
  build:

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

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

    # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html).
    # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version.
    #
    # - name: Setup Gradle
    #   uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
    #   with:
    #     gradle-version: '8.5'
    #
    # - name: Build with Gradle 8.5
    #   run: gradle build
    - name: make application-prod.yml
      run: |
        cd ./src/main/resources
        touch ./application-prod.yml
        echo "${{ secrets.APPLICATION_PROD }}" > ./application-prod.yml
        
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew

    - name: Build with Gradle
      run: ./gradlew build -x test

    - name: Docker build
      run: |
        docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
        docker build -t app .
        docker tag app ${{ secrets.DOCKER_USERNAME }}/test:latest
        docker push ${{ secrets.DOCKER_USERNAME }}/test:latest
        
    - name: Deploy
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.HOST }} # EC2 인스턴스 퍼블릭 DNS
        username: ${{ secrets.EC2_USER }} # EC2 user
        key: ${{ secrets.PRIVATE_KEY }} # pem 키
        # 도커 작업
        script: |
          docker pull ${{ secrets.DOCKER_USERNAME }}/test:latest
          docker stop $(docker ps -a -q)
          docker run -d --log-driver=syslog -p 8080:8080 -e SPRING_PROFILES_ACTIVE=prod ${{ secrets.DOCKER_USERNAME }}/test:latest
          docker rm $(docker ps --filter 'status=exited' -a -q)
          docker image prune -a -f
          
  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'

    # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies.
    # See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md
    - name: Generate and submit dependency graph
      uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0

 

 

Secret 변수 정리

secrets.DOCKER_USERNAME : docker user name

secrets.DOCKER_PASSWORD : docker access token or password

secrets.HOST : EC2 인스턴스 퍼블릭 DNS or 탄력적 IP

secrets.EC2_USER :  EC2 user name ex)ubuntu

secrets.PRIVATE_KEY : EC2 ssh 접근 시 사용하는 pem key

 

Github Secret 용도 및 등록 방법

gradle.yml에 포함 되는 내용 중 노출이 되면 안되는 변수들을 등록하는 용도이다.

위에 정리 해놓은 변수들의 경우 스크립트로 노출이 되면 다른 사용자가 접근 및 조작을 할 수 있게 됨으로 따로 관리를 해야하는데 Github Secret에 저장하고 yml에서 끌어다 쓸 수 있다.

 

 

Repository > Settings > Secrets and variable > Actions

 

 

Repository에서 사용 할 변수를 저장/변경/추가 작업을 할 수 있다.

연필 표시가 수정인데 일반 수정 기능처럼 이전 값이 표시되진 않는다. 

새로운 값으로 변경만 가능한데, 보안성을 생각하면 당연한 부분인것 같다.

 

위에 New repository secret으로 값을 등록 한 후에는 yml에서 {{ secretes.secret_name }} 으로 가져다 사용하면 된다.

 

이후에 작업 할 내용

현재는 Security Group에 22번 포트(ssh)가 전체 ip 오픈되어 있는 상태인데, 현실적으로 서비스 운영 할 땐 ip 제한을 둬야 한다.

이 경우 Github Action에서 접근하는 ip를 유동적으로 추가하고 배포 후 삭제하는 작업이 필요한데 이를 적용 할 예정.