본문 바로가기

개발/리뷰

EC2 서버 배포 자동화 해보자! (feat. Github Action + AWS Code deploy)

 

내가 바로 배포머신~~~~

 

 

회사 프로젝트를 진행하면서 서버 코드가 업데이트될 때마다 EC2에 수동으로 배포해왔다.

 

 

유저 테스트 때문에 코드가 엄청 자주 바뀌었던 1월에는 진짜 하루에도 몇 번씩 수동 배포했다.

 

 

그때마다 현타가 왔다^^

시간만 생기면 자동화하고 만다... 이를 갈았다..ㅋ

 

 

그렇게 벼루고 벼루다 배포 자동화를 완료했다.

 

 

서울사람 프로젝트를 했을때 웹 페이지&서버 배포 자동화를 해본적이 있었기 때문에

나름 자신이 있었다^0^

 

 

그때는 토이 프로젝트다보니 프리티어 EC2를 썼다.

프리티어가 얼마나 구리냐면..;;

 

 

npm install 만으로도 cpu 사용량이 100%로 치솟아

EC2가 뻗어버려 재시작을 해야 했다.

 

 

그래서 그 시절에는....

 

1) github action으로 환경변수들을 셋업하고 Docker 이미지를 빌드하고 registry에 푸시했다.

2) Docker에서는 npm install 부터 앱 빌드까지 이미지로 만들었다.

3) EC2에 직접 접속해서 shell script를 실행해 가장 latest 이미지를 띄우게 했다.

 

 

이걸 semi-자동화라고 칭하고 싶다^^

 

 

완전 자동화를 할 수도 있었겠지만 이정도도 충분하다고 느껴서

그만했던 것 같다,,

 

 

이번에는 complete-자동화를^____^ 해보고 싶어 구글링 해본 결과,

AWS code deploy 서비스를 알게 되었다.

 

 

https://aws.amazon.com/ko/codedeploy/pricing/

 

AWS CodeDeploy 요금 | Amazon Web Services

AWS CodeDeploy 및 아키텍처 비용을 단일 예상 비용으로 계산할 수 있습니다.

aws.amazon.com

EC2, Lambda, ECS에서 CodeDeploy를 사용하는 경우: AWS CodeDeploy를 통해 Amazon EC2, AWS Lambda 또는 Amazon ECS에 코드를 배포하는 데는 추가 비용이 부과되지 않습니다.
온프레미스에 CodeDeploy를 사용하는 경우: AWS CodeDeploy를 사용해 온프레미스 인스턴스를 업데이트하는 경우에는 업데이트당 0.02 USD의 요금이 부과됩니다. 최소 요금 및 사전 약정은 없습니다. 예를 들어 인스턴스 3개에 대한 배포는 인스턴스 업데이트 3회와 동일합니다. CodeDeploy에서 인스턴스를 업데이트하는 경우에만 요금이 부과됩니다. 배포가 진행되지 않은 인스턴스에 대해서는 요금이 부과되지 않습니다.
애플리케이션을 저장하고 실행하기 위해 CodeDeploy와 함께 사용할 수 있는 다른 AWS 리소스(예: S3 버킷)에 대해서는 비용을 지불해야 합니다. 사용한 만큼만 비용을 지불하고 최소 요금 및 사전 약정은 없습니다.

 

 

Code deploy 서비스는 온프레미스 이외에 대한 배포는 무료이다!

S3 이용료랑 원래쓰던 EC2 돈만 내면 됨!! 개이득???

 

 

회사에서 진행하는 프로젝트다보니 빠방한 지원 덕에 사용하는 EC2 스펙이 꽤 준수하다ㅎㅎ

 

 

Docker의 힘은 빌리지 않아도 괜찮을 것 같아서 과감하게 Docker는 버리고

Github Action + AWS Code deploy 조합을 택했다.

 

 

배포가 일어나는 과정을 타임라인 순으로 보면 다음과 같다.

 

1. Github 레포지토리에 원하는 event(예시-특정 브랜치에 commit을 push함)가 발생한다.

2. event 발생에 따라 정의된 Github Action이 실행되어 Job을 실행시킨다.

3. Job은 먼저 레포지토리 코드를 압축하여 AWS S3에 업로드한다.

4. Job은 다음 배포할 소스코드가 업로드되어 배포하라고 AWS code deploy에게 알려준다.

5. EC2에 띄워놓은 AWS code deploy agent가 배포를 진행한다.

 

 

 


IAM 사용자를 만들고 권한을 추가하자!

 

 

 

AWS Code deploy 서비스를 이용하기 위해서는

코드를 S3에 올리고 Code deploy를 실행시킬 수 있는 권한이 있는 IAM 사용자를 만들어야 한다.

 

 

IAM > 사용자 > 사용자 생성에서

원하는 IAM 사용자 이름을 정한다.

 

 

 

그런 다음 위와 같이 AmazonS3FullAcess와 AWSCodeDeployFullAccess 정책을 추가한다.

 

 

만들어진 IAM 사용자의 Access key와 Secret key는 추후에 코드/cli로 AWS 서비스에 접근할때 필요하다.

잘 저장하도록 하자.

 

 

 


AWS Code deploy에 애플리케이션을 만들자!

 

 

 

이제 AWS Code deploy 서비스 애플리케이션을 만들어 보자.

 

 

 

 

다음처럼 CodeDeploy > 애플리케이션  > 애플리케이션 생성을 하게 되면,

EC2를 이용해서 배포를 할 수 있는 컨테이너가 생성되었다.

 

 

애플리케이션은 여러 개의 배포 그룹을 포함할 수 있다.

 

 

애플리케이션은 S3 버킷에 코드 업로드를 하고 여러 개의 배포그룹에 한꺼번에 배포할 수 있는 큰 단위이고,

배포그룹은 배포할 EC2를 바인딩해주고 애플리케이션 안에서도 하나씩 배포할 수 있도록 하는 작은 단위로 이해했다.

 

 

 

 

배포 그룹은 CodeDeploy > 애플리케이션  > {배포그룹 추가를 원하는 애플리케이션} > 배포 그룹 생성에서 위와 같이 만들면 된다.

 

 

여기서의 중요 작업은 배포 그룹에는 배포할 EC2를 설정하고,

로드 밸런서 메뉴에서 로드 밸런싱 활성화가 디폴트라서 필요 없다면 끄는 것이다.

 

 

배포 설정은 나중에 자동화할때 aws cli로 재정의 가능하다.

콘솔에서 수동 배포할게 아니라면 디폴트로 냅두자.ㅎㅎ

 

 


Github Action를 이용해 AWS S3로 소스코드를 업로드하자!

 

 

 

AWS Code deploy를 사용하기 위해서는 배포할 소스코드가 필요한데,

보통 S3에 압축된 소스코드를 업로드하고 EC2에 설치된 code deploy agent가 얘를 가져다가 배포한다.

 

 

Github 레포지토리랑 바로 연결시킬 수 있는 것?? 같았지만 (확실치 않다)

얕은 구글링에 따르면 회사에서 사용하는 엔터프라이즈 Github과는 호환이 안되는듯 했다.

 

 

그래서 Github Action에서 해야할 일은 아주 간단하다.

레포지토리에 원하는 event가 발생했을때 소스코드를 압축해서 S3에 업로드해주는거다.

 

 

나는 release 브랜치에 코드가 푸시되면 배포가 되도록 했다.

 

 

일단 프로젝트 루트 디렉토리에 .github/workflows 폴더를 만든다.

여기 안에 위치한 xxx.yml 파일들이 각각의 Action으로 처리된다.

 

 

 

원하는 이벤트가 발생하면 위와 같이 Action이 실행된다.

Action을 선택하면 프로그래스 및 로그들도 확인할 수 있다.

 

 

.github/workflows/xxx.yml S3로 코드 업로드하는 부분까지만 작성해보면 다음과 같다.

 

name: Code S3 Upload

on:
  push:
    branches:
     - release

jobs:
  build:
    runs-on: ubuntu-18.04
    steps:
      - name: Check out repository code
        uses: actions/checkout@v2

      - name: Zip code
        run: tar cvfz ./${{ github.event.repository.name }}.tar.gz *

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

      - name: Upload to S3
        run: aws s3 cp --region ap-northeast-2 ./${{ github.event.repository.name }}.tar.gz s3://{YOUR_BUCKET_NAME}

 

 

yml 안에 들어간 AWS_ACCESS_KEY와 SECRET_KEY는

압축된 코드를 S3 업로드 하기 위해 필요한 값으로

 

 

가장 처음에 만든 IAM 사용자의 키 값을 넣어주면 된다.

 

 

key를 하드코딩하면 보안에 취약하기 때문에 Github에서 제공하는 Secret 기능을 사용하자.

 

 

Github 해당 레포지토리 Setting > Security > Secrets and variables > Actions 에서

New repository secret으로 key와 value로 관리할 수 있다.

 

 

 

 

이 작업이 성공하면 S3에 코드가 압축되어 잘 올라간 것을 확인할 수 있다.

 

 

 


AWS Code deploy가 배포하기 전/후 작업을 정의하자!

 

 

Code deploy는 배포하기 전/후 작업으로 실행할 스트립트를 정의할 수 있도록 한다.

이는 appspec.yml이라는 파일을 프로젝트의 최상위에 만들면 된다.

 

 

https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/reference-appspec-file-structure.html

 

AppSpec 파일 구조 - AWS CodeDeploy

이 AppSpec 파일은 YAML로 작성되지만, 동일한 구조를 사용하여 JSON으로 Lambda 배포용 AppSpec 파일을 작성할 수 있습니다. JSON 형식 AppSpec 파일의 문자열은 항상 따옴표("")로 둘러싸야 합니다.

docs.aws.amazon.com

 

파일에는 다음과 같은 내용이 들어가면 된다.

 

1. 소스코드가 저장될 위치

2. 배포 전, 후에 진행할 작업 스크립트 파일명

 

# appspec.yml
version: 0.0
os: linux
permissions:
  - object: /home/ubuntu/data
    owner: ubuntu
    group: ubuntu
    mode: 755
    type:
      - directory
      - file
files:
  - source: /
    destination: {소스 코드를 가져올 경로}
hooks:
  BeforeInstall:
    - location: {소스 코드를 가져오기 전에 실행시킬 스크립트 경로}
  AfterInstall:
    - location: {소스 코드를 가져온 후에 실행시킬 스크립트 경로}

 

 

소스코드가 저장될 디렉토리의 경우 미리 만들어두도록 한다.

디렉토리의 owner와 group가 혹시나 root이면 code-deploy-agent가 접근할 수 없으니 미리 풀어두자.

 

 

 

 

BeforeInstall에서는 보통 서버 프로세스를 죽이고

과거 소스코드 지우기 및 디렉토리를 다시 만들어주는 작업을 진행한다.

 

# kill server running
pkill -f runserver

# remove stale server directory
if [ -d /home/ubuntu/data/server ]; then
    rm -rf /home/ubuntu/data/server
fi
mkdir -vp /home/ubuntu/data/server

 

 

AfterInstall에서는 저장한 소스코드를 실행시키기 위한 사전작업 및 서버 코드 실행을 한다.

해당 스크립트를 실행해야만 배포가 일어난 것이다.

 

# copy setting files and activate venv
cd /home/ubuntu/data/
cp setting-backup/* server
. ./venv/bin/activate

# install packages, migrate db and run server
cd server
pip install -r requirements.txt
python manage.py migrate
nohup python manage.py runserver 0.0.0.0:8000 > nohup.out 2>&1 &

 

내 서버는 장고이기 때문에 venv를 실행시키고 pip 인스톨 및 db migrate등을 한 후

서버를 실행시키도록 정의했다.

 

 

어떤 프레임워크를 쓰느냐에 따라 AfterInstall 스크립트의 내용은 다를 것이다.

 

 


EC2에 Code deploy agent를 설치하고 찐으로 배포하자!

 

 

 

프로젝트 루트 디렉토리에 만들었던 .github/workflows/xxx.yml 파일 가장 하단에

찐으로 EC2에 배포하는 하는 작업을 넣자!

 

      - name: Deploy with AWS CodeDeploy
        run: aws deploy create-deployment
          --application-name {YOUR_CODE_DEPLOY_APPLICATION_NAME}
          --deployment-config-name CodeDeployDefault.OneAtATime
          --deployment-group-name {YOUR_CODE_DEPLOY_GROUP_NAME}
          --s3-location bucket={YOUR_BUCKET_NAME},bundleType=tgz,key=${{ github.event.repository.name }}.tar.gz 

 

 

aws deploy cli는 Github Action이 S3에 코드를 업로드하고 EC2에 배포하도록 트리거해준다.

 

 

EC2에 배포가 가능하기 위해서는 AWS Code deploy 배포그룹에 바인딩한 EC2에

code deploy agent라는 프로그램을 설치하고 띄워놓아야 한다.

 

 

해당 프로그램이 event를 pull 받아 배포 작업을 수행하기 때문이다.

 

 

EC2에 접속해서 code deploy를 설치하고 프로그램을 run하자.

 

https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/codedeploy-agent-operations-install-ubuntu.html

 

Ubuntu Server용 CodeDeploy 에이전트 설치 - AWS CodeDeploy

출력을 임시 로그 파일에 쓰는 것은 Ubuntu 20.04에서 install 스크립트를 사용하여 알려진 버그를 해결하는 동안 사용해야 하는 해결 방법입니다.

docs.aws.amazon.com

 

 

 

 

이제 그럼 Github action이 실행될 수 있도록 release 브랜치에 develop 브랜치를 머지를 해보겠다.

 

 

나는 다음과 같은 에러가 발생했다.

 

An error occurred (AccessDeniedException) when calling the CreateDeployment operation:
User: arn:aws:iam::XXX:user/XXX is not authorized to perform: codedeploy:CreateDeployment on resource...

 

 

나는 서버에서 S3로 파일을 업로드하는 코드 작업을 하면서

S3 업로드 정책이 허용된 IAM 사용자를 미리 만들어둔 상태였다.

또 만들기 귀찮으니까 원래 사용하던 사용자 키를 재사용했다. (그래서 가이드글 제대로 안읽음;;)

 

 

내가 쓰는 사용자에 CodeDeployFullAccess 정책(또는 Code deploy 애플리케이션을 만들때 추가한 역할)을 추가하지 않아서 였다.

 

 

 

 

사용자에 정책을 넣어주니 Code deploy agent까지 잘 실행되었다.

 

 

 

 

배포가 잘된건가 긴가민가 하다면 CodeDeploy > 배포 > 배포 내역에서

가장 최근 배포된 항목의 상태를 확인해보자.

 

 

 


 

 

회사에서 했던 작업이라 환경이나 셋업이 좀 달라 애를 좀 먹었지만

하고 나니 가장 뿌듯하고 잘했다 싶은 작업이다.

 

 

올려둔 코드들도 회사에서 사용하는 셋업들을 제거하면서 약간 다르거나 오타가 수 있다는 점!

 

 

자유롭게 회사 컴으로 블로깅하는 그날이 왔으면 좋겠다......