AWES[1기](Amzaon EKS Study)

[AWES] 5주차 - EKS Autoscaling

Joon0464 2023. 5. 25. 20:44

1) Horizontal Pod Autoscaling (HPA)
- HPA는 스케일 인/아웃을 통해 파드 수를 늘리거나 줄이는 방법이다
- 쿠버네테스에서 설정한 메트릭에 따라 포드 수를 자동으로 조절한다. (예: CPU 사용률)
- 애플리케이션의 부하가 일시적으로 높아질 때 유용
- 파드 수를 늘림으로써 처리량을 높일 수 있다.
- 리소스 사용량이 증가할 수 있어 비용 효율성을 떨어뜨릴 수 있다.

 

CON324_Optimizing-Amazon-EKS-for-performance-and-cost-on-AWS.pdf 발췌

  • HPA 아키텍처 : cAdvisor 이 컨테이너의 메모리/CPU 수집 → metrics-server 는 kubelet 를 통해서 수집 후 apiserver 에 등록 → HPA는 apiserver(Resource API)를 통해서 15분 마다 메모리/CPU 수집하여 정책에 따라 동작

  • metrics.type(Resource, Pods, Object), target.type(Utilization, AverageValue, Value)

2) Vertical Pod Autoscaling (VPA)
- VPA는 포드 수는 유지하되 필요한 리소스를 늘리거나 줄이는 방법이다.
- 파드 리소스 할당량 (CPU, 메모리 등)을 기준에 따라 동적으로 조절한다. -> resource.limit ,request 값을 수정하여  최적의 값을 찾는데 도움
- 파드의 리소스 사용량이 지속적으로 변동하는 경우 사용한다.
- 리소스 사용량을 최적화하여 비용 효율을 높일 수 있다.
- 스케일 업 과정에서 재시작이 필요할 수 있어 다운타임이 발생할 수 있다.

- 흐름도 : 메트릭 서버가 용량 수집 -> 파드 최적값을 계산 -> 파드 삭제 -> 파드 생성 -> 리소스 조정

CON324_Optimizing-Amazon-EKS-for-performance-and-cost-on-AWS.pdf 발췌


3) Cluster Autoscaling (CAS) -> Karpenter 사용
- CAS는 클러스터의 노드 수를 조절하여 Pending 상태의 파드를 Running 상태로 변경하는 방법이다.
- pending 상태의 파드가 있을 때 워커 노드를 추가하여 리소스를 확장하고, 파드를 running 상태로 변경한다.
- 클러스터 자원이 부족하거나 남는 경우 노드 수를 조절하여 사용률을 최적화한다.
- 클러스터의 처리량과 리소스 사용률을 효과적으로 관리 가능
- 노드 수의 증감으로 인한 오버헤드와 비용 변동성이 발생할 수 있다.

CON324_Optimizing-Amazon-EKS-for-performance-and-cost-on-AWS.pdf 발췌

1. HPA - Horizontal Pod Autoscaler

# Run and expose php-apache server
curl -s -O https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/application/php-apache.yaml
cat php-apache.yaml | yh
kubectl apply -f php-apache.yaml

# 확인
kubectl exec -it deploy/php-apache -- cat /var/www/html/index.php
...

# 모니터링 : 터미널2개 사용
watch -d 'kubectl get hpa,pod;echo;kubectl top pod;echo;kubectl top node'
kubectl exec -it deploy/php-apache -- top

# 접속
PODIP=$(kubectl get pod -l run=php-apache -o jsonpath={.items[0].status.podIP})
curl -s $PODIP; echo

테스트용 파드 배포
요청 및 응답이 잘 오는지 테스트해본다.

# Create the HorizontalPodAutoscaler : requests.cpu=200m - 알고리즘
# Since each pod requests 200 milli-cores by kubectl run, this means an average CPU usage of 100 milli-cores.
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
kubectl describe hpa
...
Metrics:                                               ( current / target )
  resource cpu on pods  (as a percentage of request):  0% (1m) / 50%
Min replicas:                                          1
Max replicas:                                          10
Deployment pods:                                       1 current / 1 desired
...

# HPA 설정 확인
kubectl krew install neat
kubectl get hpa php-apache -o yaml
kubectl get hpa php-apache -o yaml | kubectl neat | yh
spec: 
  minReplicas: 1               # [4] 또는 최소 1개까지 줄어들 수도 있습니다
  maxReplicas: 10              # [3] 포드를 최대 5개까지 늘립니다
  scaleTargetRef: 
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache           # [1] php-apache 의 자원 사용량에서
  metrics: 
  - type: Resource
    resource: 
      name: cpu
      target: 
        type: Utilization
        averageUtilization: 50  # [2] CPU 활용률이 50% 이상인 경우

# 반복 접속 1 (파드1 IP로 접속) >> 증가 확인 후 중지
while true;do curl -s $PODIP; sleep 0.5; done

# 반복 접속 2 (서비스명 도메인으로 접속) >> 증가 확인(몇개까지 증가되는가? 그 이유는?) 후 중지 >> 중지 5분 후 파드 갯수 감소 확인
# Run this in a separate terminal
# so that the load generation continues and you can carry on with the rest of the steps
kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"

HPA 생성 및 확인
krew 툴 neat을 설치 후 사용하여 좀 더 설정을 보기 쉽게 확인 가능
부하를 주기 위해 curl 요청을 while문으로 계속 보내본다.
Desired -> 2로 증가 후 파드가 1개에서 2개로 Scale Out되는 것을 확인할 수 있다.
마지막으로 리소스 제거하고 HPA 실습 마무리

2. KEDA - Kubernetes based Event Driven Autoscaler

기존의 HPA(Horizontal Pod Autoscaler)는 리소스(CPU, Memory) 메트릭을 기반으로 스케일 여부를 결정한다.

반면에 KEDA는 특정 이벤트를 기반으로 스케일 여부를 결정할 수 있다.

예를 들어 airflow는 metadb를 통해 현재 실행 중이거나 대기 중인 task가 얼마나 존재하는지 알 수 있다.

이러한 이벤트를 활용하여 worker의 scale을 결정한다면 queue에 task가 많이 추가되는 시점에 더 빠르게 확장이 가능하다.

https://keda.sh/docs/2.10/concepts/



- KEDA는 쿠버네테스의 사용자 정의 리소스를 활용하여 이벤트에 따른 스케일링을 정의하고 관리한다.
- 이벤트 소스와 메트릭을 기반으로, KEDA는 수평적인 파드 자동 확장(HPA)을 지원하며, 이를 통해 자동으로 파드를 스케일 아웃/스케일 인한다.
- KEDA는 이벤트 소스로부터 메시지나 작업의 수를 추적하고, 구성된 임계값을 기반으로 파드를 확장하거나 축소한다.
- KEDA는 다양한 이벤트 소스와 큐 시스템을 지원하여, AWS SQS, Azure Storage Queue, Apache Kafka 등과 같은 서비스들도 쉽게 연동할 수 있다.
- 쿠버네테스 환경에서 서버리스 워크로드를 효과적으로 관리할 수 있으며, Knative Serving과 같은 다른 오토스케일링 패러다임과 함께 사용될 수 있다.

# KEDA 설치
cat <<EOT > keda-values.yaml
metricsServer:
  useHostNetwork: true

prometheus:
  metricServer:
    enabled: true
    port: 9022
    portName: metrics
    path: /metrics
    serviceMonitor:
      # Enables ServiceMonitor creation for the Prometheus Operator
      enabled: true
    podMonitor:
      # Enables PodMonitor creation for the Prometheus Operator
      enabled: true
  operator:
    enabled: true
    port: 8080
    serviceMonitor:
      # Enables ServiceMonitor creation for the Prometheus Operator
      enabled: true
    podMonitor:
      # Enables PodMonitor creation for the Prometheus Operator
      enabled: true

  webhooks:
    enabled: true
    port: 8080
    serviceMonitor:
      # Enables ServiceMonitor creation for the Prometheus webhooks
      enabled: true
EOT

kubectl create namespace keda
helm repo add kedacore https://kedacore.github.io/charts
helm install keda kedacore/keda --version 2.10.2 --namespace keda -f keda-values.yaml

# KEDA 설치 확인
kubectl get-all -n keda
kubectl get crd | grep keda

# keda 네임스페이스에 디플로이먼트 생성
kubectl apply -f php-apache.yaml -n keda
kubectl get pod -n keda

# ScaledObject 정책 생성 : cron
cat <<EOT > keda-cron.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: php-apache-cron-scaled
spec:
  minReplicaCount: 0
  maxReplicaCount: 2
  pollingInterval: 30
  cooldownPeriod: 300
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  triggers:
  - type: cron
    metadata:
      timezone: Asia/Seoul
      start: 00,15,30,45 * * * *
      end: 05,20,35,50 * * * *
      desiredReplicas: "1"
EOT
kubectl apply -f keda-cron.yaml -n keda

# 그라파나 대시보드 추가
# 모니터링
watch -d 'kubectl get ScaledObject,hpa,pod -n keda'
kubectl get ScaledObject -w

# 확인
kubectl get ScaledObject,hpa,pod -n keda
kubectl get hpa -o jsonpath={.items[0].spec} -n keda | jq
...
"metrics": [
    {
      "external": {
        "metric": {
          "name": "s0-cron-Asia-Seoul-00,15,30,45xxxx-05,20,35,50xxxx",
          "selector": {
            "matchLabels": {
              "scaledobject.keda.sh/name": "php-apache-cron-scaled"
            }
          }
        },
        "target": {
          "averageValue": "1",
          "type": "AverageValue"
        }
      },
      "type": "External"
    }

# KEDA 및 deployment 등 삭제
kubectl delete -f keda-cron.yaml -n keda && kubectl delete deploy php-apache -n keda && helm uninstall keda -n keda
kubectl delete namespace keda

 

KEDA 설치
설치된 KEDA 확인
keda 네임스페이스에 디플로이먼트 생성\
ScaledObject 정책 생성 : cron

# 대시보드 추가

1. https://github.com/kedacore/keda/blob/main/config/grafana/keda-dashboard.json 접속  

2. 대시보드 import

스케줄러 방식으로 동작하여 일정 시간마다 Scale in out 된다.
마무리로 리소스는 깔끔히 지워준다.

 

3. VPA - Vertical Pod Autoscaler

- pod resources.request을 최대한 최적값으로 수정, HPA와 같이 사용 불가능, 수정 시 파드 재실행

- VPA는 파드에 할당된 리소스(CPU, 메모리 등)를 실시간 모니터링하고 해당 파드의 사용량에 따라 리소스를 조절한다.
- 최소 리소스 요구량과 최대 리소스 제한 사이에서 동적으로 스케일링을 수행하며, 예약된 및 사용 중인 리소스 사용률을 최적화한다.
- VPA가 스케일링을 위해 적용할 변경 사항이 있다면, 파드를 재시작하여 새로운 리소스로 실행할 수 있다.

# 코드 다운로드
git clone https://github.com/kubernetes/autoscaler.git
cd ~/autoscaler/vertical-pod-autoscaler/
tree hack

# openssl 버전 확인
openssl version
OpenSSL 1.0.2k-fips  26 Jan 2017

# openssl 1.1.1 이상 버전 확인
yum install openssl11 -y
openssl11 version
OpenSSL 1.1.1g FIPS  21 Apr 2020

# 스크립트파일내에 openssl11 수정
sed -i 's/openssl/openssl11/g' ~/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/gencerts.sh

# Deploy the Vertical Pod Autoscaler to your cluster with the following command.
watch -d kubectl get pod -n kube-system
cat hack/vpa-up.sh
./hack/vpa-up.sh
kubectl get crd | grep autoscaling

코드 다운로드
OpenSSL 1.1 버전 이상을 설치하고 확인한다.
스크립트파일내에 openssl11 수정
Deploy the Vertical Pod Autoscaler to your cluster with the following command.
공식 예제 배포
모니터링
파드 리소스 Requestes 확인

 

VPA에 의해 기존 파드 삭제되고 신규 파드가 생성됨
실습 후 리소스 제거

4. CA - Cluster Autoscaler

https://catalog.us-east-1.prod.workshops.aws/workshops/9c0aa9ab-90a9-44a6-abe1-8dff360ae428/ko-KR/100-scaling/200-cluster-scaling

- Cluster Autoscale 동작을 위한 cluster-autoscaler 파드(디플로이먼트)를 배치한다.
- Cluster Autoscaler(CA)는 pending 상태인 파드가 존재할 경우, 워커 노드를 스케일 아웃한다.
- 특정 시간을 간격으로 사용률을 확인하여 스케일 인/아웃을 수행한다. 그리고 AWS에서는 Auto Scaling Group(ASG)을 사용하여 Cluster Autoscaler를 적용한다.

- CA는 클러스터의 부족한 리소스를 감지하고 필요한만큼 노드를 추가하여 사용가능한 리소스를 확장한다.
- 반대로 여분의 리소스가 있는 경우 사용하지 않는 노드를 제거하여 리소스를 절약한다.
- 클러스터의 부하와 리소스 이용률에 따라 자동으로 노드를 확장하거나 축소한다.

- CA 문제점 : 하나의 자원에 대해 두군데 (AWS ASG vs AWS EKS)에서 각자의 방식으로 관리 ⇒ 관리 정보가 서로 동기화되지 않아 다양한 문제 발생

설정 전 확인

# EKS 노드에 이미 아래 tag가 들어가 있음
# k8s.io/cluster-autoscaler/enabled : true
# k8s.io/cluster-autoscaler/myeks : owned
aws ec2 describe-instances  --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Reservations[*].Instances[*].Tags[*]" --output yaml | yh
...
- Key: k8s.io/cluster-autoscaler/myeks
      Value: owned
- Key: k8s.io/cluster-autoscaler/enabled
      Value: 'true'

AWS용 Cluster Autoscaler는 Auto Scaling 그룹과의 통합을 제공한다. 사용자는 다음 네 가지 배포 옵션 중 하나를 선택할 수 있다

- One Auto Scaling group
- Multiple Auto Scaling groups
- Auto-Discovery : Auto-Discovery 는 클러스터 오토스케일러를 구성하는 데 선호되는 방법이다.
- Control-plane Node setup

 

# 현재 autoscaling(ASG) 정보 확인
# aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='클러스터이름']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" --output table
aws autoscaling describe-auto-scaling-groups \
    --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" \
    --output table
-----------------------------------------------------------------
|                   DescribeAutoScalingGroups                   |
+------------------------------------------------+----+----+----+
|  eks-ng1-44c41109-daa3-134c-df0e-0f28c823cb47  |  3 |  3 |  3 |
+------------------------------------------------+----+----+----+

# MaxSize 6개로 수정
export ASG_NAME=$(aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].AutoScalingGroupName" --output text)
aws autoscaling update-auto-scaling-group --auto-scaling-group-name ${ASG_NAME} --min-size 3 --desired-capacity 3 --max-size 6

# 확인
aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" --output table
-----------------------------------------------------------------
|                   DescribeAutoScalingGroups                   |
+------------------------------------------------+----+----+----+
|  eks-ng1-c2c41e26-6213-a429-9a58-02374389d5c3  |  3 |  6 |  3 |
+------------------------------------------------+----+----+----+

# 배포 : Deploy the Cluster Autoscaler (CA)
curl -s -O https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml
sed -i "s/<YOUR CLUSTER NAME>/$CLUSTER_NAME/g" cluster-autoscaler-autodiscover.yaml
kubectl apply -f cluster-autoscaler-autodiscover.yaml

# 확인
kubectl get pod -n kube-system | grep cluster-autoscaler
kubectl describe deployments.apps -n kube-system cluster-autoscaler

# (옵션) cluster-autoscaler 파드가 동작하는 워커 노드가 퇴출(evict) 되지 않게 설정
kubectl -n kube-system annotate deployment.apps/cluster-autoscaler cluster-autoscaler.kubernetes.io/safe-to-evict="false"

 

현재 autoscaling(ASG) 정보 확인
MaxSize 6개로 수정
수정된 값 확인
배포 : Deploy the Cluster Autoscaler (CA)
배포 확인
cluster-autoscaler 파드가 동작하는 워커 노드가 퇴출(evict) 되지 않게 설정

# 모니터링 
kubectl get nodes -w
while true; do kubectl get node; echo "------------------------------" ; date ; sleep 1; done
while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text ; echo "------------------------------"; date; sleep 1; done

# Deploy a Sample App
# We will deploy an sample nginx application as a ReplicaSet of 1 Pod
cat <<EoF> nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-to-scaleout
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        service: nginx
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx-to-scaleout
        resources:
          limits:
            cpu: 500m
            memory: 512Mi
          requests:
            cpu: 500m
            memory: 512Mi
EoF

kubectl apply -f nginx.yaml
kubectl get deployment/nginx-to-scaleout

# Scale our ReplicaSet
# Let’s scale out the replicaset to 15
kubectl scale --replicas=15 deployment/nginx-to-scaleout && date

# 확인
kubectl get pods -l app=nginx -o wide --watch
kubectl -n kube-system logs -f deployment/cluster-autoscaler

# 노드 자동 증가 확인
kubectl get nodes
aws autoscaling describe-auto-scaling-groups \
    --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" \
    --output table

./eks-node-viewer
42 pods (0 pending 42 running 42 bound)
ip-192-168-3-196.ap-northeast-2.compute.internal cpu ███████████████████████████████████ 100% (10 pods) t3.medium/$0.0520 On-Demand
ip-192-168-1-91.ap-northeast-2.compute.internal  cpu ███████████████████████████████░░░░  89% (9 pods)  t3.medium/$0.0520 On-Demand
ip-192-168-2-185.ap-northeast-2.compute.internal cpu █████████████████████████████████░░  95% (11 pods) t3.medium/$0.0520 On-Demand
ip-192-168-2-87.ap-northeast-2.compute.internal  cpu █████████████████████████████░░░░░░  84% (6 pods)  t3.medium/$0.0520 On-Demand
ip-192-168-3-15.ap-northeast-2.compute.internal  cpu █████████████████████████████░░░░░░  84% (6 pods)  t3.medium/$0.0520 On-Demand

# 디플로이먼트 삭제
kubectl delete -f nginx.yaml && date

# 노드 갯수 축소 : 기본은 10분 후 scale down 됨, 물론 아래 flag 로 시간 수정 가능 >> 그러니 디플로이먼트 삭제 후 10분 기다리고 나서 보자!
# By default, cluster autoscaler will wait 10 minutes between scale down operations, 
# you can adjust this using the --scale-down-delay-after-add, --scale-down-delay-after-delete, 
# and --scale-down-delay-after-failure flag. 
# E.g. --scale-down-delay-after-add=5m to decrease the scale down delay to 5 minutes after a node has been added.

# 터미널1
watch -d kubectl get node


```bash

위 실습 중 디플로이먼트 삭제 후 10분 후 노드 갯수 축소되는 것을 확인 후 아래 삭제를 해보자! >> 만약 바로 아래 CA 삭제 시 워커 노드는 4개 상태가 되어서 수동으로 2대 변경 하자!
kubectl delete -f nginx.yaml

# size 수정 
aws autoscaling update-auto-scaling-group --auto-scaling-group-name ${ASG_NAME} --min-size 3 --desired-capacity 3 --max-size 3
aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" --output table

# Cluster Autoscaler 삭제
kubectl delete -f cluster-autoscaler-autodiscover.yaml
```

예제 앱 배포

 

replica 개수를 15로 설정
파드개수 증가 확
노드개수가 5개로 증가하였다.

 

테스트용 예제 앱 제거
앱 제거 10분 후 노드가 다시 3개로 스케일 인 되었다.

 

5. CPA - Cluster Proportional Autoscaler

- 노드 수 증가에 비례하여 성능 처리가 필요한 애플리케이션(컨테이너/파드)를 수평으로 자동 확장

- CPA는 클러스터의 확장 또는 축소와 관련하여 추가 또는 삭제되는 노드 수에 따라 컨트롤 비례하도록 워크로드를 스케일한다.
- CPA는 사용자가 설정한 기준(메트릭)에 따라 워크로드를 조절한다.
- 시스템 상태를 조사하여 리소스 예약을 줄이고 글로벌 워크로드를 최적화한다.

6. Karpenter : K8S Native AutoScaler & Fargate

 

- 쿠버네테스 스케줄러가 실행할 수 없는(unschedulable)로 표시한 파드를 감시한다.
- 파드에 의해 요청된 스케줄링 제약 조건(리소스 요청, 노드 셀렉터, 애핀리티, 톨러레이션, 토폴로지 스프레드 제약 조건)을 평가한다.
- 파드의 요구 사항을 충족하는 노드를 프로비저닝한다.
- 파드를 새로운 노드에서 실행하도록 스케줄링한다.
- 노드가 더 이상 필요 없을 때 노드를 제거한다.

- Karpenter는 아키텍처, 리소스 제한 및 노드 조건에 따라 오토스케일링을 수행하여 가장 효율적인 노드를 프로비저닝한다.
- 클은 관리형 컴퓨팅 서비스를 사용하여 클러스터에 추가적인 연산 리소스를 제공하고 리소스 사용량을 최적화한다.

CON405_How-to-monitor-and-reduce-your-compute-costs.pdf