GitHub Actions 이용하여 Spring Boot 프로젝트 CI/CD 구축

joona95·2023년 1월 2일
0
post-thumbnail

0. 배경

프로젝트 진행하면서 스프링부트 서버 구축을 AWS EC2 를 활용하여 진행했다. 매번 코드 업데이트할 때마다 AWS EC2 접속해서 빌드하고 실행하는 게 너무 귀찮았다. Github에 코드 올리면 자동으로 배포까지 됐으면 좋겠다고 생각하다가 Github Actions를 활용하여 CI/CD 구축을 해보기로 결심했다.

1. Github에 Secret 파일 등록

  • Github 공개 레파지토리를 이용하다보니 Secret key, DB접속 정보 등 .gitignore 파일에 적어 Github 에는 올리지 않는 Secret 파일들이 존재

  • 변수 몇 개만 필요한 게 아니라 여러 파일들이 필요한 거였기 때문에 Github Secrets에 base64 인코딩한 파일 내용을 저장한 후 워크플로에서 디코딩하는 방법 이용

    • 방법1) base64 인코딩/디코딩 사이트 에서 텍스트 붙여넣어서 base64 인코딩

    • 방법2) ssl을 위한 keystore.p12 는 사이트 이용 없이 openssl 사용하여 base64 인코딩

      openssl base64 -in [키스토어 파일명] -out [저장될텍스트파일명]

2. workflow 작성

기본 제공되는 workflow 파일 이용

  • Github 레파지토리에서 Actions > Java with Gradle configure 버튼 클릭하면 기본적인 yml 파일 제공

gradle 빌드 관련 내용 수정

	# 빌드 파일 권한 수정
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    
    # gradle 빌드
    - name: Build with Gradle 
      run: ./gradlew clean build

Secret 파일 디코딩 후 파일 복사 내용 추가

 	# application.yml 파일 복사
    - name: Copy application.yml
      env:
        APPLICATION_YML_FILE: ${{ secrets.APPLICATION_YML }}
      run: echo $APPLICATION_YML_FILE | base64 --decode > src/main/resources/application.yml
      
    # recipeapp-key.json 파일 복사
    - name: Copy recipeapp-key.json
      env:
        RECIPEAPP_KEY_FILE: ${{ secrets.RECIPEAPP_KEY }}
      run: echo $RECIPEAPP_KEY_FILE | base64 --decode > src/main/resources/recipeapp-key.json

    # Secret.java 파일 복사
    - name: Copy Secret.java
      env:
        SECRET_JAVA_FILE: ${{ secrets.SECRET_JAVA }}
      run: echo $SECRET_JAVA_FILE | base64 --decode > src/main/java/com/recipe/app/config/secret/Secret.java
      
    # keystore.p12 파일 복사
    - name: Copy keystore.p12
      env:
        KEYSTORE_FILE: ${{ secrets.KEYSTORE }}
      run: echo $KEYSTORE_FILE | base64 --decode > keystore.p12

오류1 - secret 폴더 생성 안해서 파일 복사 시 오류 발생

  • src/main/java/com/recipe/app/config 폴더 내에 /secret 디렉토리가 없어서 해당 폴더에 파일 복사 실패

  • /secret 폴더 생성 하는 스텝 추가하여 해결

        # secret 파일 디렉토리 생성
        - name: Create secret directory
          run: mkdir src/main/java/com/recipe/app/config/secret

오류2 - keystore.p12 파일 디코딩 오류 발생

  • openssl 로 직접 인코딩한 keystore.p12 파일은 -i 옵션을 추가해줘서 해결

Reason is that base64 produces output with line breaks, which are garbage chars for the decoder.
Solution is to either produce the base64 without linebreaks (-w 0) or make the decoder ignore garbage chars (-i).

3. EC2 설정 추가

태그 추가

  • CodeDeploy 에서 어떤 인스턴스 수행할지 구분하기 위해 태그 사용

  • EC2 인스턴스 > 작업 > 인스턴스 설정 > 태그 관리 > 새로운 태그 추가

IAM 역할 추가

  • IAM 액세스 관리에서 역할 관리 페이지로 이동하여 새로운 역할 만들기

  • IAM > 액세스 관리 > 역할 > 역할 만들기

  • AWS 서비스 와 EC2 선택 후 AmazonS3FullAccess 권한 추가

  • 역할 이름 지정 후 역할 생성 완료

  • EC2 인스턴스에서 IAM 연결

  • EC2 인스턴스 > 작업 > 보안 > IAM 역할 수정 > IAM 역할 업데이트

CodeDeploy Agent 설치

$ sudo yum update
$ sudo yum install ruby
$ sudo yum install wget
$ cd /home/ec2-user
$ wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
$ chmod +x ./install
$ sudo ./install auto
$ sudo service codedeploy-agent status
  • 설치가 완료된 경우 The AWS CodeDeploy agent is running 이라고 떠야함

4. AWS S3 버킷 생성

S3 메뉴에서 버킷 생성

  • Amazon S3 > 버킷 > 버킷 만들기

5. CodeDeploy 생성

  • CodeDeploy는 Amazon EC2 인스턴스, 온프레미스 인스턴스, 서버리스 Lambda 함수 또는 Amazon ECS 서비스로 애플리케이션 배포를 자동화하는 배포 서비스

CodeDeploy 전용 IAM 역할 만들기

  • IAM > 액세스 관리 > 역할 > 역할 만들기

  • AWS 서비스와 CodeDeploy 선택 후 역할 이름 지정하고 생성

CodeDeploy 애플리케이션 생성

  • CodeDeploy > 배포 > 애플리케이션 > 애플리케이션 생성

CodeDeploy 배포 그룹 생성

  • CodeDeploy > 배포 > 애플리케이션 > 해당 애플리케이션의 배포 그룹 > 배포 그룹 생성

  • Amazon EC2 인스턴스에 추가해둔 태그 키 값 입력

6. Github Actions 에서 사용할 IAM 사용자 추가

IAM 사용자 메뉴에서 사용자 추가

  • Github Actions 에서 CodeDeploy 와 S3 접근 가능하도록 처리 필요

  • IAM > 액세스 관리 > 사용자 > 사용자 추가

  • Github 는 액세스 키로 접근 가능하도록 처리

(+) 2023.07.22 기준 변경

  • 접근 권한 추가
    • AWSCodeDeployFullAccess
    • AmazonS3FullAccess

  • 사용자 추가 후 Access Key 와 Secret Key 얻음

(+) 2023.07.22 기준 변경

  • 생성 후 생성된 콘솔 암호 키 값 저장 (나중에는 확인 불가)

  • IAM > 사용자 > 사용자 선택 > 보안 자격 증명 > 액세스 키 만들기

Gihub 레파지토리에 Secrets에 Access Key 와 Secret Key 추가

7. AppSpec 파일 작성

  • CodeDeploy에 고유한 애플리케이션 사양 파일로 YAML 형식 또는 JSON 형식 파일

  • 프로젝트의 어떤 파일들을 EC2의 어떤 경로에 복사할지 설정 가능하고 배포 프로세스 이후에 수행할 스크립트를 지정하여 자동으로 서버 띄울 수 있음

  • 기본적으로 루트 디렉터리에 위치해야 함

version: 0.0
os: linux

files:
  - source:  /
    destination: /home/ec2-user/app
    overwrite: yes

permissions:
  - object: /
    pattern: "**"
    owner: ec2-user
    group: ec2-user

hooks:
  AfterInstall:
    - location: stop.sh
      timeout: 60
      runas: root
  ApplicationStart:
    - location: start.sh
      timeout: 60
      runas: root

오류1 - 참고한 EC2 서버 종류가 달라서 해당 폴더 존재하지 않는 오류

  • 참고한 appspec.yml 에서는 ubuntu 서버 기준이라 destination: /home/ubuntu/app 이어서 해당 폴더 찾을 수 없다는 오류 발생

  • EC2가 ubuntu 서버가 아니라 Amazon Linux 이므로 destination: /home/ec2-user/app로 수정해서 해결

오류2 - 쉘스크립트 실행 시 log파일 생성하면서 권한 오류 발생

  • 쉘스크립트 실행 시에 sudo 권한이 필요했음

  • runas: ec2-user 로 지정했던 부분을 root 권한으로 실행될 수 있도록 runas: root 로 변경해서 해결

8. 배포 스크립트 작성

  • PROJECT_ROOT 를 현재 사용중인 EC2에 맞게 PROJECT_ROOT="/home/ec2-user/app"로 수정

  • JAR_FILE의 jar파일명을 맞게 수정

stop.sh

  • 애플리케이션 작동중인 경우 종료하는 스크립트
#!/usr/bin/env bash

PROJECT_ROOT="/home/ec2-user/app"
JAR_FILE="$PROJECT_ROOT/app-0.0.1-SNAPSHOT.jar"

DEPLOY_LOG="$PROJECT_ROOT/deploy.log"

TIME_NOW=$(date +%c)

# 현재 구동 중인 애플리케이션 pid 확인
CURRENT_PID=$(pgrep -f $JAR_FILE)

# 프로세스가 켜져 있으면 종료
if [ -z $CURRENT_PID ]; then
  echo "$TIME_NOW > 현재 실행중인 애플리케이션이 없습니다" >> $DEPLOY_LOG
else
  echo "$TIME_NOW > 실행중인 $CURRENT_PID 애플리케이션 종료 " >> $DEPLOY_LOG
  kill -9 $CURRENT_PID
fi

start.sh

  • 애플리케이션을 실행하는 스크립트

  • SSL 처리 때문에 필요한 keystore.p12 가 루트 위치에 존재하지 않아서 오류 발생

  • EC2 서버에 복사된 keystore.p12 파일을 루트 위치로 이동시키기 위해서 cp $PROJECT_ROOT/keystore.p12 /keystore.p12 내용 추가

#!/usr/bin/env bash

PROJECT_ROOT="/home/ec2-user/app"
JAR_FILE="$PROJECT_ROOT/app-0.0.1-SNAPSHOT.jar"

APP_LOG="$PROJECT_ROOT/application.log"
ERROR_LOG="$PROJECT_ROOT/error.log"
DEPLOY_LOG="$PROJECT_ROOT/deploy.log"

TIME_NOW=$(date +%c)

# build 파일 복사
echo "$TIME_NOW > $JAR_FILE 파일 복사" >> $DEPLOY_LOG
cp $PROJECT_ROOT/deploy/*.jar $JAR_FILE
cp $PROJECT_ROOT/keystore.p12 /keystore.p12

# 실행권한 추가
chmod +x $JAR_FILE

# jar 파일 실행
echo "$TIME_NOW > $JAR_FILE 파일 실행" >> $DEPLOY_LOG
nohup java -jar $JAR_FILE > $APP_LOG 2> $ERROR_LOG &

CURRENT_PID=$(pgrep -f $JAR_FILE)
echo "$TIME_NOW > 실행된 프로세스 아이디 $CURRENT_PID 입니다." >> $DEPLOY_LOG

9. Github Actions Workflow 수정

  	# 전송할 파일을 담을 디렉토리 생성
    - name: Make Directory for deliver
      run: mkdir deploy

    # Jar 파일 Copy
    - name: Copy Jar
      run: cp ./build/libs/*.jar ./deploy/

    # appspec.yml Copy
    - name: Copy appspec
      run: cp appspec.yml ./deploy/

    # 쉘스크립트 deploy 폴더로 복사
    - name: Copy shell
      run: |
        cp ./scripts/start.sh ./deploy/start.sh
        cp ./scripts/stop.sh ./deploy/stop.sh

    # keystore.p12 deploy 폴더로 복사
    - name: Copy keystore
      run: |
        cp ./keystore.p12 ./deploy/keystore.p12

    # 압축파일 형태로 전달
    - name: Make zip file
      run: zip -r -qq -j ./recipe-storage-build.zip ./deploy

    # 압축한 파일 S3 Bucket으로 업로드
    - name: Deliver to AWS S3
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      run: |
        aws s3 cp \
          --region ap-northeast-2 \
          --acl private \
          ./recipe-storage-build.zip s3://recipe-storage-github-actions-s3-bucket/

    # EC2 서버에 deploy
    - name: Deploy
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      run: |
        aws deploy create-deployment \
          --application-name recipe-storage-codedeploy-app \
          --deployment-group-name recipe-storage-codedeploy-deployment-app \
          --file-exists-behavior OVERWRITE \
          --s3-location bucket=recipe-storage-github-actions-s3-bucket,bundleType=zip,key=recipe-storage-build.zip \
          --region ap-northeast-2
  • jar 파일 실행 시에 keystore.p12 파일도 필요해서 deploy 폴더로 복사되도록 내용 추가

  • 압축파일명, S3 bucket 이름, codedeploy 애플리케이션명, codedeploy 배포 그룹명 맞춰서 수정

    • 압축파일명: recipe-storage-build.zip
    • S3 Bucket 이름: recipe-troage-github-actions-s3-bucket
    • CodeDeploy 애플리케이션 이름: recipe-storage-codedeploy-app
    • CodeDeploy 배포 그룹 이름: recipe-storage-codedeploy-deployment-app

10. 배포 성공 확인

  • Github Actions 성공 확인

  • CodeDeploy 성공 확인

  • app 실행 확인

참고 사이트

0개의 댓글