[Serverless Framework] serverless.yml 설정 정보 숨기기

DoonDoon·2018년 10월 3일
15
post-thumbnail

Serverless Framework serverless.yml 파일의 중요 설정값을 감춰보자!

개요


지난 9월에 참여한 Planet Hackathon 에서 Serverless Framework를 활용하여 백엔드 API를 만들었었습니다!

해커톤 종료 후 코드 공개를 위해 공개된 레파지토리에 코드를 올렸어야 했는데,
이 때 민감 정보(그냥 내가 감추고 싶은 값)를 어떻게 감출 수 있을까 찾아보다 적용한 방법을 공유합니다.

API


참고 링크
Serverless Framework에서 serverless.yml 파일의 변수의 값으로 설정할 수 있는 방법들은 꽤 많습니다

  • Environment variables
  • CLI options
  • Other properties (같은 serverless.yml 파일 내에 정의된)
  • CloudFormation Outputs
  • Variables from AWS S3
  • Variables from AWS SSM Parameter Store
  • Properties from Javascript files
  • Pseudo parameters reference (AWS cloudformation의 가상 선택자 문법)

목록 중에서 다양한 환경에 따라 동적으로 값을 설정할 수 있는건
자바스크립트 파일 읽어오기(Properties from Javascript files) 기능일 것 같습니다
이 기능을 이용한 실제 적용 방법을 설명하겠습니다.

(물론 serverless.yml 파일 내에서 custom을 이용하여 분기처리도 가능합니다!)

예제


예제코드

목표

- 데이터베이스 연결 정보 환경변수로 설정함
- 데이터베이스 연결 정보를 `serverless.yml` 에서 노출하지 않음
- stage에 따라 데이터베이스 연결 정보를 다르게 제공함
  1. 테스트를 위해 Serverless Project를 임의로 하나 생성하겠습니다
$ mkdir slsTest && cd slsTest
$ yarn init -y && yarn add -D serverless
$ yarn sls create --template aws-nodejs-ecma-script -n slsTest -p tmp
$ mv ./tmp/* .
$ rm -rf ./tmp
$ yarn # 프로젝트 생성되고 추가적인 패키지 설치를 위함
$ mkdir src && mv first.js second.js ./src # src 하위로 소스 코드 이동
$ mkdir config && touch ./config/config.js
  1. serverless.yml 파일 설정을 다음과 같이 한다고 가정하겠습니다
  service:
    name: slsTest

  # Add the serverless-webpack plugin
  plugins:
  - serverless-webpack

  custom:
    STAGE: ${self:provider.stage} # 현재 스테이지 별로 데이터베이스 접속 정보를 달리하기 위함
    DB_CONFIG: ${file(./config/config.js):DB_CONFIG} # config.js 에서 가져올 데이터 베이스 접속정보

  provider:
    name: aws
    runtime: nodejs8.10 # 현재 AWS Lambda node.js 런타임 v8.10 까지 지원, 기본값은 6.10
    stage: ${opt:stage, 'dev'} # -s 옵션을 받으면 사용하고, 그렇지 않으면 기본 dev 스테이지 사용
    region: ap-northeast-2
    environment:
      STAGE: ${self:provider.stage}
      # custom 항목 중 DB_CONFIG를 읽어서 스테이지 별로 해당하는 값을 불러옴
      DB_HOST: ${self:custom.DB_CONFIG.${self:custom.STAGE}.DB_HOST} 
      DB_USER: ${self:custom.DB_CONFIG.${self:custom.STAGE}.DB_USER}
      DB_PASSWORD: ${self:custom.DB_CONFIG.${self:custom.STAGE}.DB_PASSWORD}

  functions:
    first:
      handler: src/first.hello # 디렉토리 내의 파일을 실행시킬때의 경로 설정
    second:
      handler: src/second.hello
      events:
      - http:
          method: get
          path: second
  1. /config/config.js 파일을 생성하고, 아래와 같이 작성합니다
module.exports.DATABASE_CONFIG = (serverless) => ({
    dev: {
      DB_HOST: 'localhost',
      DB_USER: 'scott',
      DB_PASSWORD: 'tiger'
    },
    prod: {
      DB_HOST: 'fake.database.com',
      DB_USER: 'scott2',
      DB_PASSWORD: 'tiger2'
    }
});
1. commonJS 형태로 작성합니다
2. 반드시 함수로 작성되어야 합니다!
3. 암시적으로 serverless 인자를 받을 수 있습니다 (serverless API 기능을 사용할 수 있습니다)
	- `node_modules/serverless/lib/Serverless.js` 코드를 참고하세요
    - `serverless.cli.consoleLog()` 와 같은 명령어가 사용 가능 합니다.
  1. 설정 값이 잘 적용되었는지 확인할 수 있도록 src/first.js 파일을 조금 수정합니다
// eslint-disable-next-line import/prefer-default-export
export const hello = (event, context, callback) => {
  // 환경 변수에서 데이터 베이스 접속 정보를 가져옴
  const {DB_HOST, DB_USER, DB_PASSWORD} = process.env;
  const p = new Promise((resolve) => {
    resolve('success');
  });
  p
    .then(() => callback(null, {DB_HOST, DB_USER, DB_PASSWORD})) // 출력
    .catch(e => callback(e));
};
  1. 결과를 확인합니다.
    - stage: prod (prod 에서 사용할 데이터베이스 설정값이 잘 출력됩니다)
    image.png
  • stage: dev (dev 에서 사용할 데이터베이스 설정값이 잘 출력됩니다)
    image.png
  1. config.js 파일이 공유되지 않도록.gitignore에 추가합니다
  • /config/config.js
  1. config.js 파일의 존재와 어떤 항목을 사용하는지 알 수 있도록 config.sample.js 파일을 작성합니다
    // 이 파일은 샘플입니다. 파일을 config.js 로 변경 후 사용해 주세요
    // 필요한 항목을 기입해야 정상적으로 실행됩니다
    module.exports.DB_CONFIG = (serverless) => ({
      dev: {
        DB_HOST: '<Please fill in this information>',
        DB_USER: '<Please fill in this information>',
        DB_PASSWORD: '<Please fill in this information>'
      },
      prod: {
        DB_HOST: '<Please fill in this information>',
        DB_USER: '<Please fill in this information>',
        DB_PASSWORD: '<Please fill in this information>'
      }
    });

설정 끗!

장•단점


장점

  1. 동적으로 값을 생성할 수 있습니다 (예제는 그렇지 않았지만)
    • 이를테면 OS 환경에 따라 값을 생성한다거나 하는 것이 가능합니다 👍
  2. 비동기로 값을 받아와서 매핑해야 하는 경우에도, Promise.resolve값을 반환하면 가능합니다
    • 데이터베이스에 설정 값이 있어 이를 조회해 전달한다거나 하는 경우 등

단점

  1. 협업시 설정 값을 추가할 경우, 추가된 값을 다른 사람에게 잊지 않고 공지해야 한다
    • config.sample.js 파일은 공유되지만, 유심히 읽지 않을 수 있기 때문에 ㅠㅠ
    • 물론 실제로 환경변수로 사용할 값이 자주 추가되거나 변경되지는 않겠지만...
    • 이러한 문제는 .env 파일로 관리하더라도 마찬가지일듯 싶습니다! 👻
    • 값의 추가나 변경의 경우, 반드시 sample 파일에도 업데이트 해야 합니다!
    • 환경 변수 키의 변경이 있을 경우, 값을 사용하고 있는 코드를 일일이 찾아 리팩토링 해야합니다
  2. (당연하지만) 익숙하지 않다면, 도리어 설정이 복잡해 보일 수 있습니다 😭
  3. 자바스크립트로만 작성해야 합니다

결론


환경 변수를 자바스크립트 코드로 작성해 동적으로 관리할 수 있다는 건 장점이라고 생각합니다!
(dotenv를 추가로 설치 하지 않아도 되고)

어찌되었건 serverless.yml 파일은 무심코 공개 레파지토리에 올릴 경우, 중요한 정보가 많이 노출될 수 있으므로 이러한 방법이 아니더라도, 중요한 정보는 잘 숨겨서 관리하도록 해야겠습니다!

내용 중 잘못된 부분은 언제든 지적해주세요!
읽어주셔서 감사합니당 😀

profile
Espresso Cream Cat DD

6개의 댓글

comment-user-thumbnail
2018년 10월 3일

좋은 정보 감사합니다!

1개의 답글
comment-user-thumbnail
2018년 10월 4일

저도 .json 에 관리를 했었는데
CircleCI 에서 환경변수를 사용해야 됐어서..
${env:SECRET_KEY} 이런식으로 사용하고
만약에 로컬에서 배포 할 수 밖에 없는 상황엔
https://github.com/velopert/velog/blob/master/velog-backend/scripts/loadenv
이런 스크립트를 사용해서 .env.production 파일에 있는 변수들을 불러오게했어요.

https://www.npmjs.com/package/serverless-dotenv-plugin

이런 솔루션도 있네요..!

아.. 그리고 벨로그 댓글에 링크 있으면 a 태그로 변환 시켜야겠네요 ㅋㅋ

1개의 답글
comment-user-thumbnail
2018년 12월 10일

환경변수를 dotEnv처럼 전역으로 쓰고 싶진 않았는데,
해당 글 참조하여 실 서비스에 적용 하였습니다
감사합니다.

1개의 답글