프로젝트/나날이

나날이 백엔드 CI-CD 구축의 과정

chanhee01 2023. 11. 20. 22:17

이전 2번의 프로젝트 Trend_Pick, 피료해에서는 수동으로 배포를 했다. ec2 서버에 연결한 다음에 로컬에서 JAR 파일을 보낸 뒤 직접 자바 파일을 실행시켰다.

 

배포를 한 후에도 계속해서 변경사항이 생겼고, 생길 때마다 ec2 서버에 JAR 파일을 보내고 기존의 실행되는 애플리케이션을 kill 한 다음에 다시 자바를 실행시키는 일이 매우 번거로웠다.

 

계속 CI-CD를 구축해봐야지라고 생각만 하고 다른거 하기 바빠서 안했었는데 이번에는 시간을 들여서 제대로 해봤다.

(물론 계속 안돼서 10시간은 넘게 썼다. 구글링해도 다 달라서 잘 안되더라......)

 

 

 

scripts/deploy.sh

#!/usr/bin/env bash

REPOSITORY=/home/ubuntu/nanali
cd $REPOSITORY

APP_NAME=Nanali
JAR_NAME=$(ls $REPOSITORY/build/libs/ | grep 'SNAPSHOT.jar' | tail -n 1)
JAR_PATH=$REPOSITORY/build/libs/$JAR_NAME

CURRENT_PID=$(pgrep -f $APP_NAME)

if [ -z $CURRENT_PID ]
then
  echo "> 종료할것 없음."
else
  echo "> kill -9 $CURRENT_PID"
  kill -15 $CURRENT_PID
  sleep 5
fi

echo "> $JAR_PATH 배포"
nohup java -jar $JAR_PATH > /dev/null 2> /dev/null < /dev/null &

 

여기서는 ec2에 올라간 자바 파일을 자동으로 재시작해주는 역할을 한다. 여기서 중요한 점은 경로를 제대로 입력 해야한다. 경로를 제대로 입력 안하고 /home/ec2-user 경로로 해가지고 한참 시간을 날린 기억이 있다. 꼭 제대로 된 경로를 입력해서 애플리케이션이 잘 실행되도록 설정하자!

 

 

 

appspec.yml

version: 0.0
os: linux

files:
  - source: /
    destination: /home/ubuntu/nanali

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

hooks:
  AfterInstall:
    - location: scripts/deploy.sh
      timeout: 60
      runas: ubuntu

여기서도 파일이 어디로 이동하는지 설정하는건데, 역시 경로도 잘 설정해야하고.. 꼼꼼히 해야겠다는 생각이 많이 들었다.

 

 

 

이제 깃허브 workflows에서 설정을 해야한다.

 

deploy.yml

name: Build and Deploy Spring Boot to AWS EC2

on:
  push:
    branches: [main]

env:
  PROJECT_NAME: Nanali
  BUCKET_NAME: nanali
  CODE_DEPLOY_APP_NAME: nanali
  DEPLOYMENT_GROUP_NAME: nanali_deploy

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup Java JDK 17
        uses: actions/setup-java@v1
        with:
          java-version: 17

      - uses: actions/checkout@v2
      - run: touch ./src/main/resources/application.properties
      - run: echo "${{ secrets.APPLICATION }}" > ./src/main/resources/application.properties

      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
        shell: bash

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

      - name: Modify application.properties
        run: |
           sed -i 's~spring.datasource.url =.*~spring.datasource.url = '"${{ secrets.DB_URL }}"'~' ./src/main/resources/application.properties
           sed -i 's~spring.datasource.username =.*~spring.datasource.username = '"${{ secrets.DB_username }}"'~' ./src/main/resources/application.properties
           sed -i 's~spring.datasource.password =.*~spring.datasource.password = '"${{ secrets.DB_password }}"'~' ./src/main/resources/application.properties
           sed -i 's~cloud.aws.credentials.access-key =.*~cloud.aws.credentials.access-key = '"${{ secrets.AWS_ACCESS_KEY_ID }}"'~' ./src/main/resources/application.properties
           sed -i 's~cloud.aws.credentials.secret-key =.*~cloud.aws.credentials.secret-key = '"${{ secrets.AWS_SECRET_ACCESS_KEY }}"'~' ./src/main/resources/application.properties
     
      - name: Make Zip File
        run: zip -qq -r ./$GITHUB_SHA.zip .
        shell: bash

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-2

      - name: Upload to S3
        run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$BUCKET_NAME/$PROJECT_NAME/$GITHUB_SHA.zip

      - name: Code Deploy
        run: aws deploy create-deployment --application-name $CODE_DEPLOY_APP_NAME --deployment-config-name CodeDeployDefault.OneAtATime --deployment-group-name $DEPLOYMENT_GROUP_NAME --s3-location bucket=$BUCKET_NAME,bundleType=zip,key=$PROJECT_NAME/$GITHUB_SHA.zip

이 포스팅의 하이라이트이다. 가장 많은 시간을 들인 것이 '환경변수 설정' 이다.

 

 

application.properties

server.port=8080

spring.datasource.url = ${DB_URL}
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.username = ${DB_username}
spring.datasource.password = ${DB_password}

spring.jpa.show-sql = true
spring.jpa.properties.hibernate.format_sql = true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
spring.jpa.hibernate.ddl-auto = update

cloud.aws.credentials.access-key = ${AWS_ACCESS_KEY_ID}
cloud.aws.credentials.secret-key = ${AWS_SECRET_ACCESS_KEY}

cloud.aws.s3.bucket = nanali
cloud.aws.region.static = ap-northeast-2
cloud.aws.stack.auto = false

 

github에 올릴 때 중요한 값들은 그냥 올리면 안된다.

(예전에 aws key를 그냥 올렸다가 aws에서 경고 메일 날라오고 기능이 잠긴 기억이 있다.)

 

그래서 환경 변수로 설정해야하는데, 배포 환경에서 환경 변수를 어떻게 설정해야 하는지에 대한 의문이 계속 들었다. 구글을 찾아봐도 사람마다 다르고 마음대로 가져다 썼다가 빌드 에러가 더 심해질까봐 건드리지도 못했다. 계속 찾아가면서 겨우 환경 변수를 설정할 수 있었다.

 

 

github의 settings에 key를 저장하는 곳이 있다.

여기에다가 key를 저장한 다음에 배포를 할 때 환경변수로 넘겨주는 것이다. 보안 관련해서 매우 중요한 일이기 때문에 할 때마다 조심스럽게 해보고 오랜 시간을 들여서 겨우 할 수 있었다.

 

ec2의 application.properties를 덮어쓰고 s3의 JAR 파일에도 환경변수가 들어간 application.properties를 올려준다.

 

 

 

이 외에도 AWS에서 설정한 것들이 매우 많지만 내가 헤맸던 부분을 되짚어보고 실수하지 않도록 포스팅을 하게 되었다.

 

인프라 구축은 개발만큼 매우 중요한 것이므로 이후에도 CI-CD를 활용해서 배포 자동화를 잘 하고싶다. 그리고 방학이 되면 도커와 쿠버네티스를 공부해서 클라우드 쪽도 제대로 알고싶다.