콘텐츠로 이동

2026-05-28 — ECS 클러스터 사용 현황 감사 (지난 7일)

한 줄 요약

us-east-1 리전의 ECS 클러스터 40개 중 지난 7일간 실제로 태스크가 실행된 클러스터는 1개(compositet3-prod-240531-cluster)였다는 사실을 CloudWatch 메트릭과 ECS 이벤트로 교차검증한 절차.

  • 대상 계정: 790590917886 (reco-kyle 프로파일)
  • 대상 리전: us-east-1
  • 작업일: 2026-05-28

1. 배경/목적

미사용 ECS 클러스터를 정리하기 전 단계로, "최근 사용 흔적이 있는가"라는 기준으로 클러스터를 분류해야 했다. "있다/없다"를 일관되게 판정할 수 있는 지표가 필요했다.

무엇을 "사용 중"으로 볼 것인가

후보 신호 판정에 적합한가 이유
describe-clustersrunningTasksCount 현재 시점 스냅샷일 뿐
서비스의 desiredCount > 0 설정값이지 활동 증거가 아님
서비스 이벤트 개수 has reached a steady state.가 6시간마다 자동 발생해 desired=0인 클러스터도 100개씩 쌓임
CloudWatch AWS/ECS CPUUtilization 데이터포인트 태스크가 실제로 실행되어야만 점이 찍힘
CloudWatch AWS/ECS CPUReservation EC2 launch type에서 컨테이너 인스턴스가 예약돼 있을 때만 점이 찍힘
ECS/ContainerInsights RunningTaskCount ✅ (단, Container Insights 활성화 시)

결론: CPUUtilization 데이터포인트 존재 여부를 1차 지표로 삼는다.


2. 절차

2.1 클러스터 목록 가져오기

aws ecs list-clusters --region us-east-1 --profile reco-kyle --output json

2.2 각 클러스터에 대해 지난 7일 CPUUtilization 데이터포인트 수 확인

스크립트(check_ecs.sh):

#!/bin/bash
PROFILE=reco-kyle
REGION=us-east-1
START=$(date -u -v-7d '+%Y-%m-%dT%H:%M:%SZ')
END=$(date -u '+%Y-%m-%dT%H:%M:%SZ')

check_cluster() {
  local arn=$1
  local name=$(echo "$arn" | awk -F/ '{print $NF}')

  # 클러스터 레벨 CPUUtilization (EC2 launch type)
  local cluster_dp=$(aws cloudwatch get-metric-statistics \
    --profile $PROFILE --region $REGION \
    --namespace AWS/ECS --metric-name CPUUtilization \
    --dimensions Name=ClusterName,Value="$name" \
    --start-time "$START" --end-time "$END" \
    --period 86400 --statistics Maximum \
    --query 'length(Datapoints)' --output text 2>/dev/null)

  # Fargate 등 서비스 레벨 메트릭이 따로 있는 경우 보강
  local svc_dp=0
  if [ "$cluster_dp" = "0" ] || [ -z "$cluster_dp" ]; then
    local services=$(aws cloudwatch list-metrics \
      --profile $PROFILE --region $REGION \
      --namespace AWS/ECS --metric-name CPUUtilization \
      --dimensions Name=ClusterName,Value="$name" \
      --query 'Metrics[?length(Dimensions)==`2`].Dimensions[?Name==`ServiceName`].Value' \
      --output text 2>/dev/null | tr '\t' '\n' | sort -u)
    for svc in $services; do
      [ -z "$svc" ] && continue
      local dp=$(aws cloudwatch get-metric-statistics \
        --profile $PROFILE --region $REGION \
        --namespace AWS/ECS --metric-name CPUUtilization \
        --dimensions Name=ClusterName,Value="$name" Name=ServiceName,Value="$svc" \
        --start-time "$START" --end-time "$END" \
        --period 86400 --statistics Maximum \
        --query 'length(Datapoints)' --output text 2>/dev/null)
      if [ -n "$dp" ] && [ "$dp" != "0" ]; then
        svc_dp=$((svc_dp + dp))
        break
      fi
    done
  fi

  local total=$((${cluster_dp:-0} + svc_dp))
  if [ "$total" -gt 0 ]; then
    echo "USED|$name|cluster_dp=${cluster_dp:-0}|svc_dp=$svc_dp"
  else
    echo "IDLE|$name|cluster_dp=0|svc_dp=0"
  fi
}

export -f check_cluster
export PROFILE REGION START END

aws ecs list-clusters --profile $PROFILE --region $REGION \
  --query 'clusterArns[]' --output text | tr '\t' '\n' | \
  xargs -P 10 -I{} bash -c 'check_cluster "$@"' _ {}

2.3 (선택) 교차검증

ContainerInsights, CPUReservation, 그리고 현재 상태로 결과를 재확인:

# ContainerInsights RunningTaskCount (활성화돼 있을 때만 데이터 존재)
aws cloudwatch get-metric-statistics \
  --profile reco-kyle --region us-east-1 \
  --namespace ECS/ContainerInsights --metric-name RunningTaskCount \
  --dimensions Name=ClusterName,Value="$NAME" \
  --start-time "$START" --end-time "$END" \
  --period 86400 --statistics Maximum

# 현재 클러스터 스냅샷
aws ecs describe-clusters --profile reco-kyle --region us-east-1 \
  --clusters "$NAME" \
  --query 'clusters[0].[runningTasksCount,pendingTasksCount,activeServicesCount,registeredContainerInstancesCount]'

# 서비스 이벤트 중 실제 활동 메시지가 있는지 보기
aws ecs describe-services --profile reco-kyle --region us-east-1 \
  --cluster "$NAME" --services "$SVC" \
  --query 'services[0].events[0:10].[createdAt,message]' --output table

이벤트 내용으로 한 번 더 거르기

서비스 이벤트가 "started 1 tasks", "stopped N tasks", "unable to place a task" 같이 상태 변화 메시지면 활동 신호, "has reached a steady state."만 반복되면 노이즈로 간주한다.


3. 검증

이 절차에서 확인해야 할 출력:

  • USED|... 라인의 클러스터 이름 목록 = 지난 7일 사용된 클러스터
  • 의심 가는 클러스터는 직접 CPUUtilization 값까지 조회해 0이 아닌 Maximum이 있었는지 확인:
aws cloudwatch get-metric-statistics \
  --profile reco-kyle --region us-east-1 \
  --namespace AWS/ECS --metric-name CPUUtilization \
  --dimensions Name=ClusterName,Value=compositet3-prod-240531-cluster \
  --start-time "$START" --end-time "$END" \
  --period 86400 --statistics Maximum Average

본 작업 결과: compositet3-prod-240531-cluster만 7일 모두 데이터포인트 존재, Maximum CPU ~26%까지 관측됨.


4. 함정과 노하우

  • get-metric-statistics --period 86400은 86400초(1일) 단위로 집계한다. 더 짧은 활동(예: 30분짜리 잡)도 잡고 싶다면 --period 3600(1시간)을 쓰고 datapoint 개수 대신 Sum/Maximum 값을 본다.
  • 서비스 이벤트 개수는 활동 신호가 아니다. ECS는 desired=0인 서비스에도 ~6시간 주기로 steady state 이벤트를 남긴다. 7일이면 ~28개 + 다른 자동 이벤트로 90~100개가 흔하게 쌓인다.
  • Fargate-only 서비스는 클러스터 디멘션 단독 CPUUtilization이 안 찍힐 수 있다. ClusterName + ServiceName 디멘션 메트릭까지 함께 보아야 한다.
  • Container Insights는 클러스터마다 켜져 있지 않을 수 있다. ECS/ContainerInsights 네임스페이스에 데이터가 없다고 "사용 안 됨"으로 단정하면 안 된다.

5. 롤백

이 작업은 읽기 전용 감사라 롤백이 필요 없다.