본문 바로가기
프로젝트

Github Actions + Docker Compose + Elastic Beanstalk

by 멍멍구리 2022. 7. 21.

GIthub Actions, Docker compose, Elastic Beanstalk으로 CI/CD 파이프라인 구축하기

딱 이 세 가지 조합으로만 구성된 간단한 CI/CD 파이프라인의 레퍼런스가 없어서 도움이 되시길 바라며 작성합니다. 이 조합은 CI/CD를 구성하는 데에 과도한 리소스를 쏟을 만한 시간이 없을 때 선택하면 좋을 것 같습니다.

 

추상적인 CI/CD 흐름은 아래와 같습니다.

 

정확한 흐름은 아니지만, Local에서 Remote Repository로 Push가 발생하면 GIthub Actions가 이를 감지하여 EB에 Docker Compose 파일을 전달해서 EB 내부에서 실행하도록 구성합니다. 

 

구성 순서는 아래와 같습니다. Docker에 대한 기본적인 이해가 필요합니다.

 

  • Dockerfile 작성
  • Github Actions 작성
  • Docker Compose 작성
  • EB 생성
  • IAM 생성
  • Github Actions 수정

 

1. Dockerfile 작성

1-1. JDK 이미지를 가져오고 빌드의 결과물을 도커 컨테이너 내부에 옮긴 후 애플리케이션을 실행하도록 도커파일을 작성합니다.

FROM openjdk:11-jdk-slim
WORKDIR /usr/springboot
COPY build/libs/linkApp-0.0.1-SNAPSHOT.jar /usr/springboot/app.jar
ENTRYPOINT ["java", "-jar", "/usr/springboot/app.jar"]

 

1-2. 현재는 WAS가 한 대지만 추가적으로 늘어날 경우를 고려해서 Nginx를 사용합니다. Nginx 이미지를 가져오고 설정파일을 컨테이너 내부에 옮기도록 도커파일을 작성합니다.

FROM nginx:alpine
COPY ./default.conf /etc/nginx/conf.d/default.conf

 

1-3. Nginx 설정 파일을 작성합니다. upstream 설정은 docker-compose 설정과 연관되어 있습니다. 아래의 docker-compose 설정을 참고하십시오.

upstream backend {
    server spring:8080;
}

server {
    listen 80;

    location / {
        proxy_pass http://backend/;
    }
    
    location /api {
        proxy_pass http://backend/api;
    }
}

 

2. GitHub Actions 작성

on:
  push:
    branches: main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3
        
      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: '11'
        
      - name: Grant for Gradle
        run : chmod +x gradlew
        
      - name: Build Gradle
        run: ./gradlew clean build
        
      - name: Get Current Time
        uses: josStorer/get-current-time@v2.0.0
        id: current-time
        with:
          format: YYYY-MM-DDTHH-mm-ss
          utcOffset: "+09:00"
          
      - name: Show Current Time
        run: echo "CurrentTime=${{ steps.current-time.outputs.formattedTime }}"
        
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2.0.0
      
      - name: Login dockerhub
        uses: docker/login-action@v2.0.0
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
          
      - name: Spring Boot Image Build and Push
        uses: docker/build-push-action@v3.0.0
        with:
          context: ./
          file: ./Dockerfile
          push: true
          tags:
            ${{ secrets.DOCKERHUB_USERNAME }}/spring-app:latest
      
      - name: Nginx Image Build and Push
        uses: docker/build-push-action@v3.0.0
        with:
          context: ./nginx
          file: ./nginx/Dockerfile
          push: true
          tags:
            ${{ secrets.DOCKERHUB_USERNAME }}/nginx:latest

main branch에 push가 발생하면 수행되는 job입니다.

 

JDK 설정 - Gradle 권한 설정 - 빌드 - 수행 시간 기록 - 도커 허브 로그인 - 각 이미지 빌드 후 도커 허브로 Push 순으로 수행됩니다. 이 단계가 수행되면 내 도커 허브에 이미지가 등록됩니다. 이제 docker-compose.yml을 작성하고, EB 내부에서 docker-compose를 실행해서 컨테이너를 활성화해야 합니다.

 

GItHub Actions의 스크립트들은 다소 어려워보이지만 별도의 서버를 구축할 필요가 없고 몇 가지 규칙만 인지한다면 매우 쉽고 편리합니다. 처음 접하신다면 아래의 가이드를 참고하십시오.

 

Actions를 설정할 레포지토리

1. GIthub Actions는 on 옵션을 통해 수행 시점을 설정할 수 있습니다.

2. Job 이라는 작업 단위를 가집니다.

3. 하나의 Job에는 여러 Step(단계)가 있고 순차적으로 실행합니다.

4. uses 옵션으로 마켓플레이스에 등록된 라이브러리를 사용할 수 있습니다.

5. run 옵션으로 운영체제의 명령어를 사용할 수 있습니다.

 

위의 스크립트를 번역하면 아래와 같습니다.

 

1. main branch에 push가 발생할 경우 작업(jobs)을 실행(on)한다.

2. job을 구동할 Github Actions의 서버는 ubuntu-latest 기반으로 한다.

3. Github Actions 서버 내로 checkout 한다.

4. Actions 서버 내부에 JDK 11을 Set up 한다.

5. 내부 Gradle에 권한을 부여한다.

6. 본인 레포지토리 > GIthub Actions 내부로 가져온 프로젝트를 Gralde로 빌드한다. 

 

또한, 민감 정보들은 Setting에 등록하여 외부로 노출되지 않도록 설정할 수 있습니다.

 

Secrets에 등록한 정보들은 본인이 설정한 Name을 통해 Github Actions의 스크립트에서 사용할 수 있습니다.

 

${{ secrets.DOCKERHUB_USERNAME }}/spring-app:latest의 경우 Secrets에 DOCKERHUB_USERNAME이라는 Name으로 내가 등록한 값이 사용됩니다. 단, 한 번 등록한 값들은 재확인이 불가능하고 Update 혹은 Remove만 가능합니다.

 

이제 전체적인 Github Actions 스크립트를 통해 원격에 있는 Docker Hub에 내가 만든 이미지가 저장 되었습니다. Docekr Hub에 있는 이미지를 가져 와서 실행할 수 있도록 Docker Compose를 구성하겠습니다. 

 

3. docker-compose.yml 작성

version: '3'

services:
  spring:
    image: audtncks2/spring-app //여기서 audtncks2는 본인의 docker-hub name
    ports:
      - "8080:8080"
  nginx:
    restart: always
    image: audtncks2/nginx //여기서 audtncks2는 본인의 docker-hub name
    ports:
      - "80:80"
    depends_on:
      - spring

docker-compose 파일은 외부(도커허브)에 있는 이미지(GitHub Actions에서 Push한 이미지)를 활성화하도록 설정합니다. 컨테이너 실행 순서를 명시하도록 depends_on 옵션을 추가합니다.

 

services 아래의 각각의 서비스 명이 nginx의 설정 파일에서 주소의 Alias가 되는 것을 주의해야 합니다. Spring Boot의 서비스명을 spring으로 설정하였으므로 upstream 하위의 server address를 spring으로 설정할 수 있게 됩니다.

 

이제 이 파일을 Beanstalk으로 구성된 EC2 내부에 전달해서 실행하면 되는데, Elastic Beanstalk을 생성하겠습니다. 

 

4. AWS Elastic Beanstalk 생성

Elastic Beanstalk 콘솔로 들어가서 새 환경을 생성합니다.

 

애플리케이션 이름과 환경 이름을 선택합니다.

 

플랫폼과 플랫폼 브랜치를 설정합니다. 이러면 끝입니다!

 

이제 Elastic Beanstalk가 서버를 운영하기 위한 여러 설정들을 자동으로 진행합니다. 이렇게 추상화되어 있는 부분은 사용하기에는 간편하지만 파악하기에는 다소 어려움이 있습니다. 사용자가 필요한 깊이를 정하고 딱 거기까지 파악하는 것이 중요하다고 생각하는데요, EB가 처음 구성되고 나서의 이벤트 로그를 한 번 살펴보겠습니다.

 

Auto Scailing Group, EC2, CloudWatch, Load Balancer 서비스들을 자동으로 구성해주는 것을 확인할 수 있습니다. 물론 이외에도 수많은 설정이 추가되지만, 이후에는 필요에 따라 Description을 확인하는 것이 나아 보입니다.

 

골자는, Elastic Beanstalk의 환경을 생성하기만 하면, 내부적으로 AWS의 다양한 서비스들이 자동으로 구성된다는 것입니다. 이제 생성한 beanstalk의 EC2 내부에서 작성한 docker-compose를 실행시키면 됩니다. Github Actions에 스크립트를 추가해야하는데, CI 서버에서 Beanstalk에 액세스할 수 있도록 권한을 먼저 생성하겠습니다.

 

5. IAM 생성

Identity and Access Management(IAM)을 생성해서 필요한 권한에만 액세스하도록 설정할 수 있습니다.

 

IAM 콘솔로 이동해서 사용자 추가 버튼을 클릭합니다.

 

사용자 이름과 자격 증명 유형을 선택합니다. 프로그래밍 방식 액세스를 선택합니다.

 

기존 정책 직접 연결을 선택해서 아래의 세 가지 권한을 부여해줍니다.

 

  • AdministratorAccess-AWSElasticBeanstalk
  • AmazonS3FullAccess
  • AmazonEC2FullAccess

액세스 키는 발급 시점 이후에는 확인이 불가능 하니 csv 파일을 다운로드 하여 관리해줍시다.

 

6. Github Actions steps 추가

      - name: Deploy to EB
        uses: einaregilsson/beanstalk-deploy@v20
        with:
          aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          environment_name: dnd-7th-linkApp-env
          region: ap-northeast-2
          application_name: dnd-7th-linkApp
          version_label: github-action-${{steps.current-time.outputs.formattedTime}}
          deployment_package: ./docker-compose.yml

위에서 발급 받은 Access Key ID와 Secret Access Key를 Github Setting의 Secrets에 등록하여 외부로 노출되지 않도록 합니다. 

 

기존에 작성했던 Github Actiosn에 docker-compose 파일을 AWS로 전달하는 스텝을 추가합니다. 이때, Github Actions의 Market Place에서 beanstalk-deploy를 검색해서 배포 시의 옵션을 커스터마이징 할 수 있습니다.

 

Dockerrun.aws.json 파일이 기존에는 필요했었고, 이 부분에서 다소 헤맸으나 Docker 플랫폼의 Amazon Linux 2 브랜치에서는 별도로 추가 구성 옵션을 지정하지 않을 경우 더이상 Dockerrun.aws.json 파일을 작성하지 않고 docker-compose 파일만 넘겨주면 됩니다. 

 

이제 위의 스크립트에 따라 docker-compose.yml을 S3에 업로드 하고 EB에서 새 버전이 생성된 다음 EB에서 S3 파일(docker-compose)을 가져오기 + EB의 여러 설정 작업 + docker-compose 실행이 수행됩니다. 

댓글