개요

백엔드 개발 시 main 브랜치 push 에 따라 자동 배포를 통해 개발 서버를 유지하고자 한다. 이를 위해 Github action 와 AWS 의 S3, CodeDeploy, EC2 를 사용해서 배포한다. 서버는 Spring Boot 를 사용하였다!

image

전체적인 구조는 위와 같다.

  1. Github 에 push 시에 Github Action 이 동작한다.
  2. Github Action 을 통해 S3 에 코드가 저장되며 CodeDeploy 가 실행된다.
  3. AWS CodeDeploy 는 S3 에 존재하는 코드를 EC2 에 복사하고 실행시킨다.

해당 프로세스를 통해 Github push 를 통해 개발 서버를 배포할 수 있다!

1. AWS 세팅

IAM

1. IAM 유저 생성

image

가장 먼저 IAM 유저를 생성해야한다.

image

User 를 생성하면 user Group 을 생성하라는 메시지를 볼 수 있다. 여기서 Group 을 생성하고 두 가지 권한을 설정하자.

  • AmazonS3FullAccess
  • AWSCodeDeployFullAccess

image

Group 이 생성되었으면 생성하던 User 를 해당 Group 에 포함시킨다.

image

생성 후의 모습은 위와 같다. User 는 두 가지 권한을 가지고 있어야한다.

2. IAM Role 생성

Role 을 생성해주어야한다.

image

EC2 에서 S3 에 접근할 수 있는 Role

image

CodeDeploy 에서 S3 에 접근할 수 있는 Role

image

Role 생성 결과는 위와 같다.

3. Access Key 생성

image

IAM 을 접근할 수 있는 Key Pair 를 생성한다.

image

생성한 Key 는 이후 사용해야하므로 잘 저장해 놓도록 하자.

EC2

1. 인스턴스 생성

image

image

개발 서버를 배포할 EC2 인스턴스를 생성한다. Spring boot 를 생성하기에 인바운드에 8080 포트를 열어놓도록 하자.

2. EIP 설정

image

Elastic IP Address 또한 설정해 유지하도록 하자.

3. CodeDeploy Agent 설치

이후 해당 EC2 서버에 접속해 CodeDeploy Agent 를 설치해야한다.

sudo apt update
sudo apt install ruby-full
sudo apt install wget
cd /home/ubuntu
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto > /tmp/logfile

Agent 의 소유자도 미리 변경해준다.

sudo service codedeploy-agent stop
sudo sed -i 's/""/"ubuntu"/g' /etc/init.d/codedeploy-agent
sudo systemctl daemon-reload
sudo chown ubuntu:ubuntu -R /opt/codedeploy-agent/
sudo chown ubuntu:ubuntu -R /var/log/aws/

image

다음의 커맨드를 통해 잘 설치 되었는 지 확인할 수 있다.

sudo service codedeploy-agent status

image

Spring 실행을 위한 jdk 도 함께 설치해주도록 하자.

sudo apt update
sudo apt install openjdk-17-jdk

4. EC2 에 IAM Role 설정

image

EC2 가 S3 에 접근해서 코드를 가져올 수 있도록 미리 만들어놓은 Role 을 세팅해주어야한다.

image

S3

1. Bucket 생성

image

이제 코드를 저장할 S3 를 생성한다. 내부적으로 접근할 예정이므로 Public Access 는 닫아놔도 된다.

CodeDeploy

1. CodeDeploy 생성

image

CodeDeploy 를 생성한다.

2. EC2 와 연결

image image image

이후 CodeDeploy 와 이전에 생성한 EC2 를 연결해주는 작업을 해야한다.

2. 코드 작성

Github Action

1. Github Action Workflows

image

Github Action 을 위한 workflows 의 기본 형태는 자동으로 생성할 수 있다.

Java With Gradle 을 사용하였다.

name: Java CI with Gradle

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

env:
  AWS_REGION: ap-northeast-2
  AWS_S3_BUCKET: cicd-bucket-name
  AWS_CODE_DEPLOY_APPLICATION: cicd-CodeDeploy
  AWS_CODE_DEPLOY_GROUP: cicd-CodeDeploy-group

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: 'corretto'

    - name: gradlew permmision
      run: chmod +x ./gradlew

    - name: Setup Gradle
      uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0

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

    - name: AWS credential
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-region: $
        aws-access-key-id: $
        aws-secret-access-key: $

    - name: S3 upload
      run: aws deploy push --application-name $ --ignore-hidden-files --s3-location s3://$AWS_S3_BUCKET/cicdtest/$GITHUB_SHA.zip --source .
    
    - name: CodeDeploy
      run: aws deploy create-deployment --application-name $ --deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name $ --s3-location bucket=$AWS_S3_BUCKET,key=cicdtest/$GITHUB_SHA.zip,bundleType=zip

  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: 'corretto'

    - name: Generate and submit dependency graph
      uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0

build 후 S3 에 업로드 하고 CodeDeploy 를 실행시키는 과정이다.

2. Repository Secrets

image

AWS credential 을 위한 IAM Key Pair 가 여기서 사용된다. Repository Secrets 을 사용한다. 여기서 application.property 도 작성하면 여러 설정들을 세팅해놓을 수 있다.

3. Appspec.yml

CodeDeploy 가 실행시킬 appspec.yml 을 루트 디렉토리에 작성해주어야한다.

version: 0.0
os: linux

files:
  - source:  /
    destination: /home/ubuntu/spring-github-action
    overwrite: yes

permissions:
  - object: /
    owner: ubuntu
    group: ubuntu

hooks:
  AfterInstall:
    - location: scripts/stop.sh
      timeout: 60
  ApplicationStart:
    - location: scripts/start.sh
      timeout: 60

이름에서 유추할 수 있듯이 EC2 에 존재하는 파일에서 stop.sh 이후 start.sh 를 실행시키는 작업을 한다.

4. Shell

프로젝트의 루트 디렉토리에 shell 파일을 작성한다.

stop.sh

#!/bin/bash

ROOT_PATH="/home/ubuntu/spring-github-action"
JAR="$ROOT_PATH/application.jar"
STOP_LOG="$ROOT_PATH/stop.log"
SERVICE_PID=$(pgrep -f $JAR) # 실행중인 Spring 서버의 PID

if [ -z "$SERVICE_PID" ]; then
  echo "서비스 NouFound" >> $STOP_LOG
else
  echo "서비스 종료 " >> $STOP_LOG
  kill "$SERVICE_PID"
  # kill -9 $SERVICE_PID # 강제 종료를 하고 싶다면 이 명령어 사용
fi

start.sh

#!/bin/bash

ROOT_PATH="/home/ubuntu/spring-github-action"
JAR="$ROOT_PATH/application.jar"

APP_LOG="$ROOT_PATH/application.log"
ERROR_LOG="$ROOT_PATH/error.log"
START_LOG="$ROOT_PATH/start.log"

NOW=$(date +%c)

echo "[$NOW] $JAR 복사" >> $START_LOG
cp $ROOT_PATH/build/libs/spring-github-action-1.0.0.jar $JAR

echo "[$NOW] > $JAR 실행" >> $START_LOG
nohup java -jar $JAR > $APP_LOG 2> $ERROR_LOG &

SERVICE_PID=$(pgrep -f $JAR)
echo "[$NOW] > 서비스 PID: $SERVICE_PID" >> $START_LOG

결과

Github 메인 브랜치에 push 를 해보자. 여러 트러블 슈팅 과정 후 정상적으로 동작하는 것을 확인할 수 있었다. 실제 EIP 에서도 접속 가능한 것을 확인했다.

image

image

image

실제 변경된 내용이 잘 배포되는 지 마지막으로 확인해보자.

image

image

간단하게 메세지만 바꾸었을 때 정상적으로 잘 동작하는 것을 확인할 수 있었다.

Github Code

참고

실전! Github actions, AWS Code deploy로 Spring boot 배포 자동화하기

업데이트:

댓글남기기