[NodeJS] .env 파일 제외하고 CodeDeploy 배포하기 (AWS Parameter Store)

주형(Jureamer)·2022년 7월 8일
0

개요

환경변수를 .env 파일에다 저장한 뒤 dotenv 모듈을 통해 꺼내 쓰는 방식은 다들 쓰실거라고 생각합니다.

저도 토이 프로젝트 할 때부터 지금까지 그렇게 사용했습니다..!

그리고 이번에 사내 프로젝트를 통해 자동 배포를 반영 하고나서 급하다는 핑계로

.env 파일을 Github Private Repository에 잠깐 저장하고 사용했습니다.

하지만 Private Repository라도 충분히 털릴 위험이 있기 때문에 다른 방식으로 많이 대체하는 걸로 알고 있습니다.

검색해 본 결과 AWS System manger에 있는 Parameter Store에 저장하여 사용하는 방식을 많이 쓰는 것 같아 이번에 적용 해보게 되었습니다.

AWS Paramter Store 적용기

기존방식 (.env)

우선 기존 방식은 이렇습니다.

.env파일을 서비스 환경에 따라 구분하여 만들어 준 뒤에 app.js에서 환경변수인 NODE_ENV에 따라 분기시켜주었습니다.

NODE_ENV는 /home/ubuntu/.profile 안에 환경마다 다르게 저장해두었습니다.

// /home/ubuntu/.profile -- EC2 development 
...

export NODE_ENV=development

...


// /home/ubuntu/.profile -- EC2 production
...

export NODE_ENV=production

...


// .env.development
DB_HOST="AAAA"
DB_USER="A"

// .env.production
DB_HOST="BBBB"
DB_USER="B"

// app.js
if (process.env.NODE_ENV === 'production') {
  logger.info(" This is Production Mode ")
  dotenv.config({ path: path.join(__dirname, '.env.production') }) // for .env.production
} else if (process.env.NODE_ENV === 'development') {
  logger.info("  This is Development Mode ")
  dotenv.config({ path: path.join(__dirname, '.env.development') }) // for .env.development
} else if (process.env.NODE_ENV === 'local') {
  logger.info("  This is Local Mode ")
  dotenv.config({ path: path.join(__dirname, '.env.local') }) // for .env.local
} else { // for .env
  logger.info(" This is NOTHING(PRODUCTION) Mode ")
  dotenv.config({ path: path.join(__dirname, '.env.production') }) // for .env.production
}

변경된 방식 (AWS Parameter Store)

우선 저장된 변수들을 파라미터 스토어에 저장을 해줍니다.

이름과 값은 필수 사항이며 .env에 저장되어있던 키, 밸류를 각각 저장하여줍니다.

이름은 /를 포함하여 계층구조로 작성할 수 있으며 예시로 저는

/프로젝트이름/서비스환경/파라미터명 순으로 작성하였습니다.

/project/production/DB_HOST
/project/production/DB_USER

/project/development/DB_HOST
/project/development/DB_USER

참고로 표준, 고급 파라미터 2가지 종류가 있는데 표준으로도 10,000개까지 무료로 사용 가능합니다.

자세한 비교는 공식 홈페이지에서 확인 할 수 있습니다.

이후 몇 가지 시도를 해봤는데 정상적으로 작동되지 않아서 삽질한 기록이 아래와 같습니다.

1. start.sh와 같은 appspec.yml의 hook단에서 스크립트 실행시키기

이 방법은 Codedeploy를 사용하는 경우 배포하는 쉘에서만 변수가 적용되어서
정상적으로 환경변수를 불러오지 못했습니다.

# start.sh 

#!/bin/bash

path=""
# proudction / development 환경에 따라 parameter path를 다르게 설정
if [ ${NODE_ENV} == "production" ] ; then
    path="/project/production/"
else
    path="/project/development/"
fi

export DB_USER=$(aws ssm get-parameters --region ap-northeast-2 --names $path"DB_USER" --query Parameters[0].Value | sed 's/"//g')
export DB_HOST=$(aws ssm get-parameters --region ap-northeast-2 --names $path"DB_HOST" --query Parameters[0].Value | sed 's/"//g')
    

su - personal -c "cd /home/ubuntu/project; npm start"

2. /etc/profile.d/codedeploy.sh에 환경변수 작성

블로깅을 하다가 codedeploy할 때 환경변수를 불러오려면 해당 파일을 생성한 뒤 변수를 등록하면

정상 작동가능하다 하여서 시도해보았는데 이 방법은 말 그대로 배포할 때 필요한 변수를 등록하는 것이기에

제 상황과는 맞지 않았습니다. 첫 npm start 시에 정상 작동되는 듯 보였지만, 이후 pm2 reload 등으로 재기동을 해보면 등록된 환경변수가 날아가 작동이 되지 않았습니다.

3. /home/ubuntu/.profile에 환경변수 작성
여기는 NODE_ENV를 저장해놨던 파일이었는데, 여기에 저장하니 EC2를 접속할 때마다
AWS에서 변수를 찾아오니 로딩 시간도 걸리고 변수가 수정되야될 때마다 EC2 환경마다 각각 수정을 시켜줘야한다는 번거로움이 생길 것 같아 포기하였습니다.

이 3가지의 시행착오를 거치고 결국 .env를 만들어 주기로 했습닌다.

4. 최종 변경 방식 .env 파일 생성 해주기
Codedeploy에서 initialize 단계에서 실행하는 initialize.sh에서 아래와 같은 코드를 추가함으로써 .env 파일을 생성해 주었습니다.

path=""
filename=""

if [ ${NODE_ENV} == "production" ] ; then
    path="/project/production/"
    filename=".env.production"
else
    path="/project/development/"
    filename=".env.development"
fi

# .env 파일 생성
touch $filename


# .env 파일에 덮어쓰기
echo DB_USER=$(aws ssm get-parameters --region ap-northeast-2 --names $path"DB_USER" --query Parameters[0].Value | sed 's/"//g') >> $filename
echo DB_HOST=$(aws ssm get-parameters --region ap-northeast-2 --names $path"DB_HOST" --query Parameters[0].Value | sed 's/"//g')  >> $filename
echo DB_USER=$(aws ssm get-parameters --region ap-northeast-2 --names $path"DB_USER" --query Parameters[0].Value | sed 's/"//g')  >> $filename
...

이렇게 적용한 게 잘 작동하고 .env파일을 저장하는 방식보다는 괜찮은 수준이지만, 아직 보안상 완벽하진 않은 느낌이 있습니다.

사실 Parameter Store 안에도 KMS를 통한 보안 문자열 옵션을 줄 수도 있고, aws-sdk를 사용해 변수를 사용할 때마다 가져오는 방법이 있기도 하기 때문입니다.

이 부분은 더 공부하여 보안적으로 보완(?) 할 수 있도록 해볼 생각입니다.

참고

profile
작게라도 꾸준히 성장하는게 목표입니다.

0개의 댓글