← Back to list

ECS Fargate에 Secret 변수를 운영 환경에 맞춰 적용하기(ft. nestjs)

NestJS로 개발된 서버가 ECS에서 동작할 때, 필요한 환경 변수(secrets)들을 어떻게 관리해야 할지 고민한 내용들에 대해 정리해보겠다.

Jeongkuk Seo in sjk5766 · 2025-07-21 13:02 · 0 claps · 11.0 min read
#ecs-fargate #ecs-task-definition #github-actions #github-secret #devops
Open on Medium ↗
Wiki topics: CRY · Crypto & Web3 ☁️ · DevOps & Cloud 🔓 · Open Source

ECS Fargate에 Secret 변수를 운영 환경에 맞춰 적용하기(ft. nestjs)

NestJS로 개발된 서버가 ECS에서 동작할 때, 필요한 환경 변수(secrets)들을 어떻게 관리해야 할지 고민한 내용들에 대해 정리해보겠다.

처음에 멍 때리면서 [@nestjs/config](https://docs.nestjs.com/techniques/configuration)와 .env 구조를 생각했다가 ECS Fargate는 serverless 라서 운영 환경별로 .env 파일을 관리하는 방법은 선택할 수 없었다.

다른 방법으로 secret manager에 비밀 값을 저장해 두고 ECS 배포시 task-definition에 secret 값을 명시해서 비밀 값을 주입하는데 성공했다.

위의 ARN 정보를 task definition 파일의 containerDefinitions 속성에 secrets 값으로 arn을 셋팅해준다.

"containerDefinitions": [
   .
   .
   .
  "secrets": [
    {
      "name": "DB_HOST",
      "valueFrom": "arn:aws:secretsmanager:ap-northeast-2:11111:secret:secret-manager-name:${key}::"
    },
]

이때 한 가지 주의할 것은 valueFrom 값에 ***ARN주소:ENV키::* 형태로 넣어야 한다. 이것 때문에 몇 번 더 배포하느라 시간을 조금 잡아먹었는데 valueFrom에 아래와 같이 넣어야 한다.

(X) "valueFrom": "arn:aws:~~생략~~:secret:secret-manager-name"
(O) "valueFrom": "arn:aws:~~생략~~:secret:secret-manager-name:DB_HOST::"

그 외에 신경쓸 것은 ECS 태스크 생성시 부여한 역할에 secret manager에 대한 정책을 추가해야 한다. ECS로 배포할 때 secret manager에 접근하기 때문이다.

이렇게 ECS에 배포하고 전달한 값이 정상적으로 주입된 것을 확인했다. 그리고 잠시 멈칫했는데 개발 환경과 운영 환경을 나눈다면 task definition에 값을 어떻게 줘야할지 난감해졌다. 왜냐면 현재 task definition에 입력한 secret manager arn 값은 고정 ARN 값이기 때문이다.

찾아보니 테라폼이나 CloudFomation을 사용하면 이런 부분도 지원할 수 있다는데 아직 이 기술들을 사용하기엔 시기상조였다. 그렇다면 선택할 수 있는 방법은 아래 세가지였다.

  1. 서버가 시작할때 @aws-sdk/client-secrets-manager를 이용해 환경에 따라 다른 secret 값을 가져와 변수 값을 셋팅하고 서버를 시작하는 방법
  2. Secret Manager에 등록된 변수를 가리키는 ARN 값을 Github Secret에 저장하고 task definition을 이용해 환경에 맞게 동적으로 변수를 주입하는 방법
  3. 2번에서 Secret Manager를 제거한 방법으로 DB의 설정 정보 자체를 Github Secret에 직접 저장하는 방법이다.

1번 방법은 secret 값만 입력해두면 서버가 시작할때 process.env.NODE_ENV 같은 환경 변수 값을 체크해서 어떤 secret 값에 접근할지 판단할 수 있다. 반면에 배포가 성공하고 서버 실행 초기에 secret manager에 접근하기 때문에 aws에 장애가 발생해서 secret manager에 접근할 수 없다면 서버가 중단되거나 비정상 동작을 하는 문제가 있다.

2번과 3번에는 각각 장단점이 있다. 2번을 선택한다면 AWS DB 정보를 AWS가 관리한다는 심리적 편안함과 secrets 값에 대한 세밀한 접근제어, 패스워드가 자동으로 교체되도록 설정할 수 있지만, DB 설정은 secret manager가 보관하고 github secret이 arn을 저장해야 한다는 복잡성이 추가가 된다.

3번을 선택한다면 Github Secret이 DB 설정 정보를 저장한다로 논리가 단순해지며 불필요하게 Secret Manager의 ARN을 저장할 필요가 없다. 대신 AWS DB의 설정 정보를 Github에 등록하는게 맞을까 하는 의심과 패스워드를 자동으로 로테이션 할 수 없다는 단점이 있다.

개인적으로 단순한걸 좋아하지만 여전히 AWS DB 패스워드 정보를 Github에 저장하는게 맞을지 확신이 없어 2번 방법을 택했다. 어떻게 했는지 정리하는 걸로 마무리하겠다.

task definition에서는 환경에 따라 변하는 값을 고정값이 아니라 아래처럼 변수로 수정해둔다.

{
  "containerDefinitions": [
    {
      .
      .
      "secrets": [
        {
          "name": "DB_HOST",
          "valueFrom": "${DB_HOST_SECRET_ARN}"
        },
        {
          "name": "DB_USERNAME",
          "valueFrom": "${DB_USERNAME_SECRET_ARN}"
        },
        {
          "name": "DB_PASSWORD",
          "valueFrom": "${DB_PASSWORD_SECRET_ARN}"
        },
        {
          "name": "DB_NAME",
          "valueFrom": "${DB_NAME_SECRET_ARN}"
        }
      ]
    }
  ]
}

추가하려는 변수를 환경에 따라 secret manager에 입력한다. 아래 dev, prod 환경에 동일한 변수명(DB_HOST, DB_USERNAME…)에 운영 환경에 맞는 서로 다른 DB 설정 값들을 저장해두었다.

Github Env 설정에 production, development 환경을 만든다.

생성한 production, development 환경마다 DB ARN 정보를 저장할 변수들을 생성한다. 이때 각 변수에 저장될 값은 secret manager에 ARN 값이다. ex) full_arn:DB_HOST::

아래는 위 설정에 따른 Github Action 코드이다. 주석 3번 부터는 실제 배포 설정이니 다른 주석들을 살펴보자. 0번에서 환경을 지정하기에 1번의 secrets은 github secret의 production 정보를 가져올 것이다.

주석 1번에서 secrets 값을 환경 변수에 임시로 저장했다가 주석 2번에서 envsubst 명령에 의해 task-definition.template.json 파일의 변수들의 값이 치환되어 task-definition.json으로 수정된다.

name: Deploy to Amazon ECS

on:
  push:
    branches: ['main']
  pull_request:
    branches: ['main']
env:
  AWS_REGION: ap-northeast-2
  ECR_REPOSITORY: your_ecr_repo
  ECS_SERVICE: your_ecs_service
  ECS_CLUSTER: your_ecs_cluster
  ECS_TASK_DEFINITION: task-definition.json
  CONTAINER_NAME: your_container_name

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    # 0. Github Env를 dev, prod 어디서 가져올지 결정, 개발 환경은 development로 되어있다.
    environment: production

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ secrets.AWS_ACCESS_ROLE }}
          aws-region: ${{ env.AWS_REGION }}

      # 1. 운영 환경에 따라 secrets 값들을 환경 변수에 주입
      - name: Set environment variables for secrets
        run: |
          echo "DB_HOST_SECRET_ARN=${{ secrets.DB_HOST_SECRET_ARN }}" >> $GITHUB_ENV
          echo "DB_USERNAME_SECRET_ARN=${{ secrets.DB_USERNAME_SECRET_ARN }}" >> $GITHUB_ENV
          echo "DB_PASSWORD_SECRET_ARN=${{ secrets.DB_PASSWORD_SECRET_ARN }}" >> $GITHUB_ENV
          echo "DB_NAME_SECRET_ARN=${{ secrets.DB_NAME_SECRET_ARN }}" >> $GITHUB_ENV

      # 2. 1번에서 주입된 변수들로 템플릿에 값을 셋팅하며 파일명 수정
      - name: Render task definition from template
        run: |
          envsubst < task-definition.template.json > task-definition.json
          cat task-definition.json

      # 3. 여기서부턴 docker image로 빌드해서 배포하는 프로세스라 중요하지 않다.
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

          docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest

          echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

      - name: Fill in the new image ID in the Amazon ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ env.ECS_TASK_DEFINITION }}
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ steps.build-image.outputs.image }}

      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true

정리하면 Github의 Secret은 환경(development, production)에 따라 환경에 맞는 AWS의 Secret Manager의 ARN 값을 가리키고 있다. 또한 위에서 task definition에 valueFrom에 ${DB_PASSWORD_SECRET_ARN} 와 같이 변수로 작성해둔 부분은 Github Action 주석 2번 부분이 수행될 때 변수가 secret 값으로 치환되어 ECS에 배포되므로 환경 변수로 사용할 수 있는 원리이다.


메타데이터
post_id
6cf67ef17dbc
slug
ecs-fargate에-secret-변수를-운영-환경에-맞춰-적용하기-ft-nestjs-6cf67ef17dbc
url
https://medium.com/sjk5766/ecs-fargate%EC%97%90-secret-%EB%B3%80%EC%88%98%EB%A5%BC-%EC%9A%B4%EC%98%81-%ED%99%98%EA%B2%BD%EC%97%90-%EB%A7%9E%EC%B6%B0-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-ft-nestjs-6cf67ef17dbc
canonical_url
https://medium.com/sjk5766/ecs-fargate%EC%97%90-secret-%EB%B3%80%EC%88%98%EB%A5%BC-%EC%9A%B4%EC%98%81-%ED%99%98%EA%B2%BD%EC%97%90-%EB%A7%9E%EC%B6%B0-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-ft-nestjs-6cf67ef17dbc
author_url
https://medium.com/@wjdrnr6662
status
ok
fetched_at
2026-06-10 08:17:25