DevOps/AWS

[AWS] Github Actions, CodeDeploy, Nginx 로 무중단 배포하기 - 1

샤아이인 2022. 5. 31.

 

이번글은 Github Actions를 활용한 무중단 배포를 시도하면서 (미래의 저를 위해)과정을 정리한 글 입니다.

AWS free tier 상에서 가능한 최소한의 무중단 배포를 진행해보도록 하겠습니다.

 

총 4개의 시리즈 글로 진행될 것 입니다.

 

1) Github Actions 과 AWS S3 연동 (이번 글)

2) EC2 설정과 CodeDeploy 적용

3) EC2와 RDS

4) Nginx 설치와 배포스크립트

 

▶ 전체 흐름도

우선 전반적인 흐름은 다음과 같습니다!

과정을 진행하면서 막힐때, 다음 그림을 보면서 생각해보시면 어떤 부분에서 막히고 있는지 이해하기 쉬우실 거에요!

 

우선 간단하게 흐름을 요약해보면!

  1. Github Actions에서 프로젝트를 우선 build 하고, 결과물인 jar 파일을 압축해서 S3에 업로드합니다.
  2. 이후 CodeDeploy에게 S3에 있는 jar 파일을 가지고 배포를 진행해달라고 요청합니다.
  3. CodeDeploy는 우리의 EC2 내부에 있는 CodeDeploy Agent에게 배포하라는  메시지를 전달하고
  4. Agent는 jar 파일을 S3에서 받아서 사전에 작성된 스크립트에 따라 배포를 진행합니다.
  5. 새로운 Spring Boot WAS를 띄우고, Nginx 스위칭을 통해 무중단 배포를 할 수 있도록 Agent에게 배포 스크립트를 제공합니다.

 

1. Github Actions 과 AWS S3 연동

1.  예제 Spring Code

우선 배포에서 사용될 Spring의 예제 Controller는 다음과 같습니다.

@RestController
@RequestMapping("/api")
public class HealthController {

    @GetMapping("/health")
    public String healtCheck() {
        return "OK!";
    }
}

간단하게 서버를 실행했을때 "/api/health" 로 요청시 "OK!"라는 응답을 받아볼 수 있도록 만들었습니다.

향후 배포 스크립트에서 1번 서버에서 2번 서버로 switching 하는 과정에서 Health Check 과정에서도 사용될 예정 입니다.

 

페키지 구조는 다음과 같을 것 입니다! 

(프로젝트를 진행하면서 글을 작성하고 있기 때문에 페키지에 뭐가 많은데, 이 글에서 필요한것은 HealthContoller 뿐 입니다!!)

최 상위에는 디렉토리가 2개가 각각 BE, iOS 가 존재하고 있습니다.

 

다음으로는 Github Actions을 통해 진행하게 될 것 입니다.

public repository에서는 무료로 사용이 가능한 기능 입니다!

 

yml 파일로 스크립트를 작성하게 되는게, 크게 어려운 점이 없기 때문에 

 

2. Github Actions 에서 Work Flow 생성하기

저장소의 Actions을 누르면 set up a workflow yourself 이 보이는데, 눌러 줍시다!

누르면 다음과 같은 화면을 확인할 수 있습니다.

완쪽에는 yml 스크립트를 입력할 수 있는 부분이 있고, 오른쪽 에는 누군가 공유한 스크립트를 필요에 따라 추가할 수 있는 Marketplace 와, 더 나아가 workflow 에 대한 설명을 볼 수 있는 Documentation 를 확인할 수 있습니다.

 

또한 빨간 박스 안을 보면 workflow yml 파일이 프로젝트/.github/workflows/ 위치에 생성됩니다.

이전 단락에서 사진을 보면 .github/workflows, BE, iOS 이렇게 총 3개의 디렉토리가 있었던것을 기억하시죠?

 

3. 스크립트 작성하기

이제 좌측에 스크립트를 다음과 같이 작성해 봅시다!

# airbnb-deploy.yml

name: test-deploy

on:
  push:
    branches: [test-deploy]
    
permissions:
  contents: read
  
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      working-directory: ./BE

    steps:
      - name: Checkout
        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
        shell: bash
        working-directory: ${{env.working-directory}}

      - name: Build with Gradle
        run: ./gradlew bootjar
        shell: bash
        working-directory: ${{env.working-directory}}
        
# 나머지는 뒤에서 더 추가할 예정

우선 스크립트에 대하여 알아봅시다!

  • name
    • 진행할 workflow 의 이름을 지정합니다.
  • on
    • 이 workflow 가 언제 실행될건지 전략을 지정할 수 있습니다.
    • 특정 브랜치가 push 되는 경우, Pull Request 가 생성될 경우, 또는 crontab 문법으로, workflow_dispatch 는 수동으로 직접 실행시키는 방식 입니다.
    • 우리는 특정 brach (우리의 예시에서는 test-deploy 브렌치) 에 push 할때마다 적용 되도록 할것 입니다.
  • job, steps
    • workflow 는 하나 혹은 그 이상의 job 을 가질 수 있고 각 job 은 여러 step 에 따라 단계를 나눌 수 있습니다.
  • runs-on
    • 해당 workflow 를 어떤 OS 환경에서 실행할 것인지 지정할 수 있습니다.
  • env
    • 스크립트 build 부분에서 사용할 환경변수를 지정하고 있습니다.

 

각 step에서 uses에서 actions/행위이름 를 사용하는 부분은, 앞서 보았던 marketplace 에서 미리 정의되어 있는 행위를 가져와서 사용하는 것 입니다!

이때 checkout 은 github가 제공하는 work-space(우리의 workflow 를 실행하는 공간)에서 내 저장소가 위치한 곳으로 이동하는 것 입니다.

 

또한 환경변수를 하나 설정하고 있습니다.

env:
  working-directory: ./BE

위와 같이 working-directory 에 "./BE" 라는 값을 할당해줬습니다.

 

이후 다음과 같이 각 step 이 진행될 디렉토리를 환경변수를 통해 지정해줄 수 있게됩니다!

- name: Grant execute permission for gradlew
  run: chmod +x gradlew
  shell: bash
  working-directory: ${{env.working-directory}}

 

java 를 셋업하고 gradlew 에 실행권한을 준 뒤 프로젝트를 build 하는 과정입니다.

(ps. 각 스크립트에서 사용하는 파일의 위치, 경로 등은 프로젝트의 상황에 따라 알맞게 수정하셔야 합니다...)

 

4. Work Flow 실행하기

저희는 test-deploy 브렌치에 push 할 경우 자동으로 workflows가 실행되도록 했기 때문에 코드의 일부분을 수정하신 후, test-deploy에 push 하면 다음과 같이 실행되는 것 을 확인할 수 있습니다.

다음과 같이 방금 만들었던 간단한 프로젝트에 대해 빌드가 실행된 것을 Step 별로 확인할 수 있습니다.

스크립트가 실패했다면 어떤 Step에서 문제가 있었는지도 상세 로그를 통해 확인할 수 있습니다.

또한 bash 명령어를 얼마든지 추가할 수 있기 때문에, 필요에 따라상태를 확인하면서 진행할 수도 있습니다!

 

5. S3에 jar 파일 업로드 하기

이번 단락에서는 전체 흐름의 1번에 해당되는 jar 파일 S3로 전달하는 과정에 해당됩니다.

S3 (Simple Storage Service) 는 AWS 의 파일 스토리지 서비스입니다. 객체 단위로 저장되죠!


용량도 무제한이기에 여러가지 파일들을 필요에 따라 업로드/다운로드 하면서 다양한 방법으로 활용할 수 있는데요.

99.999999999%의 내구성과 99.99%의 가용성 을 보장하기 때문에 매우 안전하다 할 수 있습니다!


지금처럼 CI/CD 배포 flow에 S3를 이용한다고 하면 보통 build 한 jar 파일을 업로드하고, 배포하는 쪽에서 jar를 다운로드하여 배포하게 됩니다.

 

6. S3 생성하기

다음과 같이 버킷을 하나 생성해 봅시다.

 

퍼블릭 엑세스는 할일이 없으니 차단하도록 하겠습니다. 그럼 S3에 어떻게 접근하냐?

우리는 IAM 이라는 또 다른 AWS 의 사용자 인증, 인가 서비스를 이용하여 최소한의 접근 권한만 가지고 flow를 구축할 것 입니다!

생성된 버킷은 다음과 같습니다.

성공적으로 생성되었습니다!

 

7. IAM 권한 사용자 생성하기

위에서 언급했듯, S3는 퍼블릭 엑세스를 모두 차단했기 때문에 접근할수가 없는 상황입니다.

 

이때 Github Actions이 S3에 접근하기 위해서는 IAM이라는 서비스를 이용해야 합니다.

IAM 은 AWS 서비스에 대한 엑세스 권한을 최소한으로 부여하고 관리할 수 있게 해주는 서비스입니다.
외부 서비스에서 AWS 서비스에 접근하려고 할 때도 물론이고, AWS 서비스 간에 접근하고자 할 때도 권한 부여를 해줄 수 있습니다.

 

우리는 Github Actions 에게 S3와 CodeDeploy 에 대한 접근 권한을 가지고 있는 User Role을 부여하려고 합니다.


IAM 서비스로 이동해 다음과 같이 엑세스 관리 - 사용자 - 사용자 추가 를 선택합니다.

 


프로그래밍 방식 엑세스 에 체크하셔야 합니다!!


Github Actions 에서 AWS CLI 명령을 통해 S3에 접근할 것이기 때문에, 엑세스 키 ID, 비밀 엑세스 키를 발급받아야 하기 때문입니다.

 

다음은 부여할 권한 정책을 선택해야 합니다. Github Actions은 S3(1번과정) 와 CodeDeploy(2번과정)에 접근할 권한이 필요 합니다!
기존 정책 직접 연결 - 검색 에서 AmazonS3FullAccess 와 AWSCodeDeployFullAccess 를 선택합니다.

 

이후 테그를 추가해주셔야 합니다. 

태그가 있어야 이후 구별하기가 편하니 꼭 지정해주길 권장합니다!

이후 다음과 같은 최종 확인페이지가 나옵니다.

사용자 만들기를 누르면, 마지막에 엑세스 키 id, 비밀 엑세스 키 가 나오는데 메모장 같은데 잘 저장해 두시거나, csv 파일을 받아두시길 권장합니다.

 

사용자를 다 만들었습니다!! 다음 과정으로 가시죠!

 

8. S3에 jar 파일 업로드 하기

이전에 만든 S3 버킷에 build한 jar 파일을 전달해 봅시다!

업로드 할때는 flow 상에서 압축하는 과정이 있습니다. 압축을 한 후, S3에 전달해 봅시다.

 

추가되는 스크립트는 다음과 같습니다.

# airbnb-deploy.yml

name: test-deploy

on:
  push:
    branches: [test-deploy]
    
permissions:
  contents: read
  
env: # 새로 추가한 부분
  S3_BUCKET_NAME: build-jar-shine-deploy
  PROJECT_NAME: test-deploy
  
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      working-directory: ./BE

    steps:
      # 이전 부분 생략
        
      # 새로 추가한 부분
      - name: Make zip file
        run: zip -r ./$GITHUB_SHA.zip .
        shell: bash
        working-directory: ${{ env.working-directory }}

      - 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: ${{ secrets.AWS_REGION }}

      - name: Upload to S3
        run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$S3_BUCKET_NAME/$PROJECT_NAME/$GITHUB_SHA.zip
        working-directory: ${{env.working-directory}}
  • env
    • 현재 스크립트에서 사용할 환경변수를 정의.
    • 여러 곳에서 공통으로 사용되는 문자열이나, 변경될수 있는 부분을 상수화 시켜 사용합니다.
  • $GITHUB_SHA
    • Github Actions 에서 제공하는 여러 기본 환경변수 중 하나입니다. 현재 workflow 를 실행시키는 커밋의 해쉬값입니다.
    • 다른 기본 환경변수들도 필요에 따라 사용할 수 있습니다.
  • ${{ secrets.KEY_NAME }}
    • 위의 환경변수와는 비슷하면서도 조금 다릅니다. Github Actions 자체에서 ${{ }} 문법으로 여러 함수나 표현식, 값들을 등록하고 가져와서 사용할 수 있습니다. 사전에 GIthub Repository 의 설정에서 키를 등록시켜서 사용할수 있습니다.
    • 자세한 내용은 Contexts and Expressions - 레퍼런스를 참조.
    • secrets 는 저장소에 등록한 비밀 키값을 가져오는 키워드이다. 밑에서 설명!
     
  • aws s3 cp
    • aws cli 명령어 입니다. copy 명령어로 현재 위치의 파일을 S3로 업로드할 수 있습니다. 반대도 가능해요!

 

jar 파일을 만들고, AWS credential 을 통해 인증하고, S3에 까지 업로드 하는 스크립트 파일을 작성하였습니다.

추가적으로 위에서 말한 엑세스 키 ID비밀 엑세스 키를 해당 저장소에 등록해 보겠습니다.

Github Repository의 Settings -> Secrets -> New secret 으로 위 스크립트에 작성했던 값들을 등록합니다.

- AWS_ACCESS_KEY_ID : 엑세스 키 ID
- AWS_SECRET_ACCESS_KEY : 비밀 엑세스 키
- AWS_REGION : ap-northeast-2

저장한 비밀값들은 스크립트에서 ${{ secrets.KEY값 }}으로 참조할 수 있게됩니다!

 

스크립트를 실행하면 우리가 원하던것처럼 S3 전달되는것을 확인할 수 있습니다.

S3안에 커밋번호가 이름이 zip 파일을 확인할 수 있다!

 

다음 글에서 CodeDeploy에 대하여 알아봅시다! 

댓글