ECS 배포 대기는 어디서 새고 있었나 — Early Success Criteria가 설정만으로 끝나지 않은 이유

ECS 배포 대기는 어디서 새고 있었나 — Early Success Criteria가 설정만으로 끝나지 않은 이유

지난 글에서 Blacksmith 캐시로 Docker 빌드를 30초대까지 내리고 나서 "남은 병목은 DB 마이그레이션과 ECS 안정화"라고 적었다. 이후 운영 배포 한 번에 5분이 걸리면 그중 3분 반은 ECS가 새 태스크를 띄우고 기존 태스크를 정리하는 시간이었다. 빌드는 더 줄일 게 없었고, ECS의 배포 완료 판정을 살펴봐야 했다.

마침 AWS가 롤링 배포에 Early Success Criteria를 추가했다.1 새 태스크가 지정한 비율만큼 healthy가 되면 배포를 성공으로 선언하고, 기존 태스크 정리는 배포 수명주기와 분리한다. 우리 파이프라인에서도 같은 결과가 나오는지 개발 환경부터 측정했다. 설정 하나로 끝나는 일은 아니었다.

배포 4분 중 ECS가 3분 반을 쓴다

측정 대상은 한 고객사 서비스의 apiweb이다. 둘 다 Fargate 롤링 배포이고 ALB 타겟그룹 헬스체크는 5초 간격, 2회 통과, 등록 해제 지연 5초다. GitHub Actions 워크플로는 이미지를 올린 뒤 amazon-ecs-deploy-task-definition으로 서비스를 갱신하고, 자체 Monitor 단계가 describe-services를 폴링하며 Slack에 진행 상황을 올린다.

적용 전 운영 api 배포 한 건의 ECS 이벤트를 시간순으로 놓으면 이렇다. 태스크 2개짜리 서비스다.

경과이벤트
0:00update-service
0:21 / 0:55새 태스크 2개 시작
1:05ALB 타겟 등록, 약 10초 뒤 healthy
2:02 / 2:25기존 태스크 2개 순차 중지·드레인
3:34deployment completed

새 태스크는 1분 남짓이면 트래픽을 받을 준비가 된다. 나머지 2분 반은 기존 태스크를 내리고 ECS가 "안정"이라고 판정하기까지의 시간이다. 개발 환경의 태스크 1개 서비스도 구조는 같았다. 과거 6회 배포에서 Monitor 단계는 api 180–198초, web 165–200초였고 같은 날 다시 잰 기준선은 197초와 189초였다.

Early Success Criteria는 무엇을 앞당기나

ECS는 원래 네 조건이 모두 맞아야 롤링 배포를 완료한다. 새 리비전이 desired count의 100%로 healthy, 롤백 미발생, 알람 기반 롤백을 쓰면 bake time 경과, 그리고 기존 리비전 태스크 정리다.2 Early Success Criteria는 목표 리비전의 healthy 비율과 소스 리비전 정리 시점을 바꾼다.

  • healthyPercent: 새 리비전에서 이 비율만큼의 태스크가 healthy가 되면 완료로 본다. 필요한 태스크 수는 올림으로 계산하며, 나머지 태스크는 배포 밖에서 일반 스케일링으로 채운다. 값은 서비스의 minimumHealthyPercent 이상 100 이하여야 한다.
  • sourceServiceRevisionCleanup: BLOCKING이면 기존 태스크 정리를 끝내고 완료를 선언한다. DEFERRED면 완료를 먼저 선언하고 정리는 비동기로 최대 2주 동안 시도한다.

이번 적용에서는 태스크 1개 서비스와 운영 보수안의 healthyPercent를 100으로 유지하고, DEFERRED로 기존 태스크 정리 대기만 분리했다. 태스크 2개 서비스에서는 50% 구성도 따로 측정했다.

실제로 바꾼 세 지점

변경 지점기존변경 후
ECS 서비스기존 태스크 정리 뒤 배포 완료새 태스크가 healthy가 되면 완료, 정리는 DEFERRED
배포 워크플로서비스 안정화 완료 신호를 기다림조기 성공 판정을 확인하면 종료
Terraform 배포 역할기존 ECS 배포 권한만 허용서비스 배포 상태 조회 권한 추가

설정만 켰더니 CI 시간은 그대로였다

개발 환경의 api·web 두 서비스에 healthyPercent 100, DEFERRED를 넣고 배포를 두 번씩 돌렸다. 로컬 AWS CLI 2.35.7에는 아직 earlySuccessCriteria 파라미터가 없어서 boto3 1.43.89로 update_service를 호출했다. 기존 deploymentConfiguration을 통째로 다시 보내야 하므로 서킷 브레이커 설정을 빠뜨리지 않도록 현재 값을 읽어서 합쳤다.

cur = ecs.describe_services(cluster=cluster, services=[svc])["services"][0]["deploymentConfiguration"]
ecs.update_service(
    cluster=cluster, service=svc,
    deploymentConfiguration={
        "deploymentCircuitBreaker": cur["deploymentCircuitBreaker"],
        "maximumPercent": cur["maximumPercent"],
        "minimumHealthyPercent": cur["minimumHealthyPercent"],
        "strategy": "ROLLING",
        "bakeTimeInMinutes": cur.get("bakeTimeInMinutes", 0),
        "earlySuccessCriteria": {
            "enable": True,
            "healthyPercent": 100,
            "sourceServiceRevisionCleanup": "DEFERRED",
        },
    },
)

하지만 CI 대기 시간은 거의 줄지 않았다. Monitor 단계가 api 181초·181초, web 183초·150초였다. 기준선과 구분하기 어려웠다. 서비스 배포 API의 기록은 달랐다. describe-service-deployments로 본 배포는 api 139초에서 111초·104초, web 106초에서 101초·84초로 줄었고 statusReason에 "Service deployment met early success criteria."가 찍혀 있었다. ECS는 약속대로 일찍 끝냈는데 워크플로가 그 상태를 읽지 못했다.

원인은 워크플로가 보는 신호였다. Monitor 단계는 describe-services 응답의 deployments[].rolloutStateCOMPLETED가 되길 기다린다. 이 값은 기존 태스크가 정리된 뒤에야 바뀐다. Early Success Criteria가 앞당기는 것은 서비스 배포(service deployment) 리소스의 상태이고, 기존 deployments 배열은 그 뒤를 따라온다. 같은 배포에서 서비스 배포 finishedAt은 15:56:19, rolloutStateCOMPLETED로 바뀐 시각은 15:57:16이었다. 57초 차이다. 개발자 가이드도 관측 방법으로 DescribeServiceDeploymentsListServiceDeploymentsSUCCESSFUL 필터를 안내한다.2

그래서 Monitor 단계를 고쳤다. 이 실행이 만든 서비스 배포만 보도록 시작 시각으로 거르고, SUCCESSFUL이면 바로 끝낸다. 롤백 계열 상태면 바로 실패로 처리하고 rolloutState는 대체 경로로 남겼다.

DEPLOY_STARTED_ISO=$(date -u -d "@$((MONITOR_START - 120))" +%Y-%m-%dT%H:%M:%SZ)
while true; do
  SD_STATUS=$(aws ecs list-service-deployments \
    --cluster "$ECS_CLUSTER" --service "$ECS_SERVICE" \
    --created-at "after=$DEPLOY_STARTED_ISO" \
    --query 'serviceDeployments[0].status' --output text 2>/dev/null || echo "")
  [ "$SD_STATUS" = "SUCCESSFUL" ] && exit 0
  case "$SD_STATUS" in
    ROLLBACK_*|STOPPED|STOP_REQUESTED) exit 1 ;;
  esac
  # 기존 rolloutState 폴링은 그대로 둔다
  sleep 5
done

두 번째 함정, 권한 없는 호출은 조용히 실패한다

고친 워크플로로 세 번째 배포를 돌렸다. api 191초, web 168초로 다시 기준선과 비슷했다. 실행 로그에는 오류가 없었다. 원인은 배포 역할의 IAM 권한 누락이었고, 위 스크립트의 2>/dev/null이 오류를 숨겼다. 로컬에서는 같은 명령이 SUCCESSFUL을 돌려줬으니 명령 자체는 맞았고, 다른 것은 자격증명뿐이었다.

배포용 GitHub OIDC 역할의 인라인 정책을 열어 보니 ECS 액션이 네 개였다. DescribeServices, DescribeTaskDefinition, RegisterTaskDefinition, UpdateService다. 최소 권한으로 좁힌 정책이 새 API를 막고 있었다. ListServiceDeployments는 서비스 ARN에, DescribeServiceDeploymentsservice-deployment/<cluster>/<service>/* ARN에 허용을 추가했다. 이 역할과 ECS 서비스 설정은 Terraform으로 관리한다. 수동 변경도 Terraform 코드에 옮겨 다음 apply에서 되돌아가지 않게 했다.

비슷한 과제를 다루고 계신가요?기술 과제 검토 요청

세 군데를 고친 뒤의 숫자

권한을 넣고 네 번째 배포를 돌리자 Monitor 대기 시간이 줄었다. 핵심 전후 수치를 아래 표로 분리했다.

개발 서비스 지표기존 → 변경 후단축
api ECS 대기197초 → 120초77초 · 39%
web ECS 대기189초 → 111초78초 · 41%
api 전체 워크플로275초 → 189초86초 · 31%
web 전체 워크플로233초 → 159초74초 · 32%

각 변경을 순서대로 적용한 결과도 함께 놓으면 신호와 권한이 왜 필요했는지 더 분명해진다.

회차바꾼 것api Monitorweb Monitor
과거 6회 평균없음192초178초
기준선(당일)없음197초189초
1회차ECS 설정181초183초
2회차ECS 설정181초150초
3회차+ 워크플로 수정191초168초
4회차+ IAM 권한120초111초

워크플로 전체 시간은 api 275초에서 189초, web 233초에서 159초가 됐다. 기준선 대비 각각 31%, 32%다. 서비스 배포 자체는 api 117초, web 98초로 끝났고 둘 다 조기 성공 판정을 받았다. 남은 120초 중 100초 남짓은 Fargate가 태스크를 띄우고 애플리케이션이 부팅해 헬스체크를 통과하는 시간이라 이 기능으로는 더 줄지 않는다.

운영 api·web은 태스크가 2개다. 여기에는 보수안으로 healthyPercent 100, DEFERRED를 그대로 두고 minimumHealthyPercent 100을 유지했다. 기존 태스크 정리 대기만 분리하는 구성이다.

운영적용 전(최근 5회)보수안 1회차보수안 2회차50% 구성
api Monitor / 전체215–247초 / 276–331초156초 / 227초164초 / 246초135초 / 208초
web Monitor / 전체172–207초 / 250–287초135초 / 205초149초 / 196초112초 / 151초

두 번 모두 서비스 배포는 조기 성공으로 끝났다(api 141초·149초, web 123초·133초). 배포 직후 두 서비스 모두 running 2 / desired 2였고, DEFERRED 정리가 끝난 뒤에는 기존 리비전 태스크가 남지 않았다. 태스크 2개에서 healthyPercent 100을 유지했으니 줄어든 것은 기존 태스크 정리 대기뿐이다. 이것만으로 Monitor 단계가 기준선 중앙값보다 api 약 70초, web 약 55초 짧아졌다. 전체 워크플로는 api 약 300초에서 227–246초, web 약 270초에서 196–205초가 됐다.

같은 날 나머지 서비스에도 같은 절차를 적용했다. 태스크 1개짜리 counsel(운영·개발)과 개발 adminhealthyPercent 100, DEFERRED만 켰다. Monitor 단계는 운영 counsel 164–181초에서 106초, 개발 counsel 164–197초에서 127초, 개발 admin 164–180초에서 105초가 됐다. 정리 대기만 분리해도 태스크 1개 서비스에서 30–40% 줄었다.

태스크 수에 따라 적용값을 고른다

태스크 수에 따라 적용한 값과 실측 결과를 한 표로 정리했다.

구성적용값확인된 결과
태스크 1개100 + DEFERREDMonitor 30–40% 단축
태스크 2개, 보수안100 + DEFERREDapi 약 70초, web 약 55초 단축
태스크 2개, 50% 구성50 + DEFERRED (minimumHealthyPercent 50)api 135초, web 112초

보수안으로 두 번을 잰 뒤 minimumHealthyPercent 50, healthyPercent 50으로 바꿔 한 번 더 측정했다. api Monitor는 135초, web은 112초로 보수안보다 20–40초 더 짧았다. 서비스 배포는 각각 119초와 104초에 조기 성공했다.

적용 절차와 확인 방법

같은 문제를 반복하지 않도록 순서를 적어 둔다.

1. SDK 확인. aws ecs update-service --generate-cli-skeletonearlySuccessCriteria가 없으면 CLI를 올리거나 최신 boto3, 콘솔을 쓴다.

2. 서비스 설정. 기존 deploymentConfiguration을 읽어 합친 뒤 earlySuccessCriteria를 넣는다. healthyPercentminimumHealthyPercent보다 낮으면 InvalidParameterException이다.2

3. 워크플로. describe-servicesrolloutStatewait services-stable 대신 list-service-deployments의 상태를 본다.

4. Terraform과 IAM. 배포 역할에 ecs:ListServiceDeployments(서비스 ARN)와 ecs:DescribeServiceDeployments(service-deployment/... ARN)를 추가하고 Terraform 코드에도 반영한다. Terraform에서 earlySuccessCriteria를 직접 표현할 수 없으면 적용 뒤 별도 스크립트로 조기 성공 설정을 다시 넣는다.

5. 검증. 워크플로 시간이 아니라 describe-service-deploymentsstatusReason에 "met early success criteria"가 찍히는지 먼저 본다. 이 값이 찍히는데 CI가 줄지 않으면 3번이나 4번이 원인이다.


배포 파이프라인의 대기 시간은 빌드보다 완료 신호를 판정하는 방식에서 더 길어질 수 있다. 801플래닛은 클라우드 & 인프라 서비스에서 이런 배포 경로의 병목을 실측으로 찾아 고친다.

참고 자료

ECS 문서와 발표는 2026년 9월 8일 확인했다. 수치는 같은 날 개발 환경의 GitHub Actions 실행 기록, ECS 서비스 이벤트, describe-service-deployments 응답에서 읽었다. 운영 수치는 같은 날 main 브랜치 배포 3회(보수안 2회, 50% 구성 1회)에서 읽었다.

Terraform 적용 참고

적용 당시 Terraform AWS Provider 5.100의 aws_ecs_serviceearlySuccessCriteria를 지원하지 않았다. minimumHealthyPercent와 IAM 권한은 Terraform 코드에 두고, deployment_* 설정을 바꾸는 apply 뒤에는 저장소의 boto3 스크립트로 조기 성공 설정을 다시 적용했다.

출처와 각주2개펼치기접기

Footnotes

  1. AWS, Amazon ECS introduces Early Success Criteria for service deployments, 2026-09-04. healthy percent, BLOCKING·DEFERRED와 지원 리전.

  2. Amazon ECS Developer Guide, Complete Amazon ECS rolling deployments early with early success criteria. 완료 조건, healthyPercent 올림 계산과 minimumHealthyPercent 제약, DEFERRED의 2주 정리 시도, DescribeServiceDeployments·ListServiceDeployments 관측. 2 3

이 주제와 연결된 구축 서비스를 확인하세요.

클라우드 & 인프라

이런 작업을 실제 환경에 적용합니다.

엔지니어가 현재 환경과 제약을 먼저 검토하고, 필요한 경우 30분 기술 대화로 실행 범위를 정합니다.

이미 금융·헬스케어·미디어·공공의 팀들과 함께
기술 과제 검토 요청