[AWS] ECS OIDC Connect와 Managed Instance 테스트

Github OIDC 연동으로 AWS ECS에 자동 배포하는 방법을 소개합니다. Managed Instance로 인스턴스 관리를 자동화하고, 카나리 배포로 안전하게 배포하며, 문제 발생 시 롤백 가능합니다.

[AWS] ECS OIDC Connect와 Managed Instance 테스트
Photo by Stephen Dawson / Unsplash

개요

Github Action으로 AWS ECS Deploy 하는 방법과 Managed Instance 테스트한 내용을 정리했습니다.

Github OIDC 연동하기

Github Action → AWS 연동

[IAM] > [ID 제공업체] > [공급자 추가]

Notion Image

이후 연동할 역할을 추가합니다

  • Github Organization은 ID를 입력합니다
    • 개인 계정일 경우 Github ID 등록
  • ID 제공업체 → token.actions.githubusercontent.com
  • Audience → sts.amazonaws.com
Notion Image
Notion Image
  • AmazonEC2ContainerRegistryPowerUser 권장합니다(ECR).
    • Push/Pull 모두 가능
Notion Image

ECS Cluster 생성

Managed Instance Cluster 생성

ECS Managed Instance의 경우 인프라 역할을 사용합니다.

  • Launch Template, Fleet 기반 인스턴스 실행(Spot)을 자동으로 관리해줍니다.
  • ASG, Launch Template을 생성할 필요 없습니다.
Notion Image
  • ecsInfrastructureRoleForManagedInstances 관리형 정책
    • 배포 및 인스턴스 관리를 위해 필요한 정책입니다.
      • AmazonECSInfrastructureRolePolicyForLoadBalancers
      • AmazonECSInfrastructureRolePolicyForManagedInstances
Notion Image

인스턴스 타입도 유연하게 설정 가능합니다.

Notion Image
Notion Image

클러스터 기본 용량 공급자 구성

Managed Instance의 경우 한 가지 이상의 용량 공급자를 사용할 수 없습니다. 즉, 온디맨드 구성과 Spot 구성은 별개로 구성되야합니다(Managed Instance 한정)

Notion Image

Task Definition

GPU 테스트를 위해 아래와 같이 구성했습니다.

{
  "compatibilities": [
    "EC2",
    "MANAGED_INSTANCES"
  ],
  "containerDefinitions": [
    {
      "cpu": 0,
      "environment": [],
      "essential": true,
      "image": "<AWS_ACCOUNT_ID>.dkr.ecr.ap-northeast-2.amazonaws.com/ecs-gpu-test:latest",
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/ecs-gpu-test",
          "awslogs-create-group": "true",
          "awslogs-region": "ap-northeast-2",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "mountPoints": [],
      "name": "gpu-test",
      "portMappings": [
        {
          "containerPort": 8080,
          "hostPort": 8080,
          "protocol": "tcp"
        }
      ],
      "resourceRequirements": [
        {
          "type": "GPU",
          "value": "1"
        }
      ],
      "systemControls": [],
      "volumesFrom": []
    }
  ],
  "cpu": "1024",
  "executionRoleArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/ecsTaskExecutionRole",
  "family": "ecs-gpu-test",
  "memory": "2048",
  "networkMode": "awsvpc",
  "placementConstraints": [],
  "registeredAt": "2026-01-05T05:17:20.549Z",
  "registeredBy": "arn:aws:sts::<AWS_ACCOUNT_ID>:assumed-role/<USER>",
  "requiresAttributes": [
    {
      "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
    },
    {
      "name": "ecs.capability.execution-role-awslogs"
    },
    {
      "name": "com.amazonaws.ecs.capability.ecr-auth"
    },
    {
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
    },
    {
      "name": "ecs.capability.execution-role-ecr-pull"
    },
    {
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
    },
    {
      "name": "ecs.capability.task-eni"
    },
    {
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.29"
    }
  ],
  "requiresCompatibilities": [
    "EC2"
  ],
  "revision": 2,
  "status": "ACTIVE",
  "taskDefinitionArn": "arn:aws:ecs:ap-northeast-2:<AWS_ACCOUNT_ID>:task-definition/ecs-gpu-test:2",
  "volumes": [],
  "tags": []
}

Github Action workflow

이제 생성된 ARN으로 Github Action Workflow를 구축합니다.

또한 Github Action로 AWS 서비스와 통합하고 있으므로 공식 Repo를 참고하면 좋습니다.

name: Build, Push and Deploy to ECS

on:
  push:
    branches:
      - main
    tags:
      - 'v*'

concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: false # 충돌 방지

env:
  AWS_REGION: ap-northeast-2
  ECR_REPOSITORY: ecs-gpu-test
  ECS_CLUSTER: bjchoi-ecs-cluster
  ECS_SERVICE: ecs-gpu-service
  CONTAINER_NAME: gpu-test

permissions:
  id-token: write
  contents: read

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    outputs:
      image_uri: ${{ steps.image.outputs.uri }}

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

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::{AWS_ACCOUNT_ID}:role/{ROLE_NAME}
          aws-region: ${{ env.AWS_REGION }}

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

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

      - name: Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}
          tags: |
            type=sha,prefix=,format=short
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}

      - name: Build and push
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          provenance: false

      - name: Output image URI
        id: image
        run: |
          SHORT_SHA=${GITHUB_SHA::7}
          echo "uri=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:$SHORT_SHA" >> $GITHUB_OUTPUT

  deploy:
    runs-on: ubuntu-latest
    needs: build-and-push

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

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::{AWS_ACCOUNT_ID}:role/{ROLE_NAME}
          aws-region: ${{ env.AWS_REGION }}

      - name: Render Amazon ECS task definition
        id: render-task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition.json
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ needs.build-and-push.outputs.image_uri }}

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

ECS Service 배포 진행 사항

ECS 서비스 > 상태 및 지표에서 배포를 확인 가능합니다.

  • 트래픽을 받고 있는 컨테이너 확인 가능
  • 트래픽 가중치 확인 가능
  • 대상 그룹으로 확인 가능
Notion Image
Notion Image

ECS는 카나리 배포와 선형 배포 2가지 방식이 존재합니다.

  • 카나리 배포
    • Deployment bake time 기간 동안 가중치 기반 트래픽 분배
Notion Image
  • 선형 배포
    • Deployment bake time 기간 내 백분율로 트래픽을 높혀갑니다.
Notion Image
Notion Image

기존의 카나리 배포가 ECS에서 선형 배포라고 보시면 됩니다.

Deployment bake time가 완료될 때 까지 언제든지 롤백 가능합니다.

Notion Image
Notion Image
Notion Image

Approve 설정

[Settings][Environments][production]

deploy:
    runs-on: ubuntu-latest
    needs: build-and-push
    environment: production
...

아래 옵션 활성화가 필요합니다.

Notion Image

배포 설정을 완료하게 되면 아래와 같이 Review Pending deployments가 추가 됩니다.

  • 트래픽 전환 자체를 승인하는 과정이 아닌 workflow 흐름만 제어하는 방식입니다.
Notion Image
Notion Image

배포 중 문제 발생시 서킷 브레이커와 롤백 수행으로 언제든지 이전 정상 서비스 구성으로 전환 가능합니다.

Notion Image

정리

ECS Managed Instance는 EKS 수준의 노드 관리 서비스를 제공합니다.

장점은 다음과 같습니다.

  1. AWS가 EC2 프로비저닝/패칭/스케일링 자동 관리
  2. Fargate의 편리함 + EC2의 유연성 (GPU, EC2 타입 등)

단점 또한 매우 크게 느껴지는 편입니다.

  1. EC2 비용과 별도로 추가 청구(Managed Instance 당 추가 비용)
  2. 단일 Capacity Provider → 유연한 확장 불가능
    1. 영어로 설정 시 확인 가능
Notion Image