티스토리 뷰

반응형

# 서론

스프링 부트 프로젝트를

Github Actions + Docker + Docker Hub + AWS EC2로 CI/CD 파이프라인 구성하는 방법을 기록한다.

기본적인 흐름도는 다음과 같다.

 

1. 로컬에서 작업 -> 깃허브 푸쉬

2. Github Actions이 해당 프로젝트를 빌드하고, 도커 이미지로 생성 후 도커 허브에 Push

3. Github Actions이 AWS EC2에 접속하여 해당 이미지를 Pull 받고, 실행

 

사전 환경

- JDK 17

- AWS EC2 t2.micro (Ubuntu 20.04 LTS)

- Docker Hub 무료 계정

 

먼저 AWS EC2를 생성하고, 해당 인스턴스에 JDK 17를 설치하는 과정은 생략한다.

 

# 도커, 도커 허브 세팅

1. 도커 설치

인스턴스에 도커를 설치하는 과정은 아래 블로그를 참조하도록 하자.

https://insight.infograb.net/docs/aws/installing-docker-on-aws-ec2/

 

AWS EC2 인스턴스(Ubuntu 18.04 LTS)에 Docker CE 설치 | DevSecOps 구축 컨설팅, 교육, 기술지원 서비스 제공

GitLab 을 AWS EC2에 설치하는 방법을 가이드 합니다. Ubuntu Server 18.04 기준으로 Docker 를 이용해서 GitLab 을 설치해서 사용 할 수 있는 가이드 입니다.

insight.infograb.net

 

2. 도커 허브 레포지토리 생성

도커 허브에 해당 이미지들을 Push할 레포지토리를 생성한다.

 

# 프로젝트 세팅 및 Dockerfile 작성

build.gradle 하단에 아래 코드를 추가한다.

bootJar로 jar 빌드 파일 생성시 -plain.jar이 붙은 파일이 생성되는데 이를 없애기 위한 코드와

SNAPSHOT-0.0.1.jar 형태의 이름을 고정적으로 변환하기 위한 코드이다.

 

다음은 Dockerfile이다.

FROM openjdk:17-jdk
ENV APP_HOME=/home/app/
WORKDIR $APP_HOME
COPY build/libs/*.jar plub-server.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","plub-server.jar"]

해당 스크립트를 프로젝트 루트 디렉토리에 Dockerfile (별도의 확장자가 없다.)로 생성해서 넣어준다.

 

이런 형태로 저장해주면 된다.

이제 도커는 이 Dockerfile에 명시된 스크립트를 통해서 도커 이미지를 생성한다.

 

# Github Actions 설정

workflow를 작성하기 전에 workflow 스크립트에서 사용할 비밀 값들을 먼저 세팅해 준다.

 

깃허브 레포로 가서 Settings 탭으로 들어가면 설정할 수 있다.

 

DOCKER_PASSWORD = 도커 허브 계정 비밀번호

DOCKER_USERNAME = 도커 허브 계정 이름 (ex. plub2023)

EC2_IP = AWS EC2 인스턴스의 공개 IP (접속 주소)

EC2_PEM_KEY = EC2 인스턴스 생성시 지정하는 키페어다. 해당 키페어를 메모장으로 연 다음에 안에 있는 모든 내용물 (===로 시작되고 ===로 끝나는것 까지 포함해야함)을 복사한 값

YML_SECRET = DB연결정보와 같은 깃허브에 올라가면 안 되는 내용물을 base64 인코딩한 결과물을 저장한 값

 

만약 오류가 발생한다면 이 부분에서 발생했을 확률이 높으므로 명칭을 제대로 입력했는지, 값을 제대로 넣었는지 다시 한번 확인한다.

 

workflow에서 사용할 비밀 값 세팅 완료

이제 workflow를 작성한다.

name: CI/CD Pipeline using Docker Hub

on:
  push:
    branches: [ "develop" ]
#  pull_request:
#    branches: [ "master" ]

permissions:
  contents: read

jobs:
  build:

    runs-on: ubuntu-latest

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

      - name: Set up yml secrets file
        env:
          YAML_SECRET: ${{ secrets.YML_SECRET }}
          YAML_DIR: src/main/resources
          YAML_FILE_NAME: application-secret.yml
        run: echo $YAML_SECRET | base64 --decode > $YAML_DIR/$YAML_FILE_NAME

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew
      - name: Build with Gradle
        run: ./gradlew bootJar

        ## 3) Docker Hub에 이미지 push 하기
      - name: Docker build
        run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -t ${{ secrets.DOCKER_USERNAME }}/plub-server .
          docker tag ${{ secrets.DOCKER_USERNAME }}/plub-server ${{ secrets.DOCKER_USERNAME }}/plub-server:${GITHUB_SHA::7}
          docker push ${{ secrets.DOCKER_USERNAME }}/plub-server:${GITHUB_SHA::7}
          
        ## 4) Docker Hub에 Push한 이미지를 리눅스 서버에 받아와서 run
      - name: Deploy
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.EC2_IP }}
          username: ubuntu
          key: ${{ secrets.EC2_PEM_KEY }}
          envs: GITHUB_SHA
          script: |
            docker pull ${{ secrets.DOCKER_USERNAME }}/plub-server:${GITHUB_SHA::7}
            docker tag ${{ secrets.DOCKER_USERNAME }}/plub-server:${GITHUB_SHA::7} plub-server
            docker stop server
            docker run -d --rm --name server -p 8080:8080 plub-server

앞서 설정했던 비밀 값들은 secrets.이름 으로 해당 스크립트에서 접근할 수 있다.

 

한 단계씩 차례로 살펴보자면

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

  - name: Set up yml secrets file
    env:
      YAML_SECRET: ${{ secrets.YML_SECRET }}
      YAML_DIR: src/main/resources
      YAML_FILE_NAME: application-secret.yml
    run: echo $YAML_SECRET | base64 --decode > $YAML_DIR/$YAML_FILE_NAME

  - name: Grant execute permission for gradlew
    run: chmod +x gradlew
  - name: Build with Gradle
    run: ./gradlew bootJar

Github Actions가 실행되는 컨테이너에 JDK 17을 세팅한다.

그리고 우리가 빌드시 동적으로 첨부해야할 파일 (application-secret.yml)을 아까 설정한 secrets에서 가져와서 넣고 빌드를 진행한다. 

빌드를 시작하기 위해서는 gradlew를 실행해야 하기 때문에, 실행에 필요한 권한을 설정하고,

이제 bootJar을 실행한다.

 

    ## 3) Docker Hub에 이미지 push 하기
  - name: Docker build
    run: |
      docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
      docker build -t ${{ secrets.DOCKER_USERNAME }}/plub-server .
      docker tag ${{ secrets.DOCKER_USERNAME }}/plub-server ${{ secrets.DOCKER_USERNAME }}/plub-server:${GITHUB_SHA::7}
      docker push ${{ secrets.DOCKER_USERNAME }}/plub-server:${GITHUB_SHA::7}

해당 과정은 이미지를 만들어서 도커 허브에 Push하는 과정을 자동화하는 스크립트이다.

docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}

-> 도커 허브에 로그인 한다. 도커 허브 계정을 통해서 로그인을 진행한다.


docker build -t ${{ secrets.DOCKER_USERNAME }}/plub-server .

-> 도커 허브 계정/이미지 이름으로 이미지를 생성한다. 이때, 도커 허브 레포지토리와 동일하게 이름을 생성해야 Push가 가능하니 주의하자.


docker tag ${{ secrets.DOCKER_USERNAME }}/plub-server ${{ secrets.DOCKER_USERNAME }}/plub-server:${GITHUB_SHA::7}

-> 도커 허브에 Push한 이미지를 분별할 수 있도록 일종의 태깅 작업을 하는 과정이다. (굳이 필요 없긴하지만, 만약 새로운 이미지를 동일한 이름으로 Push하면 덮어씌워진다.)
          

docker push ${{ secrets.DOCKER_USERNAME }}/plub-server:${GITHUB_SHA::7}

-> 생성된 파일을 도커 허브에 업로드

 

    ## 4) Docker Hub에 Push한 이미지를 리눅스 서버에 받아와서 run
  - name: Deploy
    uses: appleboy/ssh-action@master
    with:
      host: ${{ secrets.EC2_IP }}
      username: ubuntu
      key: ${{ secrets.EC2_PEM_KEY }}
      envs: GITHUB_SHA
      script: |
        docker pull ${{ secrets.DOCKER_USERNAME }}/plub-server:${GITHUB_SHA::7}
        docker tag ${{ secrets.DOCKER_USERNAME }}/plub-server:${GITHUB_SHA::7} plub-server
        docker stop server
        docker run -d --rm --name server -p 8080:8080 plub-server

AWS EC2에 지정해 놨던 키페어를 통해서 SSH 접속을 한다.

올렸던 이미지를 Pull 해서 이름을 plub-server로 변경하고,

기존에 실행중이던 컨테이너를 중지한 후에 다시 실행하는 스크립트이다.

 

여기까지 설정하면 정상적으로 파이프라인 구축에 성공한 것이다!

 

성공한 결과를 확인할 수 있다.

반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크