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

Kai·2023년 2월 2일
7

Github action에 대해서 알고 싶다면, 이 글이 속한 시리즈의 이전 글들을 참고하자.


🔥 목표

오늘은 Github actions와 AWS Codedeploy를 이용해서 CICD자동화를 해보도록 하겠다!
사용자가 특정 브랜치에 코드를 PUSH하기만 하면 자동으로 빌드하고 서버에 배포까지 하도록 하는 것이다.

🌠 개발환경

프로젝트: Spring boot 
빌드툴: Gradle
JDK: openjdk 17

깃헙 레포지토리


💻 개요


Part.1 환경 세팅

  • IAM 세팅: 계정 생성, Github과 연동, 역할 생성
  • 배포할 서버 세팅 (AWS EC2)
  • S3 세팅: 빌드 결과물 저장
  • Code deploy 세팅

Part.2 코드 작성

  • Github actions 워크플로우 생성
  • appspec.yml 작성
  • 실행, 정지 script 작성

Part.3 배포 🔥



Part 1. 환경 세팅

⭐ IAM 세팅


먼저 AWS에서 CICD를 전담할(?) IAM 계정과 필요한 역할을 생성해보겠다.

1) IAM 계정 생성

지금 생성할 IAM 계정은 CICD관련 작업에 사용할 계정이다.
먼저 IAM 콘솔에 접속한 후, 사용자를 생성해보자.

원하는대로 이름을 지어주고, 다음으로 넘어가자.

그러면 생성할 계정에 부여할 권한을 선택해야하는데, 아래의 2가지를 검색한 후 선택해주자

  • AmazonS3FullAccess
  • AWSCodeDeployFullAccess

그리고 다시 콘솔로 돌아오면, 아래와 같이 사용자가 정상적으로 생성된 것을 확인할 수 있다.


2) 키페어 발급

이제 Github에 방금 생성한 계정의 Keypair를 입력해줄 것이다.
그러니 Keypair부터 발급받아 보자.

생성한 계정의 콘솔 페이지에서 엑세스 키를 생성할 수 있는 탭으로 이동하고, "엑세스 키 만들기"버튼을 클릭한다.

그러면 뭔가를 고르는 화면이 나오는데 나는 '기타'를 선택해줬다 ㅎㅎ

그리고 태그값을 본인이 구분하기 쉽게 입력하고, 키페어를 생성하게 되면, Access key와 Secret key를 발급받게 된다.
Secret key같은 경우는 한번만 볼 수 있고, 다시 볼 수 없게 되니까 꼭! 발급받은 키페어를 안전한 곳 어딘가에 메모해두자.


3) Github에 키페어 입력하기

배포할 코드가 있는 깃헙 레포지토리로 이동해서 발급받은 Keypair를 입력해주자.
입력할 때, 각 값의 이름은 아무렇게나 지어도 된다.

이렇게 하면 이제 Github에서 나의 S3와 Code deploy에 접근할 수 있게 된다. 👍


4) IAM 역할 생성

이따가 필요하게 될 IAM 역할 2가지를 미리 생성해놓도록 하자.
IAM > 역할 콘솔에 들어가면, 역할을 생성할 수 있다.

4-1) EC2에서 S3 접근을 위한 IAM 역할

이 순서대로 생성하면 된다.
이 역할은 이따가 생성할 EC2에게 부여해줄 것이다.

역할의 이름은 자유롭게 만들어주면 된다.
작명 팁은 AWS에서 기본적으로 제공되는 수 많은 역할들과는 조금 다른 느낌(?)으로 이름을 짓는 것을 권장한다. 🤭
왜냐하면 기본 역할과 내가 만든 역할을 쉽게 구분하기 위해서 그렇다. 👍

4-2) Code deploy를 위한 IAM 역할

Code deploy를 위한 역할은 처음에 역할을 생성할 때, Code deploy를 검색해서 선택해주고 이전에 했던 것과 동일한 절차를 거쳐주면 된다.


⭐ EC2 세팅


1) 인스턴스 생성

EC2 > 인스턴스 콘솔로 이동한 후, 새로운 인스턴스를 하나 만들어주자.

내가 쓰기 편한 OS를 선택하면 되는데, 나는 ubuntu로 설정을 하도록 하겠다.
그리고 ubuntu의 버전은 20.04 버전을 하겠다.

22.04 버전의 경우 Code deploy agent를 설치하는데에 있어서 뭔가 매끄럽지가 못해서, 설치 매뉴얼이 존재하는 20.04버전을 선택했다.


네트워크 설정

네트워크 설정은 EC2를 위치시킬 VPC와 서브넷을 선택해주면 된다.

키페어

키페어는 필요하다면, 새롭게 발급 받아서 연결해주면 된다.
나는 이 서버만을 위한 키페어를 새롭게 발급받았다.

보안그룹

보안그룹은 이렇게 미리 만들어두고, 연결해주자.
나는 8080포트로 스프링을 돌릴거여서 8080포트도 열어주었다.


2) EIP 생성, 연결

탄력적 IP를 하나 생성해서, EC2에 연결해주자.
EIP생성, 연결은 간단해서 길게 이야기하지는 않겠다.


3) CodeDeploy agent 설치

이제 EC2 서버에 CodeDeploy Agent를 설치해줄 차례이다.
Ubuntu를 위한 agent 설치 매뉴얼을 AWS에서 제공하고 있는데, 이 매뉴얼을 참고해서 서버에 Agent프로그램을 설치해주자.

Ubnuntu 20.04버전으로 EC2를 만들었다면, 서버에 접속한 수 아래의 명령어를 순차적으로 실행해주면 된다.

3-1) 설치

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

3-2) 실행 확인

sudo service codedeploy-agent status

정상적으로 설치가 됐다면 이렇게, 서비스가 잘 돌아가고 있다고 메세지가 출력될 것이다.


4) Java 설치

아래 명령어들로 Java 17버전을 설치하자.

sudo apt update
sudo apt install openjdk-17-jdk

5) IAM 역할 연결

아까 생성한 S3에 접근하기 위한 IAM 역할을 연결해주자.


⭐ S3 세팅


Github action의 워크플로우가 수행되면, 코드를 압축해서 S3에 업로드할 것이다.

버킷은 이름만 적당히 지어줄 것이다.
그리고 퍼블릭 엑세스를 허용할 것은 없으므로 나머지 설정은 수정할 것 없이 그대로 두면 된다.


⭐ Code deploy 세팅


1) 애플리케이션 생성

Code deploy 콘솔로 이동한 후 애플리케이션을 뚝딱 만들어주자.


2) 배포 그룹 생성

배포 그룹을 반드시 만들어줘야해서, 생성 버튼을 누르고 쭉 진행하자.

설정은 이렇게 해주면 된다.
중요한 부분은 아까 생성했던 Code deploy를 위한 IAM 역할을 맵핑해주는 것과 태그로 조회해서 EC2를 연결해주는 부분이다.
(참고로 이 설정들은 나중에 수정이 가능하다.)


자! 이렇게 모든 환경 세팅이 완료됐다! 🤭
이제 워크플로우와 필요한 스크립트 파일을 작성해보도록 하겠다.



Part 2. 코드 작성

⚡ GitHub Actions 워크플로우 작성


혹시나 Github Actions이나 워크플로우에 대해서 더 알고 싶다면 이 글이 속한 시리즈의 이전 글들을 참고하자.

name: CICD Test
run-name: Running
on:
  push:
    branches:
      - production
      - 'releases/**'

env:
  AWS_REGION: ap-northeast-2
  AWS_S3_BUCKET: app-release-files
  AWS_CODE_DEPLOY_APPLICATION: cicid-test-CD
  AWS_CODE_DEPLOY_GROUP: cicd-test-CD-group

jobs:
  build-with-gradle:
    runs-on: ubuntu-20.04
    steps:
    - name: production 브랜치로 이동
      uses: actions/checkout@v3
      with:
        ref: production
    - name: JDK 17 설치
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'corretto'
    - name: gradlew에 실행 권한 부여
      run: chmod +x ./gradlew
    - name: 프로젝트 빌드
      run: ./gradlew clean build -x test
    - name: AWS credential 설정
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-region: ${{ env.AWS_REGION }}
        aws-access-key-id: ${{ secrets.CICD_ACCESS_KEY }}
        aws-secret-access-key: ${{ secrets.CICD_SECRET_KEY }}
    - name: S3에 업로드
      run: aws deploy push --application-name ${{ env.AWS_CODE_DEPLOY_APPLICATION }} --ignore-hidden-files --s3-location s3://$AWS_S3_BUCKET/cicdtest/$GITHUB_SHA.zip --source .
    - name: EC2에 배포
      run: aws deploy create-deployment --application-name ${{ env.AWS_CODE_DEPLOY_APPLICATION }} --deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name ${{ env.AWS_CODE_DEPLOY_GROUP }} --s3-location bucket=$AWS_S3_BUCKET,key=cicdtest/$GITHUB_SHA.zip,bundleType=zip

나는 요로코롬 작성해주었고, 각 스텝들의 역할은 name으로 작성해두었으니 참고하자.


⚡ appspec.yml 작성


그리고 프로젝트의 루트 경로에 appspec.yml도 작성해주어야한다.
이 파일은 Code deploy가 수행할 일들을 작성하는 곳이다.

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

여기서 중요한 부분은 hooks이다.

Code deploy의 작업은 이런 Lifecycle을 갖고 있는데, 필요한 부분에 hooks를 작성해주면 된다.

hooks에 작성할 요소들은 이런 것들이 있다.


⚡ 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

이 스크립트는 실행되고 있는 Spring 서버를 종료하는 역할을 한다.


⚡ 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


Part 3. 배포

🔥 실행!


자 드디어 실행할 시간이다 ㅎㅎ
production브랜치에 코드 push를 발생시키고, 잘 진행되는 지 확인해보자.

1) 워크플로우 확인

음 ~~ 워크플로우가 쌈@뽕하게 완료됐다.

2) S3 확인

S3에도 압축된 코드 파일이 잘 올라와 있다.
이제 EC2에서 빌드만 잘 되면 된다!!

3) Code deploy 확인

오! 맙소사...
첫 배포가 실패했다... 원인을 파악해보자 🤮


3-1) 이슈1: 자격증명 에러

먼저 찾아보니 여기에서 Code deploy의 로그를 보는 방법을 안내해주고 있었다.
방법대로 찾아보니...

ERROR [codedeploy-agent(3395)]: InstanceAgent::Plugins::CodeDeployPlugin::CommandPoller: Missing credentials - please check if this instance was started with an IAM instance profile

이런 에러 로그가 찍혀있는 것을 확인할 수 있었다.

공식문서를 확인해보니 자격 증명하는 부분에 문제가 있는 것을 확인했다.

리서치를 해보니, EC2에 IAM을 적용한 게 Code deploy agent입장에서 제대로 반영이 안됐을 수도 있다고 한다.
그렇다면, EC2를 재부팅해주자 🤭


3-2) 이슈2: Permission denied 에러

그리고 다시 실행해봤는데, 이번에는 script를 실행하는 부분에서 Permission denied에러가 발생했다.

찾아보니 code deploy agent의 사용자, 그룹명이 appspec.yml에 작성한 것과 달라서 발생한 문제였다.
문제를 해결하는 방법은 AWS 공식문서에서 알려주고 있다.

아래의 명령어들을 실행하면 된다.

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/

Agent 재실행, 상태 확인

sudo service codedeploy-agent start
sudo service codedeploy-agent status

사실 이 외에도 수 많은 난관이 있었는데... 다 나의 실수 때문이여서 글에 작성하지는 않았다 🤭🤮


4) 이제 진짜 확인 🔥

위 이슈들을 모두 해결하고 다시 확인해보니 드.디.어!
정상적으로 배포된 것을 확인할 수 있었다!!! ㅎㅎㅎ 👍

4-1) Deploy 성공

4-2) 목표한 jar파일 생성

4-3) 서비스 구동 확인

ps -ef | grep java

4-4) 접속 확인


☕ 마무리


드디어! CI/CD 자동화를 위한 대장정이 마무리 되었다. 휴
역시나 쉽지 않은 작업이였지만, 앞으로의 개발 생산성은 엄~청 올라갈 것 같다.
먼저 비슷한 작업들을 했던 선배림들이 있어서 가능했던 것 같다 ㅎㅎㅎ
모두 감사합니다 🙏


참고 🙏

7개의 댓글

comment-user-thumbnail
2023년 8월 15일

github actions 처음 사용해보는데 덕분에 정말 많은 도움되었습니다! 소중하고 유익한 글 감사해요:)

1개의 답글
comment-user-thumbnail
2023년 12월 8일

안녕하세요 올려주신 글을 보고 ec2 배포 자동화를 따라하고 있는 개발자 입니다. 혹시 aws 버켓을 생성하고 버켓안에 반드시 저장해야하는것인가요?

1개의 답글
comment-user-thumbnail
2024년 3월 24일

많은 도움이 되었습니다 감사합니다

1개의 답글