AI

EKS 최적화 보고서 기능 개발(스케일링 편)

Hanhorang31 2026. 4. 4. 23:42
 

개요

필자가 바이브코딩을 통해 개발하고 있는 EKS 최적화 보고서 시스템(CloudOpsOne-EKS)에 노드 스케일링 전략을 추가하겠습니다.

자세한 소스 코드는 필자의 GitHub Repo(CloudOpsOne-EKS)에 공개되어 있습니다. 웹페이지에서 버튼 클릭만으로 생성된 보고서를 확인해 보세요.

 

 

노드 스케일링 전략 Content 선정

노드 스케일링 전략으로 devfloor9 님의 karpenter 스케일링 전략을 참고하였습니다.

 

 

노드 스케일링 전략을 주제로 보고서에 담을 Content를 다음과 같이 선정하였습니다.

  • 현재 스케일링 구성 확인
  • 스키일링 성능 분석
  • 트래픽 패턴 분석
  • 스케일링 전략
  • 최적화 가이드
  • 모니터링

 

개인적으로, 참고한 devfloor9 님의 스케일링 전략 글을 보는 것을 추천드립니다.

스케일링 전략에 대한 가이드가 자세하게 나와있습니다. 필자의 보고서에는 모든 내용을 담지 못하였습니다.

 

 

기능 개발(Feat. 바이브코딩)

메트릭 수집 → 현황 분석 → 최적화 항목 제안 → 가이드 제공으로 전체 보고서를 생성하도록 바이브코딩을 진행하였습니다.

바이브코딩 지시문은 다음과 같습니다.


## Requirements

### Requirement 1: EKS 클러스터 및 Karpenter 현황 수집

**User Story:** AWS 관리자로서, 모든 EKS 클러스터의 Karpenter 설정 및 노드 현황을 자동으로 수집하고 싶습니다. 이를 통해 스케일링 최적화 기회를 식별할 수 있습니다.

#### Acceptance Criteria

1. WHEN 사용자가 리전을 지정하면, THE System SHALL 해당 리전의 모든 EKS 클러스터를 발견하고 목록을 반환한다
2. WHEN EKS 클러스터가 발견되면, THE System SHALL 각 클러스터의 Karpenter 설치 여부를 확인한다
3. WHEN Karpenter가 설치된 클러스터를 발견하면, THE System SHALL NodePool 설정(인스턴스 타입, Spot/On-Demand 비율, Consolidation 정책)을 수집한다
4. WHEN 클러스터 정보를 수집할 때, THE System SHALL Control Plane 타입(Standard/Provisioned)을 식별한다
5. WHEN 노드 정보를 수집할 때, THE System SHALL 각 노드의 인스턴스 타입, 가용 영역, 생성 시간, 리소스 사용률을 수집한다
6. IF 클러스터 접근 권한이 없으면, THE System SHALL 해당 클러스터를 건너뛰고 경고 메시지를 기록한다

### Requirement 2: 스케일링 메트릭 및 성능 분석

**User Story:** DevOps 엔지니어로서, 각 클러스터의 스케일링 성능 메트릭을 분석하여 병목 지점을 파악하고 싶습니다.

#### Acceptance Criteria

1. WHEN 클러스터 메트릭을 수집할 때, THE System SHALL CloudWatch 또는 Prometheus에서 지난 30일간의 스케일링 이벤트를 조회한다
2. WHEN 스케일링 이벤트를 분석할 때, THE System SHALL 각 이벤트의 E2E 지연 시간(메트릭 감지 → Pod 실행)을 계산한다
3. WHEN HPA 설정을 분석할 때, THE System SHALL stabilizationWindow, scaleUpPeriod, targetUtilization 값을 수집한다
4. WHEN KEDA가 설치된 경우, THE System SHALL KEDA ScaledObject 설정 및 트리거 타입을 수집한다
5. WHEN Warm Pool(Pause Pod)이 감지되면, THE System SHALL Pause Pod 수, 리소스 할당량, 비용을 계산한다
6. WHEN 메트릭 수집 간격을 분석할 때, THE System SHALL 고해상도 메트릭(1분 미만) 사용 여부를 확인한다


### Requirement 3: 스케일링 전략 평가 및 권장사항 생성

**User Story:** 아키텍트로서, 각 워크로드에 가장 적합한 스케일링 전략(반응형/예측형/아키텍처 복원력/적정 용량)을 자동으로 평가받고 싶습니다.

#### Acceptance Criteria

1. WHEN 워크로드 트래픽 패턴을 분석할 때, THE System SHALL 지난 30일간의 메트릭에서 주기성(일간/주간)을 감지한다
2. WHEN 트래픽 패턴이 예측 가능하면(주기성 > 70%), THE System SHALL 예측형 스케일링(CronHPA) 전략을 권장한다
3. WHEN 비동기 처리가 가능한 워크로드를 감지하면, THE System SHALL 아키텍처 복원력(Queue 기반) 전략을 권장한다
4. WHEN 트래픽이 안정적이고 피크가 30% 이내이면, THE System SHALL 적정 기본 용량 증설 전략을 권장한다
5. WHEN 미션 크리티컬 SLA가 필요한 경우에만, THE System SHALL 반응형 고속화(Warm Pool + KEDA) 전략을 권장한다
6. WHEN 각 전략을 평가할 때, THE System SHALL 월간 추가 비용, 구축 복잡도, ROI 달성 조건을 함께 제시한다

### Requirement 4 : Karpenter 설정 최적화 권장사항

**User Story:** Kubernetes 관리자로서, 현재 Karpenter 설정의 문제점을 자동으로 진단하고 개선 방안을 받고 싶습니다.

#### Acceptance Criteria

1. WHEN NodePool 인스턴스 타입을 분석할 때, THE System SHALL 단일 타입만 사용 중이면 다양화를 권장한다
2. WHEN Consolidation 정책을 분석할 때, THE System SHALL WhenEmpty만 사용 중이면 WhenEmptyOrUnderutilized로 변경을 권장한다
3. WHEN Spot 인스턴스 비율을 분석할 때, THE System SHALL Spot 미사용 또는 낮은 비율(<50%)이면 증가를 권장한다
4. WHEN Disruption Budget을 분석할 때, THE System SHALL 설정되지 않았으면 버스트 트래픽 대비 설정을 권장한다
5. WHEN 구세대 인스턴스를 감지하면, THE System SHALL Graviton(ARM) 또는 최신 세대로 마이그레이션을 권장한다
6. WHEN Drift Detection이 비활성화되어 있으면, THE System SHALL 자동 노드 교체를 위한 활성화를 권장한다

### Requirement 5: HPA 및 KEDA 설정 최적화

**User Story:** 애플리케이션 개발자로서, HPA 설정이 너무 보수적이어서 스케일링이 느린 경우 최적 설정값을 제안받고 싶습니다.

#### Acceptance Criteria

1. WHEN HPA stabilizationWindow를 분석할 때, THE System SHALL 300초 이상이면 60초로 단축을 권장한다
2. WHEN HPA scaleUpPeriod를 분석할 때, THE System SHALL 60초 이상이면 15초로 단축을 권장한다
3. WHEN HPA targetUtilization을 분석할 때, THE System SHALL 80% 이상이면 60-70%로 조정을 권장한다
4. WHEN KEDA가 미설치된 이벤트 드리븐 워크로드를 감지하면, THE System SHALL KEDA 도입을 권장한다
5. WHEN 메트릭 수집 간격이 15초 이상이면, THE System SHALL 고해상도 메트릭(1-5초) 사용을 권장한다
6. WHEN Custom Metrics API가 미설정된 경우, THE System SHALL Prometheus Adapter 또는 KEDA 설정을 권장한다

### Requirement 6: 보고서 생성 및 시각화

**User Story:** 경영진으로서, EKS 스케일링 최적화 현황을 요약한 대시보드와 상세 보고서를 받고 싶습니다.

#### Acceptance Criteria

1. WHEN 보고서를 생성할 때, THE System SHALL Executive Summary(총 절감 가능 비용, 주요 권장사항 3개)를 포함한다
2. WHEN 클러스터별 분석을 표시할 때, THE System SHALL 각 클러스터의 현재 상태, 문제점, 권장사항을 구조화하여 제시한다
3. WHEN 비용 분석을 시각화할 때, THE System SHALL 현재 비용 vs 최적화 후 비용을 차트로 비교한다
4. WHEN 스케일링 성능을 시각화할 때, THE System SHALL E2E 지연 시간 분포를 히스토그램으로 표시한다
5. WHEN 권장사항을 우선순위화할 때, THE System SHALL ROI(비용 절감액 / 구축 비용)가 높은 순으로 정렬한다
6. WHEN 보고서를 출력할 때, THE System SHALL JSON, Markdown, HTML 형식을 지원한다
7. WHEN 보고서 구조를 생성할 때, THE System SHALL 다음 섹션을 포함한다: 개요 및 목적, 현황 분석(As-Is), 최적화 전략(To-Be), 기대 효과(KPI)
8. WHEN As-Is 분석을 작성할 때, THE System SHALL 기존 Cluster Autoscaler 대비 문제점(노드 그룹 경직성, 느린 프로비저닝, 유휴 리소스)을 정량화한다
9. WHEN To-Be 전략을 작성할 때, THE System SHALL 고속 스케일링, 비용 최적화, 운영 안정성 섹션으로 구분하여 제시한다
10. WHEN KPI 테이블을 생성할 때, THE System SHALL 최적화 전/후 비교(노드 Ready 시간, Pod 스케줄링 지연, 인프라 비용, 개선율)를 표 형식으로 제시한다

 

 

보고서 확인

기능 검증을 위해 보고서를 먼저 생성했습니다.

사전 Karpenter, KEDA, HPA 설정 등이 전혀 안되어 있기에 데이터 부족으로 나옵니다.

 

 

가이드 검증

보고서에 있는 가이드를 토대로 노드 스케일링 구성을 진행하겠습니다.

구성 내용은 Karpenter 설치, HPA 설정, KEDA 설치입니다.

 

 

Karpenter 설치

# 1. Set environment variables
export CLUSTER_NAME=myeks
export AWS_REGION=ap-northeast-2
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

# 2. Create Karpenter IAM role
eksctl create iamserviceaccount \
  --cluster=$CLUSTER_NAME \
  --namespace=karpenter \
  --name=karpenter \
  --role-name=${CLUSTER_NAME}-karpenter \
  --attach-policy-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy \
  --role-only \
  --approve

# 3. Install Karpenter using Helm
helm repo add karpenter https://charts.karpenter.sh
helm repo update
helm upgrade --install karpenter karpenter/karpenter \
  --namespace karpenter \
  --create-namespace \
  --set settings.clusterName=${CLUSTER_NAME} \
  --set settings.interruptionQueue=${CLUSTER_NAME} \
  --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::${AWS_ACCOUNT_ID}:role/${CLUSTER_NAME}-karpenter \
  --wait

→ 당연하지만 안된다. Policy가 존재하지 않기 때문이다.

워크샵 가이드 문서와 가시다님 AEWS 4기 스터디 내용을 참고하여 Karpenter를 설치하겠습니다.


# 1. 환경 변수 설정
export TOKEN=`curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
export CLUSTER_NAME=$(eksctl get clusters -o json | jq -r '.[0].Name')
export AWS_REGION=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export KARPENTER_NAMESPACE="kube-system"
export TEMPOUT="$(mktemp)"

echo -e "Cluster Name:\t$CLUSTER_NAME \nAWS Region:\t$AWS_REGION \nAccount ID:\t$AWS_ACCOUNT_ID \nKarpenter Namespace:\t$KARPENTER_NAMESPACE"

KARPENTER_VERSION_V=$(curl -sL "https://api.github.com/repos/aws/karpenter/releases/latest" | jq -r ".tag_name")
export KARPENTER_VERSION="${KARPENTER_VERSION_V/v}"
echo "Karpenter's Latest release version: $KARPENTER_VERSION"


# 2. IAM 추가(3분소요) 
curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/refs/tags/v1.9.0/website/content/en/docs/getting-started/getting-started-with-karpenter/cloudformation.yaml  > "${TEMPOUT}" \
&& aws cloudformation deploy \
  --stack-name "Karpenter-${CLUSTER_NAME}" \
  --template-file "${TEMPOUT}" \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameter-overrides "ClusterName=${CLUSTER_NAME}"



# 3. aws-auth configmap 추가 
eksctl create iamidentitymapping \
  --username system:node:{{EC2PrivateDNSName}} \
  --cluster "${CLUSTER_NAME}" \
  --arn "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}" \
  --group system:bootstrappers \
  --group system:nodes


# 4. IRSA 추가
eksctl create iamserviceaccount \
  --cluster "${CLUSTER_NAME}" --name karpenter --namespace $KARPENTER_NAMESPACE \
  --role-name "${CLUSTER_NAME}-karpenter" \
  --attach-policy-arn "arn:aws:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy-${CLUSTER_NAME}" \
  --role-only \
  --approve

export KARPENTER_IAM_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/${CLUSTER_NAME}-karpenter"


# 5. 카펜터 설치
echo Your Karpenter version is: $KARPENTER_VERSION
helm registry logout public.ecr.aws
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version "${KARPENTER_VERSION}" \
  --namespace "${KARPENTER_NAMESPACE}" --create-namespace \
  --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=${KARPENTER_IAM_ROLE_ARN} \
  --set settings.clusterName=${CLUSTER_NAME} \
  --set controller.resources.requests.cpu=1 \
  --set controller.resources.requests.memory=1Gi \
  --set controller.resources.limits.cpu=1 \
  --set controller.resources.limits.memory=1Gi 
  
# 6. 카펜터 확인
kubectl get deployment -n $KARPENTER_NAMESPACE -l app.kubernetes.io/name=karpenter

프로메테우스 / 그라파나 설치

# 프로메테우스 / 그라파나 설치
helm repo add grafana-charts https://grafana.github.io/helm-charts
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
kubectl create namespace monitoring

# 프로메테우스 설치
curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/v"${KARPENTER_VERSION}"/website/content/en/preview/getting-started/getting-started-with-karpenter/prometheus-values.yaml | envsubst | tee prometheus-values.yaml
helm install --namespace monitoring prometheus prometheus-community/prometheus --values prometheus-values.yaml
extraScrapeConfigs: |
    - job_name: karpenter
      kubernetes_sd_configs:
      - role: endpoints
        namespaces:
          names:
          - kube-system
      relabel_configs:
      - source_labels:
        - __meta_kubernetes_endpoints_name
        - __meta_kubernetes_endpoint_port_name
        action: keep
        regex: karpenter;http-metrics

# 프로메테우스 얼럿매니저 미사용으로 삭제
kubectl delete sts -n monitoring prometheus-alertmanager

# 프로메테우스 접속
kubectl port-forward --namespace monitoring svc/prometheus-server 9090:80 &
open http://127.0.0.1:9090


# 그라파나 설치
curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/v"${KARPENTER_VERSION}"/website/content/en/preview/getting-started/getting-started-with-karpenter/grafana-values.yaml | tee grafana-values.yaml
helm install --namespace monitoring grafana grafana-charts/grafana --values grafana-values.yaml

# admin 암호
kubectl get secret --namespace monitoring grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo

# 그라파나 접속
kubectl port-forward --namespace monitoring svc/grafana 3000:80 &
open http://127.0.0.1:3000

카펜터 NodePool, NodeClass 생성

  • 서브넷, 보안그룹 태그에 karpenter.sh/discovery: "${CLUSTER_NAME}"추가 필요

# 변수 확인
export K8S_VERSION="1.34"
export ALIAS_VERSION="$(aws ssm get-parameter --name "/aws/service/eks/optimized-ami/${K8S_VERSION}/amazon-linux-2023/x86_64/standard/recommended/image_id" --query Parameter.Value | xargs aws ec2 describe-images --query 'Images[0].Name' --image-ids | sed -r 's/^.*(v[[:digit:]]+).*$/\1/')"
echo $ALIAS_VERSION

# NodePool, EC2NodeClass 생성
cat <<EOF | envsubst | kubectl apply -f -
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: kubernetes.io/os
          operator: In
          values: ["linux"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["2"]
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      expireAfter: 720h # 30 * 24h = 720h
  limits:
    cpu: 1000
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m
---
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: default
spec:
  role: "KarpenterNodeRole-${CLUSTER_NAME}"       # replace with your cluster name
  amiSelectorTerms:
    - alias: "al2023@${ALIAS_VERSION}"            # ex) al2023@latest 
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: "${CLUSTER_NAME}" # replace with your cluster name
  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: "${CLUSTER_NAME}" # replace with your cluster name
EOF


# 확인 
kubectl get nodepool,ec2nodeclass,nodeclaims

 

Warm Pool 배포

# pause파드 : 1개에 CPU 1개 최소 보장 할당할 수 있게 디플로이먼트 배포
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: warm-pool-ballast 
spec:
  replicas: 3
  selector:
    matchLabels:
      app: warm-pool
  template:
    metadata:
      labels:
        app: warm-pool
    spec:
      priorityClassName: overprovisioning  
      containers:
      - name: pause
        image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
        resources:
          requests:
            cpu: "1"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: overprovisioning
value: -200  
globalDefault: false
description: "Priority class for warm pool pods"
EOF

# Scale Up
kubectl scale deployment warm-pool-ballast  --replicas 5

# 확인
kubectl get nodeclaims
 
트러블슈팅 1. nodeclaims 상태가 Unknwon인 경우 : 노드 클래스 보안 그룹 확인 → 필자의 경우 아웃바운드가 막혀 통신이 불가했음

(참고) 노드 클레임 생성에 따라 자동 파드가 배치됨

 

 

데이터 수집 구성

노드 스케일링 전략 선택을 위해 스케일링 지연 시간, 트래픽 피크 시간 패턴 지표 수집이 필요합니다.

본 장에서는 위 옵션으로 구성한 프로메테우스 지표를 통해 데이터를 수집하고 올바른 노드 스케일링 전략을 선택할 수 있도록 하겠습니다.

→  Full EKS Optimization, Network Optimization 의 데이터 수집은 kubectl, Cloudwatch 만 사용합니다. 이는 필자의 설계 의도로 최대한 간단하면서 AWS Native 하게 데이터를 수집하고자 한 의도였습니다. 하지만, Karpenter 데이터 수집부터는 Cloudwatch 커스텀 메트릭 설정과 메트릭 수집 주기를 짧게하는 옵션 활성화에 따른 비용으로 Prometheus를 수집하도록 변경하였습니다. Prometheus가 미설치인 경우 가이드를 제공합니다.

 

Scaling Performance Analysis

# Scale 성능 :  한 달 동안 scale 시, 99%가 이 시간 안에 완료”

# P99 (월간)
histogram_quantile(0.99, sum(increase(karpenter_pods_provisioning_bound_duration_seconds_bucket[30d])) by (le))
# 평균 (월간)
sum(increase(karpenter_pods_provisioning_bound_duration_seconds_sum[30d])) / sum(increase(karpenter_pods_provisioning_bound_duration_seconds_count[30d]))


# 평균 latency : 실제 평균 (왜곡 없음)
sum(increase(karpenter_pods_provisioning_bound_duration_seconds_sum[30d])) 
/
sum(increase(karpenter_pods_provisioning_bound_duration_seconds_count[30d]))


# 노드 churn (운영 안정성) : 너무 많으면 → 불안정, 너무 적으면 → 과잉 리소스
sum(increase(karpenter_nodes_created_total[30d])) by (nodepool)
sum(increase(karpenter_nodes_terminated_total[30d])) by (nodepool)

# disruption (최적화 영향) : consolidation 영향 분석
sum(increase(karpenter_nodeclaims_disrupted_total[30d])) by (reason)

# 활용률 (비용 KPI) : 월 평균
avg(karpenter_cluster_utilization_percent)

 

 

Traffic Pattern Analysis

# Pod 수
sum(kube_pod_info)

# Pending Pod 수
sum(kube_pod_status_phase{phase="Pending"})

# CPU 사용률
sum(rate(container_cpu_usage_seconds_total[5m])) / sum(kube_node_status_allocatable{resource="cpu"}) * 100

# 메모리 사용률
sum(container_memory_working_set_bytes) / sum(kube_node_status_allocatable{resource="memory"}) * 100

 

 

시나리오 구성

보고서 검증을 위해 HPA 시나리오를 배포하고, 보고서 결과를 확인하겠습니다.

 1. 테스트 환경 배포
kubectl apply -f HPA_TEST_DEPLOYMENT.yaml

# 2. HPA 예제 배포
kubectl apply -f HPA_EXAMPLES.yaml

# 3. HPA 상태 확인
kubectl get hpa -A
더보기

# HPA 예제 모음 - EKS Scaling Optimization 보고서 검증용
# HPA_EXAMPLES.yaml
---
# 1. 기본 HPA (CPU 기반)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-hpa-basic
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300  # 5분
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 30
      - type: Pods
        value: 4
        periodSeconds: 30
      selectPolicy: Max

---
# 2. 메모리 기반 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: app-hpa-memory
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: memory-intensive-app
  minReplicas: 3
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 600  # 10분 (메모리는 천천히)
      policies:
      - type: Pods
        value: 1
        periodSeconds: 120
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Pods
        value: 2
        periodSeconds: 60

---
# 3. CPU + 메모리 복합 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: app-hpa-combined
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 5
  maxReplicas: 50
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 25  # 한 번에 25%씩만 줄임
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 50  # 한 번에 50%씩 늘림
        periodSeconds: 30

---
# 4. 커스텀 메트릭 HPA (RPS - Requests Per Second)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa-rps
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-server
  minReplicas: 3
  maxReplicas: 30
  metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "1000"  # Pod당 1000 RPS
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 180
      policies:
      - type: Pods
        value: 2
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
      - type: Pods
        value: 5
        periodSeconds: 30

---
# 5. 외부 메트릭 HPA (SQS Queue Length)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: worker-hpa-sqs
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: queue-worker
  minReplicas: 2
  maxReplicas: 100
  metrics:
  - type: External
    external:
      metric:
        name: sqs_queue_length
        selector:
          matchLabels:
            queue: "my-work-queue"
      target:
        type: AverageValue
        averageValue: "30"  # Pod당 30개 메시지 처리
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 50
        periodSeconds: 120
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 15
      - type: Pods
        value: 10
        periodSeconds: 15
      selectPolicy: Max

---
# 6. 문제 있는 HPA (보고서에서 지적할 사항들)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: problematic-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: legacy-app
  minReplicas: 1  # ❌ 너무 낮음 (최소 2 이상 권장)
  maxReplicas: 5  # ❌ 너무 낮음 (트래픽 급증 대응 불가)
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 90  # ❌ 너무 높음 (70% 권장)
  # ❌ behavior 설정 없음 (기본값 사용)

---
# 7. 최적화된 HPA (보고서 권장사항 반영)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: optimized-hpa
  namespace: production
  annotations:
    description: "Well-Architected HPA with proper behavior settings"
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: optimized-app
  minReplicas: 3  # ✅ HA를 위한 최소 3개
  maxReplicas: 100  # ✅ 충분한 여유
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70  # ✅ 적정 수준
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80  # ✅ 적정 수준
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300  # ✅ 5분 안정화
      policies:
      - type: Percent
        value: 25  # ✅ 점진적 축소
        periodSeconds: 60
      - type: Pods
        value: 2  # ✅ 최대 2개씩만
        periodSeconds: 60
      selectPolicy: Min  # ✅ 보수적 축소
    scaleUp:
      stabilizationWindowSeconds: 0  # ✅ 즉시 반응
      policies:
      - type: Percent
        value: 100  # ✅ 빠른 확장
        periodSeconds: 30
      - type: Pods
        value: 5  # ✅ 최대 5개씩
        periodSeconds: 30
      selectPolicy: Max  # ✅ 적극적 확장

---
# 8. 야간 스케일다운 HPA (CronHPA 스타일)
# 참고: 실제로는 KEDA CronScaler 또는 별도 CronHPA 사용
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: business-hours-hpa
  namespace: production
  annotations:
    description: "Day: 10-50 replicas, Night: 3-10 replicas (requires external controller)"
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: business-app
  minReplicas: 10  # 업무 시간 기준
  maxReplicas: 50
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 600  # 10분
      policies:
      - type: Percent
        value: 20
        periodSeconds: 120
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Percent
        value: 50
        periodSeconds: 30

---
# 9. 마이크로서비스용 HPA (빠른 반응)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: microservice-hpa
  namespace: microservices
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: payment-service
  minReplicas: 5
  maxReplicas: 100
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60  # 더 낮은 임계값 (빠른 반응)
  - type: Pods
    pods:
      metric:
        name: http_request_duration_p99
      target:
        type: AverageValue
        averageValue: "500m"  # P99 < 500ms
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 120  # 2분 (짧음)
      policies:
      - type: Pods
        value: 3
        periodSeconds: 30
    scaleUp:
      stabilizationWindowSeconds: 0  # 즉시
      policies:
      - type: Percent
        value: 100
        periodSeconds: 15  # 15초마다 2배
      - type: Pods
        value: 10
        periodSeconds: 15
      selectPolicy: Max

---
# 10. Stateful 애플리케이션용 HPA (보수적)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: stateful-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: StatefulSet
    name: cache-cluster
  minReplicas: 3
  maxReplicas: 15
  metrics:
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 75
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 900  # 15분 (매우 보수적)
      policies:
      - type: Pods
        value: 1  # 한 번에 1개씩만
        periodSeconds: 300  # 5분마다
    scaleUp:
      stabilizationWindowSeconds: 180  # 3분
      policies:
      - type: Pods
        value: 1  # 한 번에 1개씩만
        periodSeconds: 60

 

 

# HPA 테스트용 Deployment 예제 (HPA_TEST_DEPLOYMENT.yaml ) 
---
# 1. 기본 Nginx Deployment (CPU 테스트용)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 200m
            memory: 256Mi

---
# 2. PHP Apache (CPU 부하 생성용)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: php-apache
  template:
    metadata:
      labels:
        app: php-apache
    spec:
      containers:
      - name: php-apache
        image: registry.k8s.io/hpa-example
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 200m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 512Mi

---
# Service for php-apache
apiVersion: v1
kind: Service
metadata:
  name: php-apache
  namespace: default
spec:
  selector:
    app: php-apache
  ports:
  - port: 80
    targetPort: 80
  type: ClusterIP

---
# 3. 부하 생성기 (HPA 테스트용)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: load-generator
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: load-generator
  template:
    metadata:
      labels:
        app: load-generator
    spec:
      containers:
      - name: busybox
        image: busybox:latest
        command:
        - /bin/sh
        - -c
        - |
          while true; do
            wget -q -O- http://php-apache.default.svc.cluster.local
          done
        resources:
          requests:
            cpu: 100m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 128Mi

---
# 4. HPA 테스트용 간단한 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-app
  namespace: default
  labels:
    app: test-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: test-app
  template:
    metadata:
      labels:
        app: test-app
    spec:
      containers:
      - name: app
        image: nginx:alpine
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 50m
            memory: 64Mi
          limits:
            cpu: 100m
            memory: 128Mi

---
# 5. HPA 테스트용 기본 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: test-app-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: test-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 60  # 테스트용으로 짧게
      policies:
      - type: Pods
        value: 1
        periodSeconds: 30
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Pods
        value: 2
        periodSeconds: 30

 

 

 

보고서 검증

  • 스케일링 현황 요약
  • 현재 현황 분석 : Karpenter, HPA, KEDA, Warm Pool 확인
  • 스케일링 성능 분석 : 노드 확장 성능 분석(P99, 평균 노드 생성 지연)
  • 트래픽 패턴 분석 : 파드 생성 패턴, 파드 사용량 분석
  • 스케일링 전략 평가 : 5가지 스케일링 전략 평가.
  • 스케일링 설정 분석

 

참고

Strands Server

CFM-tip MCP server

devfloor9 님 기술 블로그

CloudNet@ AEWS4기