travis CI로 Serverless 애플리케이션 + Cloudformation Stack의 CI & CD 구축하기

백근영·2020년 4월 9일
0
post-thumbnail

CI & CD 파이프라인 구축

이번 글에서는 앞서 만들었던 AWS lambda의 함수 코드와 이를 배포하는 인프라를 정의한 Cloudformation의 stack에 대한 CI & CD 파이프라인을 구축하는 방법을 소개한다.

처음 구상했던 대략적인 파이프라인은 아래와 같다.

.travis.yml

CI 작업을 위해 작성한 .travis.yml의 전체 코드는 아래와 같다.

language: java
jdk:
  - openjdk11

env:
  global: # (1)
    - PACKAGE_VERSION=1.1-SNAPSHOT
    - PROJECT_NAME=deliSHAs_crawler
    - HANDLER_NAME=Crawler::handleRequest
    - BUCKET_NAME=snupa

branches:
  only:
    - master

cache:
  directories:
    - '$HOME/.gradle'

script: "./gradlew shadowJar" # (2)

deploy:
  - provider: s3 # (3)
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY
    bucket: $BUCKET_NAME
    region: ap-northeast-2
    skip_cleanup: true
    acl: public_read
    local_dir: build/libs
    wait-until-deployed: true
    on:
      repo: BaekGeunYoung/deliSHAs_crawler
      branch: master

  - provider: cloudformation # (4)
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY
    template: cloudformation.yml # (4-1)
    stack_name: DeployCrawler
    edge: true
    wait: true
    parameters: # (4-2)
      - LambdaCodeBucket=$BUCKET_NAME
      - LambdaCodeKey=${PROJECT_NAME}-${PACKAGE_VERSION}-all.jar
      - Lambdahandler=$HANDLER_NAME
      - JdbcUrl=$JDBC_URL
      - JdbcPassword=$JDBC_PASSWORD
    capabilities: CAPABILITY_IAM # (4-3)
    region: ap-northeast-2

설명

(1) CI 과정 동안 전역적으로 사용할 변수들을 정의했다. 패키지 버전이나 파일을 저장할 S3 버킷 이름 등을 전역변수화했다.

(2) AWS lambda에 배포하기 위해선 실행가능한 단일 Jar이 필요하기 때문에 shadowJar 커맨드로 빌드를 하도록 했다

(3) 빌드 결과물을 S3에 업로드하는 부분이다. local_dir에 지정된 경로에 있는 파일들을 지정된 bucket에 업로드해준다.

(4) Cloudformation stack을 만들거나, 갱신하는 부분이다.

(4-1) 템플릿 파일의 경로를 지정해준다. 로컬 디렉토리의 경로를 지정할 수도 있고, S3에서 받아올 수도 있다.

(4-2) 템플릿 파일에 주입해줄 parameters를 정의한다. 환경에 따라 바뀔 수 있는 값들을 파라미터화하면 좋을 것이다.

(4-3) 템플릿에서 정의한 리소스 중에 IAM 리소스가 존재한다면 이 값을 CAPABILITY_IAM으로 설정해주어야한다. 나의 경우 배포된 lambda function을 실행하는 role을 지정해줘야했기 때문에 값을 설정해주었다.

.travis.yml에서 전역변수 설정하기 vs travis console에서 환경변수 설정하기

travis ci 과정에서 전역적으로 필요하거나 환경에 따라 바뀔 수 있는 값들을 변수화하는 방법은 두 가지가 있다.

1번: .travis.yml에서 .env.global 설정하기

2번: travis 콘솔 -> settings에서 environment variables 설정하기

내 생각에 각각의 방법은 아래와 같은 상황에서 사용하면 적절할 것 같다.

1번: 어플리케이션의 버전을 업데이트함에 따라 함께 바뀔 가능성이 높은 변수

2번: 바뀔 가능성이 낮거나, 보안에 민감한 변수

2번 방법으로 설정한 변수들은 아래와 같이 빌드 과정에서 암호화되어 있는 것을 확인할 수 있다.

문제

Serverless 애플리케이션과 Cloudformation 템플릿에 대한 CI & CD 파이프라인 구축을 멋지게 완성하였다. 하지만 한 가지 문제는, 원래 나는 cloudformation이 S3의 빌드 파일의 변화가 있을 때마다 stack을 업데이트하길 기대했는데 실제론 그렇지 않았다. cloudformation은 템플릿 파일의 내용이 변경될 때만 stack을 업데이트하고 있었다.(생각해보면 당연한 것..) 그래서 travis VM에 aws cli를 설치해 lambda 함수 코드를 업데이트하는 명령어를 deploy 스텝 이후에 입력해주었다.

우선 문제를 해결하기 위해 아래와 같은 새 파이프라인을 구상했다.

아래는 코드를 푸시할 때마다 lambda 함수를 s3로부터 받아와서 업데이트할 수 있도록 travis 스크립트를 수정한 내용이다.

.travis.yml

(...생략)

before_install:
        - pip install -U pip
        - pip install --user awscli # (1)
        - mkdir -p ~/.aws
        - echo -e "[default]\naws_access_key_id = $AWS_ACCESS_KEY\naws_secret_access_key = $AWS_SECRET_KEY" > ~/.aws/credentials #(2)
        - cat ~/.aws/credentials

(...생략)

after_deploy: # (3)
        - aws lambda update-function-code
        --s3-bucket=snupa 
        --s3-key=${PROJECT_NAME}-${PACKAGE_VERSION}-all.jar
        --region=ap-northeast-2
        --function-name=[함수 이름 혹은 ARN]

설명

(1) aws-cli를 설치한다.

(2) AWS credential를 설정해준다.

(3) deploy 스텝이 끝난 후에 실행할 명령어, update-function-code 명령으로 함수를 업데이트한다.

이렇게 travis를 실행하게 되면 아래처럼 성공적으로 lambda 함수 코드가 업데이트 되는 것을 확인할 수 있다.

요약

travis CI와 AWS cloudformation을 이용해 serverless 앱에 대한 CI & CD 파이프라인을 구축해보았다. cloudformation은 처음 사용해보는거라 미숙한 점이 많지만, 코드로 인프라를 관리하는 것의 매력을 확실히 느낄 수 있었고 AWS 전반에 대한 이해도 또한 한 층 높일 수 있었다.

profile
서울대학교 컴퓨터공학부 github.com/BaekGeunYoung

0개의 댓글