차근차근 따라하는 CircleCI로 S3 자동배포 구성하기

Sunghwan Shin·2020년 12월 18일
3
post-thumbnail

CircleCI Configuration! 막 붙여서 때우지 말고 알고 씁시다 😎

0. 무엇을 하게 되나요?

자동배포 구성은 크게 두 단계로 진행됩니다.

  1. 프로젝트 빌드
  2. 업로드

프로젝트 빌드의 경우에는 로컬에서 직접 빌드할때와 거의 같은 방법으로 진행됩니다. 다만, 매번 패키지를 install 해 주는 것이 시간도 오래 걸리고 불필요한 작업이기 때문에 캐시를 이용하는 과정이 추가됩니다.

업로드의 경우, CircleCI Orb를 이용합니다. Orb는 CircleCI의 프로세스 중 자주 사용되는 것을 라이브러리처럼 만들어 둔 것이라고 생각하시면 편합니다.

yarn 대신 npm을 사용해도 똑같이 진행할 수 있습니다. 아래에서는 문맥상의 편리함을 위하여 yarn을 사용하는 경우로만 전제하고 진행합니다.

1. 프로젝트 빌드하기

0. node.js를 사용하도록 세팅하기

아시다시피 yarn은 node.js 패키지 매니저입니다. 프로젝트의 정상적인 빌드 과정을 위해서는 node.js가 필요합니다. CircleCI가 node.js를 사용할 수 있도록, 아래와 같이 node.js의 도커 이미지를 불러와 줍니다.

build-static-project:
    docker:
      - image: circleci/node:lts

1. checkout

환경 세팅 후 첫 번째 step으로 checkout 이 있습니다. checkout 은 현재 CircleCI의 cli가 실행될 디렉토리를 코드가 있는 위치로 변경합니다.

Configuring CircleCI

물론, 다른 옵션을 이용해서 다른 구성을 할 수도 있지만 간단한 배포에는 기본값으로도 잘 동작하도록 할 수 있습니다.

    steps:
      - checkout

2. 캐시 복구

yarn을 이용해 설치한 패키지 (/node_modules) 를 매번 새로 설치하게 되면 시간도 오래 걸리고 불필요한 연산도 늘어납니다. package.json을 해싱한 값을 key로 삼아 캐시를 저장하고 불러올 수 있습니다. (CircleCI의 패키지 캐시는 마지막 touch로부터 최대 30일까지 유지된다고 하는 것 같습니다.)

아래와 같이 캐시를 불러와 줍니다.

      - restore_cache:
          key: dependency-cache-live-{{ checksum "package.json" }}

(일러두기) 앞에 붙은 dependency-cache-live- 는 편의상 붙인 string입니다. {{ checksum "package.json" }} 구문이 해싱된 스트링에 해당하며, key string이 일치하는 과거 cache가 있으면 불러오는 구조입니다. 이후에 캐시 등록 부분에서도 똑같은 이름으로 캐시를 등록합니다!

3. Dependency 설치 및 프로젝트 빌드

로컬과 거의 똑같은 방법으로 dependency 설치 및 빌드합니다.

      - run: yarn --ignore-optional
      - run: yarn build

이 때, 시간을 단축하기 위해 @devDependencies 등의 optional dependency는 설치하지 않습니다. CircleCI에서 사용하는 인스턴스가 (당연하게도) 맥북보다 느리고, 빌드에 시간이 오래 소요되기 때문에 조금이라도 줄이는 편이 정신건강에 도움이 됩니다.

4. 캐시 저장

앞에서 소개한 것 처럼 캐시의 key를 지정하고, 어떤 파일들을 캐시할 것인지 작성합니다.

      - save_cache:
          key: dependency-cache-live-{{ checksum "package.json" }}
          paths:
            - ./node_modules

캐시 복구에 작성한 키와 완전히 동일한 키를 사용하며, dependency들이 저장되는 ./node_modules 디렉토리를 저장하도록 합니다.

5. (중요) 현재 작업 디렉토리 저장하기

빌드 스텝의 마지막 과정입니다. 다음 단계에서 S3에 업로드 할 빌드 결과물을 정상적으로 찾을 수 있도록 하기 위해 현재 작업중인 디렉토리를 다시 찾아 올 수 있도록 해 주어야 합니다.

      - persist_to_workspace:
          root: .
          paths:
            - .

여기서는 특정 디렉토리를 지정하지 않고 기본값(working_directory)을 사용하도록 . 을 넣어 줍니다.

2. S3에 업데이트하기

이제 빌드된 패키지를 S3에 업로드할 차례입니다.

(공식문서) CircleCI Developer - Orbs

*이번 단계에서는 위 공식 링크에 있는 AWS S3 업데이트 방법을 따라 구축한 내용을 소개합니다.

0. executor 설정하기

AWS CLI를 사용해야 CircleCI에서 CLI를 이용해 각종 동작을 수행할 수 있습니다. 프로세스 이름을 지어주고 아래와 같이 executor를 불러와 줍시다.

  deploy-project-to-S3:
    executor: aws-cli/default

1. 디렉토리 찾아가기

빌드된 파일을 찾아 업로드 하기 위해서, 아까 빌드를 수행한 디렉토리를 다시 찾아갑니다.

    steps:
      - attach_workspace:
            at: .

2. AWS S3 Sync & Copy

갑자기 업데이트라고 해 놓고 sync와 copy가 등장하니 당황스러울 수 있습니다. 각각은 다음과 같은 과정을 뜻합니다.

  • Sync: S3 버킷 안의 디렉토리와 현재 작업 위치의 디렉토리 "구조"를 맞춤. 재귀적으로 디렉토리를 탐색하며 없는 디렉토리를 생성
  • Copy: S3 버킷에 'from' 디렉토리에 있는 파일들을 옮김.

즉, 폴더 구조를 맞춘 후에 파일 업로드가 실시되며, 두 과정을 별도로 호출하도록 되어 있는 것입니다.

캐시 정책이나 퍼블릭 설정 등은 예제에 있는 값을 그대로 다라갑니다.

      - aws-s3/sync:
          arguments: |
            --acl public-read \
            --cache-control "max-age=86400"
          aws-access-key-id: AWS_ACCESS_KEY_ID
          aws-region: AWS_REGION
          aws-secret-access-key: AWS_SECRET_ACCESS_KEY
          from: 'build'
          to: 's3://my-s3-bucket-name'
      - aws-s3/copy:
          arguments: '--dryrun'
          from: 'build'
          to: 's3://my-s3-bucket-name'

이 때, AWS_ACCESS_KEY_ID, AWS_REGION, AWS_SECRET_ACCESS_KEY 세 개의 환경변수가 등장합니다. 해당 값들은 congifuration 파일에 직접 넣기에는 보안상의 문제가 발생할 수 있는 값이기에 CircleCI에 별도로 등록하여 관리합니다.

Project Settings > Environment Variables 에 들어가면 새 키를 추가할 수 있습니다. AWS 키 발급 방법은 저의 다른 포스트를 참조하세요 😉

사족) --dryrun 옵션에 대해서는 아래 공식 문서에서 좀 더 자세히 확인할 수 있습니다. 작업할 내용을 우선 표시한 뒤 실제로 작업을 수행하는 옵션으로 보입니다.

https://docs.aws.amazon.com/cli/latest/reference/s3/sync.html

https://docs.aws.amazon.com/cli/latest/reference/s3/cp.html

--dryrun: Displays the operations that would be performed using the specified command without actually running them

3. 마무리

어려운 부분은 다 끝났습니다! 마지막으로 사용한 orb를 import 해 줄 일만 남았습니다.

version: 2.1
orbs:
  aws-cli: circleci/aws-cli@1.3.1
  aws-s3: circleci/aws-s3@2.0.0

위 코드를 yaml 최상단에 붙여넣으면 모든 CI 작성이 마무리됩니다.

Full Configuration

version: 2.1
orbs:
  aws-cli: circleci/aws-cli@1.3.1
  aws-s3: circleci/aws-s3@2.0.0

jobs:
  build-static-project:
    docker:
      - image: circleci/node:lts
    steps:
      - checkout
      - restore_cache:
          key: dependency-cache-live-{{ checksum "package.json" }}
      - run: yarn --ignore-optional
      - run: yarn build
      - save_cache:
          key: dependency-cache-live-{{ checksum "package.json" }}
          paths:
            - ./node_modules
      - persist_to_workspace:
          root: .
          paths:
            - .

  deploy-project-to-S3:
    executor: aws-cli/default
    steps:
      - attach_workspace:
            at: .
      - aws-s3/sync:
          arguments: |
            --acl public-read \
            --cache-control "max-age=86400"
          aws-access-key-id: AWS_ACCESS_KEY_ID
          aws-region: AWS_REGION
          aws-secret-access-key: AWS_SECRET_ACCESS_KEY
          from: 'build'
          to: 's3://my-s3-bucket-name'
      - aws-s3/copy:
          arguments: '--dryrun'
          from: 'build'
          to: 's3://my-s3-bucket-name'

workflows:
  globalization-frontend-autodeploy:
    jobs:
      - build-static-project:
          filters:
            branches:
              only: master
      - deploy-project-to-S3:
          requires:
            - build-static-project
          filters:
            branches:
              only: master
profile
디자인하는 프론트엔드 개발자입니다. 우아한형제들에서 일하고 있습니다.

0개의 댓글