본문 바로가기
DevOps/CI CD

[CI/CD] Docker와 Elastic Beanstalk를 사용한 spring boot 프로젝트 CI/CD 자동화 배포2 (실습내용)

by 태옹 2022. 1. 30.

틀린 내용이 있을 수 있어요🥲 틀린 부분이 있다면 댓글로 알려주시면 감사하겠습니다!

 

🛠️ 사용 기술

  • spring boot
  • gradle
  • github action
  • docker
  • AWS Elastic Beanstalk

 

AWS 개발자 안내서에 제공된 내용을 확인해보면, Docker Compose를 사용하지 않고 배포하는 경우, zip파일을 생성하지 않고 Dockerfile과 Dockerrun.aws.json파일만 사용하여 애플리케이션을 배포할 수 있다고 설명하고 있다.

dockerrun.aws.json은 v1,v2,v3으로 구분되며 아래 조건에 따라 사용하는 버전을 선택할 수 있다.

  • Dockerrun.aws.json v1 ) 단일 인스턴스 Docker Amazon Linux 2 플랫폼 + Docker Compose를 사용하지 않는 환경
  • Dockerrun.aws.json v2 ) 멀티컨테이너 Docker Amazon Linux AMI 플랫폼
  • Dockerrun.aws.json v3 ) 단일 인스턴스 Docker Amazon Linux 2 플랫폼 + Docker Compose를 사용하는 환경

현재 프로젝트는 단일 인스턴스를 사용하며, Docker Compose를 사용하지 않기 때문에 v1 방법으로 진행해보도록 한다.

 

 

🪜 단계

(1) Dockerfile 작성하기
(2) AWS Elastic Beanstalk 환경 생성하기
(3) IAM 인증키 발급 및 Secrets에 등록하기
(4) Docker Hub 계정 생성 및 Secrets에 등록하기
(5) github action 사용을 위한 deploy.yml 파일 생성하기

(6) Dockerrun.aws.json 파일 작성하기

(7) 배포 확인하기

 

 

📁 새로 만드는 파일들의 디렉토리 경로

 

👇 가장 큰 도움을 받은 블로그

https://itcoin.tistory.com/685

 

Github actions를 이용한 CICD - 2

지난 깃헙액션 포스팅에서 깃헙액션을 이용해 빌드 자동화를 마쳤습니다. 이번 포스팅에서는 도커를 사용해서 배포 준비를 할 것입니다. 도커를 사용한다면 어디에서 동일한 환경의 배포를 할

itcoin.tistory.com


(1) Dockerfile 작성하기

 

프로젝트의 루트 경로에 Dockerfile을 만들고 아래의 내용을 적어준다.

FROM adoptopenjdk/openjdk11
CMD ["./gradlew", "clean", "build"]
ARG JAR_FILE_PATH=build/libs/jpashop-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE_PATH} app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

1) 본인 프로젝트의 JDK 버전에 맞게 수정하기 (현재 프로젝트는 JDK 11버전을 사용함)

2) CMD ./gradlew clean build

(Maven은 CMD ./mvnw clean package)

3) JAR_FILE_PATH라는 변수에 프로젝트의 jar파일 경로를 저장

-> 프로젝트의 jar파일은 프로젝트의 루트 경로에서 아래의 명령어를 통해 build하면 /build/libs/*.jar에 생성됨

(Maven은 /target 폴더 내부에 생성됨)

4) JAR_FILE_PATH에 저장된 값을 app.jar로 복사

5) java -jar app.jar를 실행

 

 

👇 참고 블로그

https://umanking.github.io/2021/07/11/spring-boot-docker-starter/

 

Spring Boot, Dockerfile로 이미지 생성, 배포하기

SpringBoot프로젝트, Dockerfile을 만들어서 이미지를 생성하고 배포해보자!

umanking.github.io

 

 

먼저, 내가 만든 spring boot 프로젝트가 도커 컨테이너에서 잘 실행되는지 확인해보자. 

더보기

(1) 도커 이미지 빌드하기

docker build -t 이미지명:태그명 .

dockerfile을 사용하기 때문에 build 명령어로 이미지를 생성할 수 있다. 

본인이 원하는 이미지명과 태그명을 설정하면 된다. 이 때 마지막 . 을 빼지 않도록 유의한다.

ex) docker build -t ebtest:v1 .

 

(2) 도커 컨테이너 띄우기

docker run -p 호스트포트:컨테이너포트 이미지명:태그명

방금 만든 이미지가 제대로 실행이 되는지 run명령어로 컨테이너를 띄워 확인한다.

이 때, 본인이 만든 컨테이너의 포트가 제대로 일치하는지 확인해야 한다.

spring boot 내부에서 톰캣 서버가 돌아가기 때문에 나는 8080포트가 컨테이너 포트로 잡혀있다. 혹시 모를 오류를 방지하기 위해 호스트 포트를 8081번으로 연결해주었다.

(호스트 포트는 내 로컬과 연결할 포트를 말한다.)

ex) docker run -p 8081:8080 ebtest:v1

 

(3) 실행화면 확인하기

powershell에 spring글자가 뜨고 웹이 실행되면 호스트포트로 접속해서 제대로 나오는지 확인해본다.

제대로 실행된다!

도커 컨테이너가 정상적으로 뜨기 때문에 도커 이미지가 문제없이 잘 만들어졌다는 것을 확인했다.

 


(2) AWS Elastic Beanstalk 환경 생성하기

https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/using-features.environments.html

 

Elastic Beanstalk 환경 생성 - AWS Elastic Beanstalk

이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.

docs.aws.amazon.com

 

플랫폼은 docker를 선택하고, 샘플코드로 빠르게 환경을 생성한다. 

[추가 옵션 구성] 버튼을 눌러 설정하든지, 환경 생성 후 [구성] 메뉴에서 설정하든지 '환경 유형'을 단일 인스턴스로 작업하여 진행하도록 한다.

(로드밸런싱을 고려하면 더욱 좋겠지만 그건 여건이 되면 나중에! 일단 단일 인스턴스로 진행한다.)

 

 


(3) IAM 인증키 발급 및 Secrets에 등록하기

아래의 블로그를 참고해서 IAM 사용자 만들고 인증키를 발급받는다.

인증키는 프로젝트의 git 레포지토리의 Settings -> Secrets에 아래와 같은 이름으로 각각 저장한다.

각각 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY 라는 이름으로 등록해주었다.

👇 참고 블로그

https://jojoldu.tistory.com/549

 

2. Github Action & AWS Beanstalk 배포하기 - profile=local로 배포하기

지난 시간에 만들어둔 Github Action을 통해 profile=local로 Beanstalk에 배포를 진행해보겠습니다. profile=local, 즉, 운영 DB와 구글&네이버 OAuth 를 사용하지 않는 간단한 테스트 용도로만 배포할 예정입니.

jojoldu.tistory.com

키 발급받는 과정 자체는 똑같은데 약간 달라진 점은 정책명이 AWSElasticBeanstalkFullAccess이 사용 중지되고 AdministratorAccess-AWSElasticBeanstalk라는 이름으로 변경되었다는 것! 

 


(4) Docker Hub 계정 생성 및 Secrets에 등록하기

도커 허브 계정이 없다면 도커허브 홈페이지에서 계정을 먼저 생성하자.

 

계정 생성을 완료했다면 (3)번에서 IAM 인증키를 github의 Settings->Secret에 등록한 것과 같이 진행한다.

각각 DOCKERHUB_USERNAME, DOCKERHUB_TOKEN 라는 이름으로 등록해주었다.

토큰이라고 쓰여있긴 하지만 도커허브 패스워드를 저장해주면 된다.

이렇게 네 개의 시크릿 변수를 만들면 된다.


(5) github action 사용을 위한 deploy.yml 파일 생성하기

 

deploy.yml파일은 .github/workflows/deploy.yml 경로에서 생성한다.

 

👇 전체코드 확인

더보기

지금 프로젝트 환경 설정에 맞춘 코드이기 때문에 아래 상세 설명을 통해 본인의 환경대로 변경해야함!

name: Test & Deploy

on:
  push:
    branches: [ master ]

jobs:
  test-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Latest Repo
        uses: actions/checkout@master
      - name: Set up JDK 11
        uses: actions/setup-java@v2
        with:
          java-version: '11'
          distribution: 'adopt'

        ### Maven ###
#      - name: Grant execute permission for Maven
#        run: chmod +x mvnw
#
#      - name: Build with Maven
#        run: ./mvnw package

        ### Gradle ###
      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

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


      - name: Docker build
        run: |
          docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }}
          docker build -t spring-boot-cicd .
          docker tag spring-boot-cicd taeong/spring-boot-cicd:latest
          docker push taeong/spring-boot-cicd:latest


      - name: Get timestamp
        uses: gerred/actions/current-time@master
        id: current-time

      - name: Run string replace
        uses: frabert/replace-string-action@master
        id: format-time
        with:
          pattern: '[:\.]+'
          string: "${{ steps.current-time.outputs.time }}"
          replace-with: '-'
          flags: 'g'

      # Beanstalk 플러그인을 사용
      # 미리 생성해둔 IAM 인증키를 사용
      - name: Beanstalk Deploy
        uses: einaregilsson/beanstalk-deploy@v20
        with:
          aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          application_name: docker-elasticbeanstalk
          environment_name: docker-elasticbeanstalk-env
          version_label: "github-action--${{ steps.format-time.outputs.replaced }}"
          region: ap-northeast-2
          deployment_package: Dockerrun.aws.json

 

 

Checkout Latest Repo와 Set up JDK 11

- name: Checkout Latest Repo
  uses: actions/checkout@master
- name: Set up JDK 11
  uses: actions/setup-java@v2
  with:
    java-version: '11'
    distribution: 'adopt'
  • 테스트를 위한 코드이다. (빼도 괜찮음)

 

 

Grant execute permission for gradlew & Build with Gradle

  ### Gradle ###
- name: Grant execute permission for gradlew
  run: chmod +x gradlew

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

  ### Maven ###
  #      - name: Grant execute permission for Maven
  #        run: chmod +x mvnw
  #
  #      - name: Build with Maven
  #        run: ./mvnw package
  • Gradle 프로젝트를 빌드한다. 이 때, ./gradlew build 명령어를 바로 사용하게 되면 권한이 필요하다는 오류 메시지가 뜨기 때문에 chmod +x gradlew 명령어를 먼저 실행해준다.
  • Maven 프로젝트의 경우 주석처리된 부분을 넣고 gradle 부분을 지워주면 된다.

 

 

Docker build

- name: Docker build
  run: |
    docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }}
    docker build -t 이미지명 .
    docker tag 이미지명 사용자명/이미지명:태그명
    docker push 사용자명/이미지명:태그명
  • docker-compose.yml파일이 필요없는 이유는 deploy.yml파일에서 명령어로 처리해주기 때문이다.
  • Secret탭에 저장했던 도커 id, pw의 변수명을 ${{~}} 안에 넣어주면 파일에 직접 입력하지 않고도 값을 넣어줄 수 있어 보안상으로 효과적이다. 도커 허브에 접속하여 푸시할 수 있게 한다.
  • build명령어로 이미지를 만들고, tag명령어로 image 태그를 지정하고, push명령어로 도커 허브에 이미지를 업로드한다.
  • 도커 허브에 이미지가 업로드되면 도커허브 페이지에서 다음과 같이 확인할 수 있다.

 

 

Get timestamp & Run string replace

- name: Get timestamp
  uses: gerred/actions/current-time@master
  id: current-time

- name: Run string replace
  uses: frabert/replace-string-action@master
  id: format-time
  with:
    pattern: '[:\.]+'
    string: "${{ steps.current-time.outputs.time }}"
    replace-with: '-'
    flags: 'g'
  • 배포 시 version_label에 현재 시간을 표기하기 위해 넣어준 코드이다. (빼도 괜찮음)

 

 

Beanstalk Deploy

# Beanstalk 플러그인을 사용
# 미리 생성해둔 IAM 인증키를 사용
- name: Beanstalk Deploy
  uses: einaregilsson/beanstalk-deploy@v20
  with:
    aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    application_name: 애플리케이션이름
    environment_name: 환경이름
    version_label: "github-action--${{ steps.format-time.outputs.replaced }}"
    region: ap-northeast-2
    deployment_package: Dockerrun.aws.json
  • 마찬가지로 Secret 탭에서 만들어두었던 IAM 인증키 변수를 사용하여 빈즈토크에 배포할 수 있다.
  • application_name과 environment_name에는 빈즈도크 애플리케이션 이름과 환경이름을 적어준다.
  • version_label은 수정해도 되고, 수정하지 않아도 된다. AWS 콘솔에서 버전관리를 위해 현재 시간을 넣어준다.
  • deployment_package는 빈즈토크에 배포할 파일 또는 폴더를 적어준다. zip파일이면 zip파일명을 입력하면 되고, 현재 프로젝트는 Dockerrun.aws.json파일 하나만 빈즈토크로 전달해주면 되기 때문에 다음과 같이 적어준다.

 


(6) Dockerrun.aws.json 파일 작성하기

 

도커 허브에 업로드한 도커 이미지의 정보를 담은 파일을 말한다. 

빈즈토크는 해당 파일의 내용을 읽은 뒤, 이미지를 pull하고 배포하는 작업을 한다.

 

개발자 안내서에서 Docker Compose을 사용하지 않는 경우 Docker 플랫폼을 구성하는 내용을 확인해보자.

프로젝트의 루트 경로에 Dockerrun.aws.json을 만들고 아래의 내용을 적어준다.

{
  "AWSEBDockerrunVersion": "1",
  "Image": {
    "Name": "사용자이름/이미지명:태그명",
    "Update": "true"
  },
  "Ports": [
    {
      "ContainerPort": 컨테이너포트,
      "HostPort": 호스트포트
    }
  ]
}

삽질을 하면서 느낀 것은.. 도커를 사용할 때에는 포트를 제대로 확인하고 연결하는게 중요하다는 것...

빌드한 프로젝트의 포트를 꼭 확인한 뒤 ContainerPort에 적어준다.

HostPort에는 빈즈토크의 서버포트인 5000번을 적어주었다.

 

👇 내 프로젝트의 코드

더보기
{
  "AWSEBDockerrunVersion": "1",
  "Image": {
    "Name": "taeong/spring-boot-cicd:latest",
    "Update": "true"
  },
  "Ports": [
    {
      "ContainerPort": 8080,
      "HostPort": 5000
    }
  ]
}

 

 


(7) 배포 확인하기

배포에 필요한 파일들을 모두 만들어주었으니 배포를 시작해보자.

master 브랜치에 push 후, github의 Action 탭에서 진행상황을 확인할 수 있다.

github action

더보기
후.............. 이러면 이제 에러찾기 대모험 (이렇게 3페이지 더 있음)

테스트와 배포 과정이 문제없이 수행되었다면 AWS Elastic Beanstalk 콘솔에서 배포 상태를 확인할 수 있다.

elastic beanstalk

배포에 관련된 사항들은 이벤트 탭에서 확인할 수 있다.

해당 페이지의 파란색 링크를 클릭하면 배포한 페이지로 접속이 가능하다.

배포 링크로 접속해보면 잘 배포됐음을 확인할 수 있다!

 

댓글