[ASC] CI/CD 파이프라인 구축하기

abi hong·2023년 7월 15일

AWS

목록 보기
5/11

Github Actions + AWS CodeDeploy를 이용해서~ CI/CD 파이프라인 구축해보자!

CI/CD 흐름


1. Github에 코드 Push 하면 Github Actions이 자동으로 실행되어 CI(빌드) 작업을 수행
2. 코드 상에 문제가 없다면 빌드 작업을 통해 jar(혹은 war) 파일이 생성되고 사전에 작성한 배포 스크립트 파일(= deploy.sh)과 함께 AWS Storage 서비스인 S3에 전달되어 저장됨
3. S3에 저장이 잘 되었다면 EC2에 설치한 CodeDeploy Agent가 S3에 저장된 프로젝트 Zip파일을 가져와서 내려받음
4. 그 후, 배포 스크립트 파일을 읽어 프로젝트 실행을 시작

→ Github에 코드를 Push하기만 하면 가장 latest 버전이 EC2에 내려받아져서 프로젝트가 자동으로 배포되는 흐름 !

[CI Part] Github Actions 설정 (1)

처음으로 Github Actions를 설정하게 되면, .github/workflows에 yml 파일을 작성하게 된다.

  • yml 파일에 작성해야할 비밀키, 환경변수들은 Setting에서 Secret 탭으로 들어가면 설정가능
  • Github Action Workflow / gradle.yml
    이 파일의 내용은 main 브랜치로 push 혹은 PR을 수행했을 때, 아래의 CI 액션이 동작한다.
# WorkFlow의 이름을 'Java CI with Gradle'로 정의
name: Java CI with Gradle

# Trigger 지정으로, main 브랜치가 Push, PR 될 때 해당 workflow 실행
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

permissions:
  contents: read

# workflow는 한 개 이상의 job을 가지고, 각 job은 여러 step에 따라 단계를 나눌 수 있음
jobs:
  build:
	# 해당 job에서 아래의 step들이 어떠한 환경에서 실행될 것인지를 지정
    runs-on: ubuntu-latest
	
    steps:
    # 작업에서 접근할 수 있도록 Github_Workspace에서 저장소를 체크아웃함
    - uses: actions/checkout@v3
    
    - name: Set up JDK 11
      uses: actions/setup-java@v3
      with:
        java-version: '11'
        distribution: 'temurin'
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    
    #Build
    - name: Build with Gradle
      run: ./gradlew clean build

IAM User 권한 설정

부여해야하는 권한은 AWSCodeDeployFullAccessAmazonS3FullAccess 이다.

그 후, 지금에만 확인할 수 있는 ACCESS KEY와 SECRET KEY는 .csv 파일로 저장해두어야 한다.
→ 여기서 생성된 비밀키[ACCESS KEY, SECRET KEY, Region, application.properties에 적혀있는 값들 등등]를 위에서 말한 Github Setting의 Secret 탭에 값을 등록하여 노출하지 않고 사용해야 한다.

S3 Bucket 생성 및 설정 & Github Actions secrets 추가

여기에서 S3는 AWS Storage 서비스로 Github Actions로 빌드된 .jar(혹은 .war) 파일을 저장하는 파일서버를 의미한다.

배포를 위한 버킷의 경우, 따로 설정 필요없이 생성해주면 된다.

deploy shell 스크립트 작성

EC2에 설치한 CodeDeploy Agent가 S3에 저장된 이 파일과 프로젝트 파일을 가져와서 내려받는다. 그 후, 배포 스크립트 파일을 읽어 프로젝트 실행을 시작한다.

즉 스크립트 코드는 프로젝트를 EC2에서 실행할 때, 이전에 실행되고 있는 SpringBoot 프로젝트가 있다면 종료시킨 뒤 새로운 버전의 프로젝트를 실행해준다.

WAR_NAME=$(basename server-0.0.1-SNAPSHOT.war)
DEPLOY_PATH=/home/ec2-user/action
CURRENT_PID=$(pgrep -f $WAR_NAME)
NOW=$(date)

echo "> 현재 시간: $NOW" >>$DEPLOY_PATH/deploy.log

echo "> 현재 실행중인 애플리케이션 pid: $CURRENT_PID" >>$DEPLOY_PATH/deploy.log
if [ -z $CURRENT_PID ]
then
  echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다." >> $DEPLOY_PATH/deploy.log
else
  echo "> kill -15 $CURRENT_PID" >> $DEPLOY_PATH/deploy.log
  kill -15 $CURRENT_PID
  sleep 5
fi

DEPLOY_JAR=$DEPLOY_PATH/$WAR_NAME
echo "> DEPLOY_JAR 배포: $DEPLOY_JAR"    >> $DEPLOY_PATH/deploy.log
nohup java -jar $DEPLOY_JAR >> $DEPLOY_PATH/deploy_spring.log 2>$DEPLOY_PATH/deploy_err.log &

echo "=====" >> $DEPLOY_PATH/deploy.log

[CD Part] Github Actions 설정 (2)

  • Github Action Workflow / gradle.yml
    아까 작성한 gradle.yml 파일 아래부분에 Deploy 관련한 yml 파일 내용을 추가해준다.
# WorkFlow의 이름을 'Java CI with Gradle'로 정의
name: Java CI with Gradle

# Trigger 지정으로, main 브랜치가 Push, PR 될 때 해당 workflow 실행
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

permissions:
  contents: read

# workflow는 한 개 이상의 job을 가지고, 각 job은 여러 step에 따라 단계를 나눌 수 있음
jobs:
  build:
	# 해당 job에서 아래의 step들이 어떠한 환경에서 실행될 것인지를 지정
    runs-on: ubuntu-latest
	
    steps:
    # 작업에서 접근할 수 있도록 Github_Workspace에서 저장소를 체크아웃함
    - uses: actions/checkout@v3
    
    - name: Set up JDK 11
      uses: actions/setup-java@v3
      with:
        java-version: '11'
        distribution: 'temurin'
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    
    #Build
    - name: Build with Gradle
      run: ./gradlew clean build
      
    # 전송할 파일을 담을 디렉토리 생성
    - name: Make Directory for deliver
      run: mkdir deploy
      
    # War 파일 Copy
    - name: Copy War
      run: cp ./build/libs/*.war ./deploy/
      
    # appspec.yml Copy
    - name: Copy appspec
      run: cp ./appspec.yml ./deploy/    
      
    # scripts폴더 Copy
    - name: Copy scripts
      run: cp ./scripts/* ./deploy/   
      
    # 압축파일 형태로 전달
    - name: Make zip file
      run: zip -r -qq -j ./springboot-intro-build.zip ./deploy

    # S3 Bucket으로 copy
    - name: Deliver to AWS S3
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESSKEY }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRETKEY }}
      run: aws s3 cp --region ap-northeast-2 ./springboot-intro-build.zip s3://${{secrets.S3_BUCKET}}/springboot-intro-build.zip
 #aws s3 cp --region ap-northeast-2 --acl private ./springboot-intro-build.zip s3://springboot-intro-build
 
    # Deploy
    - name: Deploy
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESSKEY }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRETKEY }}
      run: |
        aws deploy create-deployment \
        --application-name cicd-habday \
        --deployment-config-name CodeDeployDefault.AllAtOnce \
        --deployment-group-name cicd-habday \
        --file-exists-behavior OVERWRITE \
        --s3-location bucket=${{secrets.S3_BUCKET}},bundleType=zip,key=springboot-intro-build.zip \
        --region ap-northeast-2

AWS CodeDeploy와 AWS EC2 연동

배포 시스템인 CodeDeploy를 통해 EC2 인스턴스에 배포할 수 있도록 설정해야 한다.

EC2 설정

  • IAM 역할 만들고 EC2 인스턴스에 역할 연결
    → 먼저 CodeDeploy와 EC2 사이의 접근을 가능하게 하려면 AWS IAM '역할'을 만들어줘야 한다. 역할은 AWS 내에서의 접근 권한이라고 생각하면 된다.
    이때, 역할을 바꾸고 나면 인스턴스를 재부팅 해야한다.

  • EC2 인스턴스에 codedeploy 설치
    재부팅이 완료되면 터미널에서 EC2 인스턴스에 연결 후, 아래의 명령어를 통해 codedeploy를 설치한다.

    aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2

    다운로드 성공하면 'download: s3://aws-codedeploy-ap-northeast-2/latest/install to ./install' 이 문구가 나온다.

    아래와 같이 실행 권한을 주고 실행을 하여 설치를 마무리한다.

    chmod +x ./install
    sudo ./install auto

    설치 확인을 위해 아래의 명령을 입력하고

    ```
    sudo service codedeploy-agent status
    ```

    'The AWS CodeDeploy agent is running as PID ****' 이와 같은 메세지가 출력되면 된다.

CodeDeploy 설정

  • 역할 만들고 EC2 인스턴스에 역할 연결
    EC2와 마찬가지로 CodeDeploy에서도 역할을 만들어야 한다.

  • codedeploy 애플리케이션 생성 및 설정
    역할이 만들어졌다면 codedeploy 서비스에서 애플리케이션을 생성한다. 이때, 역할은 아까 만든 역할을 선택하고 배포 설정의 경우, 한대의 서버이기 때문에 CodeDeployDefault.AllAtOnce를 선택한다.

appspec.yml 생성

프로젝트 폴더에 이 파일을 생성해야 한다. codedelpy 실행 시 작성한 스크립트를 동작시킨다.

version: 0.0
os: linux
# S3에 있는 zip 파일이 EC2에 배포될 위치를 지정
files:
  - source: / # CodeDeploy에서 전달해 준 파일 중 destination으로 이동시킬 대상을 루트로 지정(전체파일)
    destination: /home/ec2-user/action/ # source에서 지정된 파일을 받을 위치, 이후 jar를 실행하는 등은 destination에서 옮긴 파일들로 진행
    overwrite: yes

permissions: # CodeDeploy에서 EC2서버로 넘겨준 파일들을 모두 ec2-user권한을 갖도록 합니다.
  - object: /
    pattern: "**"
    owner: ec2-user
    group: ec2-user

# ApplicationStart 단계에서 deploy.sh를 실행시키도록 합
hooks: # CodeDeploy배포 단계에서 실행할 명령어를 지정합니다.
  ApplicationStart: # deploy.sh를 root권한으로 실행합니다.
    - location: deploy.sh
      timeout: 60 # 스크립트 실행 60초 이상 수행되면 실패가 됩니다.
      runas: root # s3 이미지 업로드 시, permission denied 오류 해결을 위해 ec2-user에서 root로 변경

참고한 블로그
https://himoonhee-coding.tistory.com/113

0개의 댓글