GitLab CI/CD를 활용한 AWS S3 배포

hoi·2020년 7월 20일
7

Start

프로젝트를 진행하면서 AWS에 배포를 해야 하는 상황이 있었고 GitLab CI/CD를 활용하여 자동 배포를 해본 경험을 공유하고자 합니다.

사실 AWS에 직접 손으로 업데이트를 하는 방법도 있겠지만 배포의 경우에는 유지보수를 하면서 여러번 있을 수 있는 일이기 때문에 이 부분을 자동화 한다는 것은 장기적으로 봤을 때

개발자의 업무의 부담을 상당히 덜어주는 일이라고 생각합니다. 이번 프로젝트에서 사용한 기술은 React.js이며 최종 배포 과정까지 진행할 예정입니다.

What is S3 ?

  • S3(Simple Storage Service)의 약자이며 파일이 담긴 서버의 역할을 대신하는 서비스 입니다.
  • 트래픽이 증가함에 따라서 서버를 증설하거나 하는 작업에서 보다 자유롭습니다. 최소 1바이트 부터 5TB까지 원하는 범위와 금액을 지불하고 서비스를 활용할 수 있습니다.
  • 여러 지역에 다중 호스팅이 가능하며 여러 지역의 사용자들은 더욱 빠르게 파일들을 제공받을 수 있습니다.
  • S3에서는 각각의 저장된 데이터를 객체라고 부릅니다. 또한 Bucket을 생성해서 관리하게 되는데 이 Bucket은 객체 들이 모여있는 최상위 디렉토리 라고 할 수 있습니다.

Setting Environment

Access key 발급

  1. 나의 프로필에서 내 보안 자격 증명에 접속하여 Security Key Access를 발급 받아야 합니다.
  • My Security Credentials Tab Click → Access Key Click → Create New Access Key

이 과정에서 생성된 Access Key와 Secret Key 복사할 수 있으며 .CSV file로 Download도 가능합니다.

앞으로 배포와 여러 보안 인증에 관련하여 사용 되는 키며 노출되지 않도록 관리해야 합니다.

Create S3 Bucket

Access Key를 받은 이후에는 AWS Bucket을 생성해야 합니다. AWS 관리 콘솔 또는 .gitlab.yml에 명령어를 활용하여 S3 관련 작업들을 수행할 수 있습니다.

management console에서 Storage Tab에 S3에서 S3 Bucket을 생성할 수 있습니다.

  • Bucket 생성하기

Bucket을 생성할 때 Region 설정은 내가 서비스 하고자 하는 지역의 가장 가까운 지역으로 선택하는 것이 좋습니다.

사용자가 많을 것이라고 생각하는 곳으로 Region을 선택한다면 해당 지역의 사용자들은 더욱 빠르게 데이터를 전송 받을 수 있습니다.

Setting Environment (Gitlab CI/CD)

Access Key를 발급 받고 Bucket을 생성했다면 이제 Gitlab CI/CD를 활용해서 환경을 구축해 보도록 하겠습니다.

Gitlab CI/CD를 활용한다면 배포 전 테스트, 빌드, 테스트 배포, 실제 배포등을 도커의 컨테이너 단위에서 단계별로 진행이 가능합니다.

What is Gitlab CI/CD ?

GitLab CI/CD는 지속적인 통합과 개발 방법론을 위해서 GItLab 사용자들에게 지원하는 CI/CD tool 이라고 할 수 있습니다.

CI(Continuous Integration)은 코드를 Git 리포지토리에 푸시하고, 모든 푸시에서 코드 파이프 라인을 실행하여 코드 변경 사항을 메인 브랜치에 병합하기 전에 빌드, 테스트 및 유효성 검사를 수행합니다.

CD(Continuous Delivery and Deployment)는 한 단계 더 CI로 구성되며, 리포지토리의 기본 분기로 푸시 할 때마다 애플리케이션을 프로덕션에 배포합니다.

때문에 우리가 만약에 배포는 master에서만 가능하도록 하겠다라고 한다면 pipeline에서 deploy stage는 브랜치가 master 일때만 배포가 가능합니다.

또한 GitLab CI / CD는 .gitlab-ci.yml리포지토리의 루트에 위치한 파일로 구성됩니다 .

Gitlab CI/CD Link : https://docs.gitlab.com/ee/ci/

저장소 생성하기

일단 GitLab에 저장소를 생성한 후 내가 배포하고자 하는 저장소의 환경을 설정해 줍니다.

  • 저장소 생성

Variables 정보 저장하기

이후 .gitlab-ci.yml 을 생성해서 CI 명령을 구현하기 전에 아까 받은 Access key를 Gitlab의 CI/CD Variables에 설정해 주도록 하겠습니다.

CI/CD Variables는 보안과 관련된 중요한 정보들을 환경변수로 관리할 수 있도록 도와주는 기능입니다. 각각의 key를 포함한 여러 값들을 코드 내부적으로 관리하면서 export 해서 사용할 수도 있지만

이는 보안적인 측면에서 안전하게 관리되지 못할 수 있기 떄문에 이 기능을 활용하여 관리하도록 합니다.

  • Variables 설정하기 Setting → CI/CD → Variables

위와 같이 Variables Tab에 들어가게 되면 Add Variables를 클릭해서 원하는 정보를 환경변수로 관리할 수 있습니다.

이곳에 아까 발급받았던 AWS_ACCESS_KEY_ID와 AWS_SECRET_ACCESS_KEY의 정보를 저장해 주도록 합니다.

.gitlab-ci.yml 파일 설정하기

이 후 테스트부터 빌드와 배포까지의 과정을 pipeline에서 실행할 수 있도록 해주는 .gitlab-ci.yml 파일을 우리 프로젝트의 루트 경로에 만들어 주도록 합니다.

각각 pipeline의 stage는 docker의 컨테이너 환경에서 실행됩니다.

  • cache와 build 환경 설정하기
# 사용할 docker image를 지정해 줍니다.
image: node:latest
# stages 순서대로 pipeline이 형성됩니다.
stages:
  - build
# 이 전 작업과 동일한 내용인지를 확인해서 재사용 해주는 캐싱 메커니즘의 옵션입니다.
cache:
  paths:
    - node_modules/
build:
  stage: build
  script:
    - npm install
    - npm run build

deploy-to-s3:
# aws cli를 설치하기 위해서 python image를 사용합니다.
  image: python:latest
  stage: deploy
  script:
    - pip install awscli
# aws s3 sync 명령어를 활용하여 빌드된 파일을 나의 버킷에 올려주고 public-read가 가능하도록 설정해 줍니다.
    - aws s3 sync build s3://www.banjihagames.com --acl public-read
# 해당 stage는 master branch에서만 실행 가능하도록 설정해 줍니다.
  only:
    - master
# deploy는 테스트와 실제 배포의 경우가 있을 수 있습니다.
# 이에 한 stage에서 여러 동작을 할 수 있도록 when: manual 을 넣어 줍니다.
  when: manual

위 프로젝트는 React.js를 기반이니 image를 node:latest로 설정해 줍니다.

여기서 Docker image는 컨테이너를 실행할 수 있는 실행 파일 또는 설정 값을 말합니다. React.js의 빌드와 테스트에 관련된 cli는 node.js이기 때문에 기본 image를 node로 설정합니다.

artifacts 설정하기

우리의 최종 목표는 빌드까지가 아니라 빌드한 파일을 AWS S3에 배포하는 것이 목적입니다.

하지만 bulid stage에서 bulid가 완료되고 파일이 생성됐다고 해서 다음 동작인 deploy stage에서 그 파일을 사용할 수 있는것은 아닙니다.

build 와 deploy stage는 각각 다른 컨테이너에서 실행되고 있기 때문입니다.

deploy stage에서 한번 더 bulid를 해줄수도 있겠지만 이는 불필요한 동작입니다.

이에 우리는 gitlab CI/CD에서 제공하는 artifacts 옵션을 활용해서 bulid에서 만들어진 파일을 deploy에서도 활용 가능하도록 해주겠습니다.

artifacts의 기능을 활용하면 stage의 결과를 다음 stage가 사용할 수 있도록 도와주며 그렇게 만들어진 결과 파일등을 만료시키는 시간 역시 정해줄 수 있습니다.

gitlab cache와 artifacts : https://docs.gitlab.com/ee/ci/caching/

  • artifacts
image: node:latest

stages:
  - build
  - deploy

cache:
  paths:
    - node_modules/
build:
  stage: build
  script:
    - npm install
    - npm run build
  artifacts:
# 만료 시간을 설정해 줍니다.
    expire_in: 1 hour
# 다음 stage에서 사용할 파일의 경로를 지정해 줍니다.
    paths:
      - build

deploy-to-s3:
  image: python:latest
  stage: deploy
  script:
    - pip install awscli
    - aws s3 sync build s3://www.banjihagames.com --acl public-read
  only:
    - master
  when: manual

여기까지 설정했다면 우리의 프로젝트를 빌드하고 s3 Bucket에 올리는 작업까지 환경을 설정했다고 할 수 있습니다.

이 파일을 우리의 저장소에 push 한 후 CI/CD Tab에 Pipeline 항목으로 들어가면 실제 동작을 볼 수 있습니다.

  • Pipeline


각 Stage가 정상적으로 완료된다면 위의 사진처럼 passed 라는 화면이 뜨게 됩니다.

Cloudfront

Cloudfront는 정적 또는 동적인 웹사이트 컨텐츠 다양한 데이터를 AWS에서 안전하게 전달 제공하는 CDN (Content Delivery Network) 서비스 입니다.

간단히 말해서 우리는 우리 프로젝트를 S3라는 저장소에 올렸다면 이제는 실제로 사용자들이 우리의 정적인 웹사이트를 사용할 수 있도록 Cloudfront에 배포하는 과정을 확인해 보도록 하겠습니다.

Amazon S3 + Amazon CloudFront : https://cloud.hosting.kr/techblog_180716_a-match-made-in-the-cloud/

Cloudfront 생성하기

Cloudfront 서비스로 들어간 후 Create Distribution 항목을 클릭합니다. 이 후 Web의 Get Started를 클릭하면 아래와 같은 화면이 나옵니다.

  • Origin Domain Name은 아까 생성한 S3로 설정해 줍니다.
  • Viewer Protocol Policy 항목을 Redirect HTTP to HTTPS로 변경해 줍니다.
  • Create Distribution 을 통해서 Cloudfront를 생성합니다.

정상적으로 생성됐다면 아래의 화면처럼 나의 domain 주소와 Cloudfront ID가 발급된 것을 볼 수 있습니다.

이제 Cloudfront ID를 활용하여 .gitlab-ci.yml 파일에 Cloudfront를 deploy하는 코드를 작성해 보도록 하겠습니다.

Cloudfront deploy

  • Cloudfront deploy를 purge라는 이름으로 지정해 줬으며 stage는 deploy로 설정해 줬습니다.
  • 이 후 aws cloudfront create-invalidation 명령어를 사용하여 deploy-to-s3로 update 했던 build 파일을 Cloundfront로 Refresh 시켜줍니다.
image: node:latest

stages:
  - build
  - deploy

cache:
  paths:
    - node_modules/
build:
  stage: build
  script:
    - npm install
    - npm run build
  artifacts:
    expire_in: 1 hour
    paths:
      - build

deploy-to-s3:
  image: python:latest
  stage: deploy
  script:
    - pip install awscli
    - aws s3 sync build s3://www.banjihagames.com --acl public-read
  only:
    - master
  when: manual

purge:
  image: python:latest
  stage: deploy
  script
    - pip install awscli
# $PROD_CLOUDFRONT_DIST는 Cloudfront를 생성하면서 발급된 아이디를 활용한다.
    - aws cloudfront create-invalidation --distribution-id $PROD_CLOUDFRONT_DIST --paths "/*"
  only:
    - master
  when: manual

이 과정까지 완료하셨다면 저희의 Pipeline은 아래와 같은 순서로 작동됩니다.

  1. build : build 과정을 거친 후 파일을 artifacts 로 다음 컨테이너가 사용할 수 있도록 한다.
  2. deploy-to-s3 : artifacts에 담겨있는 빌드 파일을 S3에 업로드 시켜준다.
  3. purge : Cloundfront에 올라가 있는 파일을 연결된 S3에 상태로 최신화 시켜준다.

Pipeline 동작 확인

  • build

정상적으로 동작이 완료됐으며 Artifact에 build 파일이 잘 전달된 상태를 확인할 수 있습니다.

  • deploy-to-s3

deploy-to-s3 동작이 정상적으로 완료됐으며 s3에 접속해서 확인해 본다면 최신화된 파일이 update된 것을 확인할 수 있습니다.

  • purge

      purge 동작을 완료한 후 cloudfront의 domain으로 접속해 보니 정상적으로 배포가 된 것을 확인할 수 있었습니다.

Error Pages 처리

실제 배포까지 끝났지만 사실 또 하나의 문제가 남았다. React.js는 하나의 html 파일로 동작하는 SPA(single page application)이다.

근데 이번 배포에서 나는 Route 처리를 한 상태였고 루트 경로로 접근하면 페이지가 잘 나타나게 되지만 route 처리를 한 경로로 접근하게 되면 403 Error Page가 나타났다.

Cloudfront에서 해당 경로로 Redirect 하는 접근을 막고 있고 있었고 실제로 Redirect될 html 파일도 없기 때문에 404와 403 Error에 대해서는 "/index.html"로 Redirect 시켜서 처리하도록 하면 되는 문제이다.

이 설정은 Cloudfront에 Error Pages 에서 설정해 줄 수 있다.

  • Create Custom Error Response

Create Custom Error Response를 눌러서 TTL은 5초로 설정해 줬으며 403 , 404 에러에 대해서 paths는 "/" 경로로 HTTP Response Code는 200 : OK로 설정해 줬다.

이로써 Route 처리에 맞게 정상적으로 Redirect 되는것을 확인했다.

후기

AWS S3와 Cloudfront 그리고 GitLab CI/CD는 처음이라서 환경을 구축하고 성공하는데 꽤 많은 시간을 사용했다 ......

하지만 이 프로젝트 뿐만 아니라 여러 프로젝트를 관리하고 유지보수 하면서 수많은 배포를 해야하는 상황을 생각한다면 미래의 시간을 아꼈다고 생각한다 !

profile
왕초보

1개의 댓글

comment-user-thumbnail
2020년 7월 21일

제법이군요! 프론트가 CI/CD까지 섭렵하시고... 도대체 그의 끝은 어디인가?

답글 달기