[LLM 경제 뉴스 요약 서비스] # 04. 서버 CI/CD (ECR, ECS)

2026. 1. 17. 01:46Programming/Project

이번 포스팅에서는 저번에 구축한 배포환경의 CI/CD를 구성해보려합니다.

작업 순서

1. 배포 전용 IAM 사용자 생성
2. Git Secret 생성
3. Git Actions Workflow 작성

 

1. 배포 전용 IAM 사용자 생성

Github Actions에서 AWS에 액션을 취하기 위해서는,

권한이 있는 IAM 사용자로 인증을 거쳐야 합니다.

 

아래 3가지 정책이 ECS/ECR 관련해서 필요한 정책들이니 추가해주세요.

2. Github Secret 생성

Workflow에서 AWS인증을 위해서는

1번에서 만든 IAM 사용자의 ACCESS_KEY와 SECRET_ACCESS_KEY가 필요합니다.

 

...
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{secrets.AWS_ACCESS_KEY}}
          aws-secret-access-key: ${{secrets.AWS_SECRET_ACCESS_KEY}}
          aws-region: us-west-2
          mask-aws-account-id: no
...

 

 

Workflow는 노출되는 코드이기에 Github의 Secret에 정의해서 사용합니다.

repo > Settings > Security > Secret and Variables > Actions

 

3. Github Actions Workflow 작성

이제 본격적으로 Workflow를 작성해보겠습니다-

env 선언

하위에 중복적으로 사용될 환경변수를 선언합니다.

name: Backend CD Dev

on:
  push:
    branches:
      - main
  workflow_dispatch:

concurrency:
  group: backend-cd-dev
  cancel-in-progress: true

jobs:
  cd:
    name: CD
    runs-on: ubuntu-latest
    env:
      ECS_CLUSTER_NAME: <> # 배포하고자 하는 cluster 명
      ECS_CONTAINER_NAME: <> # ecs -> 태스크정의 내의 컨테이너 이름
      ECS_SERVICE_NAME: <> # ecs에서 사용하는 서비스명
      TASK_DEFINITION_NAME: <> # ecs -> 태스크 정의 명
      ...

AWS 인증

앞서 추가한 secret 키를 아래와 같이 선언하고,

작업한 aws의 region을 적어줍니다.

 

여기서 mask-aws-account-id는 로그에 account id 노출 여부를 나타냅니다.
다만 저는 디버깅 단계라 no로 지정했고, 프로덕션 단계에서는 true 설정하는 것이 보안상 안전하다고 합니다-
...
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{secrets.AWS_ACCESS_KEY}}
          aws-secret-access-key: ${{secrets.AWS_SECRET_ACCESS_KEY}}
          aws-region: us-west-2
          mask-aws-account-id: no // 디버깅용
...

ECR Push

이어서는 ECR에 로그인해주고,

ECR에 만들어뒀던 레포에 연결합니다.

 

Docker의 push 액션을 사용해

repo:tag 해시코드(version)의 이미지를 push 해줍니다-

...
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Set tag
        id: tag
        run: |
          TAG=$(echo ${{ github.sha }} | cut -c1-8)
          echo TAG=$TAG
          echo "tag=$TAG" >> $GITHUB_OUTPUT

      - name: Set image.repository
        id: image
        run: |
          echo "repository=${{ steps.login-ecr.outputs.registry }}/llm-economy/backend" >> $GITHUB_OUTPUT
          
      - name: Build and push image to Amazon ECR
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.image.outputs.repository }}:${{ steps.tag.outputs.tag }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
...

TaskDefinition 다운로드

이제 ECS에 사용할 TaskDefinition을 AWS에서 가져와야합니다.

 

AWS를 조회해서 선언한 TaskDefinition을 가져오고,

task-definition.json으로 저장합니다.

...

      - name: Download Task Definition Template
        run: |
          aws ecs describe-task-definition \
            --task-definition ${{ env.TASK_DEFINITION_NAME }} \
            --query taskDefinition \
            > task-definition.json
...

ECS 배포

앞서 다운받은 TaskDefinition과 container, image를 가지고 태스크를 정의합니다.

마지막으로, 서비스를 배포해주면 끝입니다-

 

여기서 wait-for-service-stability 옵션은
태스크가 실행된 이후에 안전상태까지 기다린다는 의미입니다.
...

      - name: Render ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition.json
          container-name: ${{ env.ECS_CONTAINER_NAME }}
          image: ${{ steps.image.outputs.repository }}:${{ steps.tag.outputs.tag }}

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

 

Workflow 전체 코드
name: Backend CD Dev

on:
  push:
    branches:
      - main
  workflow_dispatch:

concurrency:
  group: backend-cd-dev
  cancel-in-progress: true

jobs:
  cd:
    name: CD
    runs-on: ubuntu-latest
    env:
      ECS_CLUSTER_NAME: <> # 배포하고자 하는 cluster 명
      ECS_CONTAINER_NAME: <> # ecs -> 태스크정의 내의 컨테이너 이름
      ECS_SERVICE_NAME: <> # ecs에서 사용하는 서비스명
      TASK_DEFINITION_NAME: <> # ecs -> 태스크 정의 명
    outputs:
      imageRepository: ${{ steps.image.outputs.repository }}
      imageTag: ${{ steps.tag.outputs.tag }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{secrets.AWS_ACCESS_KEY}}
          aws-secret-access-key: ${{secrets.AWS_SECRET_ACCESS_KEY}}
          aws-region: us-west-2
          mask-aws-account-id: no

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Set tag
        id: tag
        run: |
          TAG=$(echo ${{ github.sha }} | cut -c1-8)
          echo TAG=$TAG
          echo "tag=$TAG" >> $GITHUB_OUTPUT

      - name: Set image.repository
        id: image
        run: |
          echo "repository=${{ steps.login-ecr.outputs.registry }}/llm-economy/backend" >> $GITHUB_OUTPUT

      - name: Build and push image to Amazon ECR
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.image.outputs.repository }}:${{ steps.tag.outputs.tag }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Download Task Definition Template
        run: |
          aws ecs describe-task-definition \
            --task-definition ${{ env.TASK_DEFINITION_NAME }} \
            --query taskDefinition \
            > task-definition.json

      - name: Render ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition.json
          container-name: ${{ env.ECS_CONTAINER_NAME }}
          image: ${{ steps.image.outputs.repository }}:${{ steps.tag.outputs.tag }}

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

 

해당 Workflow로 실행 완료한 화면입니다-

 

 

간단 회고

이번엔 Actions 입장에서 AWS를 다루는 과정을 살펴봤는데
작업하던 과정을 그대로 Action이라는 형식으로 옮기는 느낌이었어요-
크게 어려운 점은 없었고,
다음 포스팅에서는 n8n 배포로 돌아오겠습니다!
반응형