5주차 - EKS Autoscaling(HPA/KEDA/VPA → Pod Autoscaling )

728x90

 

 

Autoscaling이란

오토스케일링(autoscaling)은 서버 팜의 컴퓨팅 리소스 양(일반적으로 활성 서버 수로 측정)을 동적으로 조정하는 클라우드 컴퓨팅에 사용되는 한 방식이다. (위키백과)

 

클라우드를 배우면서 클라우드의 장점이라고 배우는 내용 중 "Autoscaling"은 빠질 수 가 없다.

웹 서비스를 운영한다고 했을 경우, 트래픽이 증가하게 되면 이에 적절하게 서버의 스펙 증가 (Scale-UP/Down)을 하거나, 서버의 수를 증가(Scale-In/Out)하여 대처를 하게 된다.

 

Kubernetes 환경에서도 동일하게 트래픽이 증가한다면 어떻게 해결해야 할까?

  • 수평 확장 Horizontal Scaling
    • 운영 환경에 더 많은 워크로드(VM, Task, Pod)를 추가하는 방식
    • 트래픽을 여러 워크로드에 분산시키는 방법
    • 확장성이 높고 유연한 방법
    • 비용 효율적
    • Stateless 워크로드에 적합 (데이터 일관성 유지 필요)
  • 수직 확장 Vertical Scaling
    • 운영 환경의 기존 워크로드의 성능(CPU, Memory)을 향상하는 방법
    • 하드웨어는 확장에 한계가 존재함
    • 확장 과정에서의 장애 위험이 존재함
    • 수평적 확장과 비교하면 유연성이 부족함
  • AWS Auto Scaling 정책
    • Simple/Step scaling : Manual Reactive, Dynamic scaling
      • 고객이 정의한 단계에 따라 메트릭을 모니터링하고 인스턴스를 추가 또는 제거
    • Target tracking : Automated Reactive, Dynamic scaling
      • 고객이 정의한 목표 메트릭을 유지하기 위해 자동으로 인스턴스를 추가 또는 제거
    • Scheduled scaling : Manual Proactive
      • 고객이 정의한 일정에 따라 인스턴스를 시작하거나 종료
    • Predictive scaling : Automated Proactive
      • 과거 트렌드를 기반으로 용량을 선제적으로 시작
  • K8S Auto Scaling 정책
    • 확장 방법 : 컨테이너(파드) vs 노드(서버)
      • 컨테이너 수평적 확장
      • 컨테이너 수직적 확장
      • 노드 수평적 확장
      • 노드 수직적 확장
    • 확장 기준
      • 컨테이너 메트릭 기반
      • 애플리케이션 메트릭 기반
      • 이벤트(일정, 대기열 등) 기반
    • 확장 정책
      • 단순 확장 정책
      • 단계 확장 정책
      • 목표 추적 확장 정책
  • EKS Auto Scaling
    • HPA : 서비스를 처리할 파드 자원이 부족한 경우 신규 파드 Provisioning → 파드 Scale Out
    • VPA : 서비스를 처리할 파드 자원이 부족한 경우 파드 교체(자동 or 수동) → 파드 Scale Up
    • CAS : 파드를 배포할 노드가 부족한 경우 신규 노드 Provisioning → 노드 Scale Out
    • Karpenter : Unscheduled 파드가 있는 경우 새로운 노드 및 파드 Provisioning → 노드 Scale Up/Out
  • AWS Auto Scaling 한계 → 관리 복잡성 증가, 최적화 설정 어려움, 비용 관리 어려움
    • 오토스케일링 전략은 EC2의 오토스케일링 그룹 사용을 중심으로 동작
    • 노드 그룹에서 인스턴스 타입이 동일하다고 가정
    • 혼합 인스턴스 타입은 가능한 CPU와 메모리가 균등하게 설정
    • 다양한 인스턴스 타입을 지원하기 위해서는 여러 노드 그룹이 필요
    • 모범사례는 AZ당 노드 그룹을 가지는 것
  • 카펜터(Karpenter)  : 유연한 노드 프로비저닝, 빠른 스케일링 및 비용 최적화, 똑똑한 통합 관리 및 간소화된 설정, 향상된 리소스 활용 및 확장성
    • EC2와 긴밀히 통합 : EC2 Fleet API, ASGs 가 없음
    • 쿠버네티스 네이티브 : Watch API, Labels, Finalizers
    • 자동화된 인스턴스 선택 : 워크로드 요구사항을 인스턴스 타입과 일치
    • 기본 동작 흐름 : 카펜터가 ‘파드 unschedulable’ 감지 후 Pod 스펙 평가 후 ‘EC2 Fleet’를 통해 노드 프로비저닝
    • Over Provisioning : 스파이크 트래픽 대비하기 - 여유 노드를 항상 유지하기, 더미파드 사용(낮은 우선순위 적용)
    • Custom Metric 활용하기 : Resource Metric API, Custom Metric API, External Metric API
      • Resource Metric API : Node 또는 Pod의 CPU, 메모리 사용량 등의 metric 기반으로한 파드 스케일링, metrics.k8s.io
      • Custom Metric API : 사용자가 정의한 클러스터 내부의 metric을 기반으로 파드 스케일링, custom.metrics.k8s.io
      • External Metric API : 클러스터 외부에서 수집된 metric 을 기반으로 파드 스케일링, external.metrics.k8s.io

 eks-node-viewer

  • 노드 할당 가능 용량요청 request 리소스 표시, 실제 파드 리소스 사용량 X
  •  동작
    • It displays the scheduled pod resource requests vs the allocatable capacity on the node.
    • It does not look at the actual pod resource usage.
    • Node마다 할당 가능한 용량과 스케줄링된 POD(컨테이너)의 Resource 중 request 값을 표시한다.
    • 실제 POD(컨테이너) 리소스 사용량은 아니다. /pkg/model/pod.go 파일을 보면 컨테이너의 request 합을 반환하며, init containers는 미포함
# macOS 설치
brew tap aws/tap
brew install eks-node-viewer

# 운영서버 EC2에 설치 : userdata 통해 이미 설치 되어 있음
yum install golang -y
go install github.com/awslabs/eks-node-viewer/cmd/eks-node-viewer@latest  # 설치 시 2~3분 정도 소요

# Windows 에 WSL2 (Ubuntu) 설치
sudo apt install golang-go
go install github.com/awslabs/eks-node-viewer/cmd/eks-node-viewer@latest  # 설치 시 2~3분 정도 소요
echo 'export PATH="$PATH:/root/go/bin"' >> /etc/profile
source /etc/profile

# Standard usage
eks-node-viewer

# Display both CPU and Memory Usage
eks-node-viewer --resources cpu,memory
eks-node-viewer --resources cpu,memory --extra-labels eks-node-viewer/node-age

# Display extra labels, i.e. AZ : node 에 labels 사용 가능
eks-node-viewer --extra-labels topology.kubernetes.io/zone
eks-node-viewer --extra-labels kubernetes.io/arch

# Sort by CPU usage in descending order
eks-node-viewer --node-sort=eks-node-viewer/node-cpu-usage=dsc

# Karenter nodes only
eks-node-viewer --node-selector "karpenter.sh/provisioner-name"

# Specify a particular AWS profile and region
AWS_PROFILE=myprofile AWS_REGION=us-west-2


Computed Labels : --extra-labels
# eks-node-viewer/node-age - Age of the node
eks-node-viewer --extra-labels eks-node-viewer/node-age
eks-node-viewer --extra-labels topology.kubernetes.io/zone,eks-node-viewer/node-age

# eks-node-viewer/node-ephemeral-storage-usage - Ephemeral Storage usage (requests)
eks-node-viewer --extra-labels eks-node-viewer/node-ephemeral-storage-usage

# eks-node-viewer/node-cpu-usage - CPU usage (requests)
eks-node-viewer --extra-labels eks-node-viewer/node-cpu-usage

# eks-node-viewer/node-memory-usage - Memory usage (requests)
eks-node-viewer --extra-labels eks-node-viewer/node-memory-usage

# eks-node-viewer/node-pods-usage - Pod usage (requests)
eks-node-viewer --extra-labels eks-node-viewer/node-pods-usage

 

eks-node-viewer 화면

 

 

HPA: Horizontal Pod Autoscaling

 

HPA란 Pod의 수량을 조절하는(Scale In/Out) Pod Autoscaling이다.

Metric-Server에서 클러스터의 리소스 사용 데이터를 확인하여 동작!

 

실습 시나리오

 

1. php-apache.yaml을 배포 (Web Server Pod)

2. HPA 생성 및 부하를 발생하여 Pod Autoscaling 확인

 

1. php-apache.yaml 배포

# Run and expose php-apache server
cat << EOF > php-apache.yaml
apiVersion: apps/v1
kind: Deployment
metadata: 
  name: php-apache
spec: 
  selector: 
    matchLabels: 
      run: php-apache
  template: 
    metadata: 
      labels: 
        run: php-apache
    spec: 
      containers: 
      - name: php-apache
        image: registry.k8s.io/hpa-example
        ports: 
        - containerPort: 80
        resources: 
          limits: 
            cpu: 500m
          requests: 
            cpu: 200m
---
apiVersion: v1
kind: Service
metadata: 
  name: php-apache
  labels: 
    run: php-apache
spec: 
  ports: 
  - port: 80
  selector: 
    run: php-apache
EOF
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

# [운영서버 EC2] 파드IP로 직접 접속
PODIP=$(kubectl get pod -l run=php-apache -o jsonpath="{.items[0].status.podIP}")
curl -s $PODIP; echo

 

- hpa-example 동작

#docker

FROM php:5-apache
COPY index.php /var/www/html/index.php
RUN chmod a+rx index.php

#php 동작

<?php
$x = 0.0001;
for ($i = 0; $i <= 1000000; $i++) {
			$x += sqrt($x);
}
echo "OK!";
?>

 

 

2. HPA 생성 및 부하 발생

 

kubectl "autoscale" 명령어를 사용하여 HPA 설정

  • cpu-percent=50 : Pod의 CPU 사용률을 확인 후 50%넘어가면 HPA 작동
  • min=1 / max=10 : Pod의 최소/최대 수량 설정
  • scaleTargetRef: autoscale 대상 선택 → 현재 php-apache Deployment를 Target으로 선택
# 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.
cat <<EOF | kubectl apply -f -
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        averageUtilization: 50
        type: Utilization
EOF
혹은
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 get hpa php-apache -o yaml | kubectl neat
spec: 
  minReplicas: 1               # [4] 또는 최소 1개까지 줄어들 수도 있습니다
  maxReplicas: 10              # [3] 포드를 최대 10개까지 늘립니다
  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 (서비스명 도메인으로 파드들 분산 접속) >> 증가 확인(몇개까지 증가되는가? 그 이유는?) 후 중지
## >> [scale back down] 중지 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"


# Horizontal Pod Autoscaler Status Conditions
kubectl describe hpa

 

KEDA: Kubernetes based Event Driven Autoscaler

HPA의 경우 기준이 CPU, MEM이여서 Autoscaling 사용의 한계가 있음.
KEDA의 경우 다양한 Application의 이벤트에 대해서 Kubernetes Autoscaling을 지원

KEDA 동작 과정

 

KEDA 공식 홈페이지에서 다양한 Scalers를 지원하는 것을 확인 할 수 있음

 

실습 시나리오

 

1. KEDA 설치

2. php-apache.yaml을 배포 (Web Server Pod) / keda namespace

3. ScaledObject 정책 생성 : cron

4. 위 Cron 설정을 통해 정해진 시간에 Pod 생성 및 삭제 확인

  • start: 00,15,30,45 * * * *
  • end: 05,20,35,50 * * * *

 

1. 실습

# 설치 전 기존 metrics-server 제공 Metris API 확인
kubectl get --raw "/apis/metrics.k8s.io" -v=6 | jq
kubectl get --raw "/apis/metrics.k8s.io" | jq
{
  "kind": "APIGroup",
  "apiVersion": "v1",
  "name": "metrics.k8s.io",
  ...


# KEDA 설치 : serviceMonitor 만으로도 충분할듯..
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: 8020
    serviceMonitor:
      # Enables ServiceMonitor creation for the Prometheus webhooks
      enabled: true
EOT

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

# KEDA 설치 확인
kubectl get crd | grep keda
kubectl get all -n keda
kubectl get validatingwebhookconfigurations keda-admission -o yaml
kubectl get podmonitor,servicemonitors -n keda
kubectl get apiservice v1beta1.external.metrics.k8s.io -o yaml

# CPU/Mem은 기존 metrics-server 의존하여, KEDA metrics-server는 외부 이벤트 소스(Scaler) 메트릭을 노출 
## https://keda.sh/docs/2.16/operate/metrics-server/
kubectl get pod -n keda -l app=keda-operator-metrics-apiserver

# Querying metrics exposed by KEDA Metrics Server
kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1" | jq
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "external.metrics.k8s.io/v1beta1",
  "resources": [
    {
      "name": "externalmetrics",
      "singularName": "",
      "namespaced": true,
      "kind": "ExternalMetricValueList",
      "verbs": [
        "get"
      ]
    }
  ]
}

# 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  # Specifies the maximum number of replicas to scale up to (defaults to 100).
  pollingInterval: 30  # Specifies how often KEDA should check for scaling events
  cooldownPeriod: 300  # Specifies the cool-down period in seconds after a scaling event
  scaleTargetRef:  # Identifies the Kubernetes deployment or other resource that should be scaled.
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  triggers:  # Defines the specific configuration for your chosen scaler, including any required parameters or settings
  - 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

# 그라파나 대시보드 추가 : 대시보드 상단에 namespace : keda 로 변경하기!
# KEDA 대시보드 Import : https://github.com/kedacore/keda/blob/main/config/grafana/keda-dashboard.json

# 모니터링
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 ScaledObject -n keda php-apache-cron-scaled && kubectl delete deploy php-apache -n keda && helm uninstall keda -n keda
kubectl delete namespace keda

 

  • HPA, VPA는 metric-server를 통하여 자원의 상태를 확인하지만, KEDA의 경우 별도의 keda-operator-metircs-apiserver를 설치하여 사용

 

VPA: Vertical Pod Autoscaling

VPA란 Pod의 스펙을 조절하는(Scale Up/Down) Pod Autoscaling이다.

Pod의 resources.request을 최적값으로 수정 → 수정된 request값에 따라 Pod의 스펙이 조절

 

※ VPA의 경우 HPA와 같이 사용 할 수 없으며, resources.request을 최적값으로 수정하면 기존 Pod는 종료하고 새로운 Pod를 생성

 

실습 시나리오

 

1. VPA 설치

2. hamster.yaml을 배포(공식 예제)

3. Pod 리소스 Requestes 확인 및 신규 Pod 생성 확인

 

1. VPA 설치

# [운영서버 EC2] 코드 다운로드
git clone https://github.com/kubernetes/autoscaler.git # userdata 로 설치 되어 있음
cd ~/autoscaler/vertical-pod-autoscaler/
tree hack

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

# 1.0 제거
yum remove openssl -y

# 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
git status
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
git add .
git commit -m "openssl version modify"

# 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

# 재실행!
sed -i 's/openssl/openssl11/g' ~/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/gencerts.sh
./hack/vpa-up.sh

kubectl get crd | grep autoscaling
kubectl get mutatingwebhookconfigurations vpa-webhook-config
kubectl get mutatingwebhookconfigurations vpa-webhook-config -o json | jq

 

2. hamster.yaml을 배포(공식 예제)

# 모니터링
watch -d "kubectl top pod;echo "----------------------";kubectl describe pod | grep Requests: -A2"

# 공식 예제 배포
cd ~/autoscaler/vertical-pod-autoscaler/
cat examples/hamster.yaml
kubectl apply -f examples/hamster.yaml && kubectl get vpa -w

# 파드 리소스 Requestes 확인
kubectl describe pod | grep Requests: -A2
    Requests:
      cpu:        100m
      memory:     50Mi
--
    Requests:
      cpu:        587m
      memory:     262144k
--
    Requests:
      cpu:        587m
      memory:     262144k

# VPA에 의해 기존 파드 삭제되고 신규 파드가 생성됨
kubectl get events --sort-by=".metadata.creationTimestamp" | grep VPA

  • VPA에 의해 기존 파드 삭제되고 신규 파드가 생성

 

 

 

 

 

 

 

728x90