AWS/EKS

EKS Autoscaling - 1 (Pod level)

joshhoxy 2025. 3. 8. 15:25

※ 해당 글은 CloudNet@ gasida 님의 EKS 스터디 내용을 참고하여 작성하였습니다.

들어가며

Autoscaling 전략

1. Application Tuning

  • Action: 애플리케이션 프로세스를 조정 (예: 스레드 수, 힙 크기 등).
  • Scaling Example: Pod 내부의 프로세스 성능을 최적화하여 리소스 활용도를 높임.
  • 특징: Pod 자체는 변경하지 않고 내부 설정만 조정.

2. Vertical Pod Autoscaler (VPA)

  • Action: Pod의 컨테이너 리소스(CPU, 메모리 등)를 증가 또는 감소.
  • Scaling Example: Pod가 더 많은 리소스를 사용할 수 있도록 조정.
  • 특징: Pod 수는 유지되지만 개별 Pod의 처리 능력을 향상.

3. Horizontal Pod Autoscaler (HPA)

  • Action: Pod의 개수를 증가 또는 감소.
  • Scaling Example: 워크로드 증가 시 새로운 Pod를 추가.
  • 특징: 동일한 Pod 사양을 복제하여 확장.

4. Cluster Autoscaler

  • Action: 클러스터의 노드(Node) 수를 증가 또는 감소.
  • Scaling Example: 클러스터에 새로운 노드를 추가하여 전체 리소스 용량을 확장.
  • 특징: 클러스터 차원에서 리소스를 관리하며, 노드 단위로 확장.

EKS Autoscaling

EKS Autoscaling은 Amazon Elastic Kubernetes Service(EKS)에서 클러스터의 노드 수를 자동으로 조정하는 기능입니다. 이 기능은 Kubernetes의 Cluster Autoscaler와 AWS의 Auto Scaling Group(ASG)를 결합하여 작동합니다. 혹은 Karpenter를 사용하여 보다 편리하고 다양하게 Autoscaling 을 구현할 수 있습니다.

Karpenter (카펜터)

Karpenter는 Amazon EKS에서 노드 프로비저닝을 자동화하는 도구로, Kubernetes의 Cluster Autoscaler와 AWS의 Auto Scaling Group(ASG)의 한계를 극복하기 위해 설계되었습니다.

특징

  • 노드 프로비저닝: Karpenter는 스케줄링할 수 없는 Pod를 감지하고, 해당 Pod의 요구 사항에 맞는 노드를 프로비저닝합니다. 노드가 더 이상 필요하지 않으면 제거합니다
  • NodePool: 다양한 인스턴스 유형, 존, OS 등에 대한 제약 조건을 설정할 수 있는 NodePool을 생성할 수 있습니다. 이는 다양한 워크로드 요구 사항에 맞게 노드를 효율적으로 관리할 수 있게 합니다
  • 인스턴스 선택이 자동으로 이루어집니다. 워크로드 요구사항을 인스턴스 타입과 일치 시켜 다양한 인스턴스 타입을 프로비저닝 할 수 있습니다.
  • Custom Metric 을 활용하는 것이 가능합니다. 단순 Pod의 CPU, Mem 뿐만 아니라 사용자가 정의한 Metric을 기준으로 파드를 스케일링할 수 있습니다.

고려 사항

  • AMI 관리: 프로덕션 클러스터에서는 테스트된 AMI 버전을 고정하여 사용하는 것이 좋습니다. 최신 AMI가 자동으로 배포되는 설정은 피해야 합니다
  • 동적 워크로드: Karpenter는 변동성이 큰 워크로드에 적합하며, 정적 워크로드에는 ASG가 더 적합할 수 있습니다
  • NodePool 구성: 서로 다른 팀이나 워크로드 요구 사항에 맞게 여러 NodePool을 생성하여 관리하는 것이 좋습니다
  • 인스턴스 유형 제약: Spot 인스턴스를 사용할 때 인스턴스 유형에 너무 많은 제약을 두지 않도록 주의해야 합니다. 이는 인스턴스 가용성에 영향을 미칠 수 있습니다

카펜터의 동작 방식

카펜터는 위에서 설명한 것 처럼 CAS(클러스터 오토 스케일러 및 오토스케일링 그룹)을 사용하지 않고 EC2 Fleet이라는 오토스케일링 단위를 사용합니다.

이때 카펜터를 구성하는 컴포넌트는 다소 생소할 수 있으니 이는 공식 문서를 읽어 보시면 좋습니다. 🔗

Karpenter는 NodePool 프로비저너EC2NodeClass 또는 AWSNodeTemplate 설정을 기반으로 적합한 노드를 생성합니다. 노드를 생성하는 과정은 다음 시퀀스 다이어그램에 상세하게 설명되어있습니다.

여기서 주목해야할 부분은 다음과 같습니다.

Scheduler의 스케줄링 시도

  • Scheduler는 ETCD에서 새로운 Pod 객체를 감시(Watch)하고, 스케줄링 가능한지 확인합니다.
  • 만약 클러스터 내에 Pod의 요구사항(Resource Requests)을 충족할 수 있는 노드가 없다면, Pod는 unschedulable 상태로 변경됩니다.

Karpenter의 역할: 실시간 감시

  • Karpenter는 Kubernetes Watch API를 통해 unschedulable 상태의 이벤트를 실시간으로 감지합니다.
  • 감지된 Pod의 스펙(요구 리소스, 태그 등)을 평가하여 추가적인 노드가 필요한지 판단합니다.

일반 k8s 클러스터라면 Unschedulable 상태에 머물러 자원이 새로 생겨나기를 기다렸겠지만 카펜터를 사용하면 이러한 Unschedualable 상태를 탐지하여 워커노드를 추가합니다. 새롭게 프로비저닝된 노드가 클러스터에 등록되면, Scheduler는 다시 스케줄링을 시도합니다.

1. 실습 환경 배포

1.1 기본 실습 환경 배포(원클릭)

가시다 님께서 실습환경을 편리하게 구성할 수 있도록 작성해주신 코드입니다. 원클릭 실습 배포 코드 입니다. 사용 환경에 맞게 환경변수 및 aws credential을 설정해 줍니다.

# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/myeks-5week.yaml

# 변수 지정
CLUSTER_NAME=myeks
SSHKEYNAME=<SSH 키 페이 이름>
MYACCESSKEY=<IAM Uesr 액세스 키>
MYSECRETKEY=<IAM Uesr 시크릿 키>

# CloudFormation 스택 배포
aws cloudformation deploy --template-file myeks-5week.yaml --stack-name $CLUSTER_NAME --parameter-overrides KeyName=$SSHKEYNAME SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32  MyIamUserAccessKeyID=$MYACCESSKEY MyIamUserSecretAccessKey=$MYSECRETKEY ClusterBaseName=$CLUSTER_NAME --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text

이번 코드는 지난번 모니터링때 사용한 클러스터 구성에 몇가지 오픈소스와 카펜터 툴을 설치합니다.

배포과정 확인하기. 운영 서버에 접속하여 어떤 과정이 일어나고 있는 지 확인합니다.

# 운영서버 EC2 SSH 접속
ssh -i <SSH 키 파일 위치> ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text)
-------------------------------------------------
#
whoami
pwd

# cloud-init 실행 과정 로그 확인
tail -f /var/log/cloud-init-output.log

# eks 설정 파일 확인
cat myeks.yaml

# cloud-init 정상 완료 후 eksctl 실행 과정 로그 확인
tail -f /root/create-eks.log

#
exit
-------------------------------------------------

EKS 배포가 된 모습을 콘솔에서 확인

로컬 환경에서 설치 확인 및 기본세팅

로컬에서 클러스터 관리 권한을 수행하기 위해 update-kubeconfig 를 수행하여 줍니다.

# 변수 지정
CLUSTER_NAME=myeks
SSHKEYNAME=kp-gasida

#
eksctl get cluster

# kubeconfig 생성
aws sts get-caller-identity --query Arn
aws eks update-kubeconfig --name myeks --user-alias <위 출력된 자격증명 사용자>
aws eks update-kubeconfig --name myeks --user-alias admin

# 
kubectl ns default
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
kubectl get pod -A
kubectl get pdb -n kube-system

노드 정보 확인 및 SSH 접속 확인

# EC2 공인 IP 변수 지정
export N1=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2a" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export N2=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2b" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export N3=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2c" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
echo $N1, $N2, $N3

# *remoteAccess* 포함된 보안그룹 ID
aws ec2 describe-security-groups --filters "Name=group-name,Values=*remoteAccess*" | jq
export MNSGID=$(aws ec2 describe-security-groups --filters "Name=group-name,Values=*remoteAccess*" --query 'SecurityGroups[*].GroupId' --output text)

# 해당 보안그룹 inbound 에 자신의 집 공인 IP 룰 추가
aws ec2 authorize-security-group-ingress --group-id $MNSGID --protocol '-1' --cidr $(curl -s ipinfo.io/ip)/32

# 해당 보안그룹 inbound 에 운영서버 내부 IP 룰 추가
aws ec2 authorize-security-group-ingress --group-id $MNSGID --protocol '-1' --cidr 172.20.1.100/32

# 워커 노드 SSH 접속
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -o StrictHostKeyChecking=no ec2-user@$i hostname; echo; done

쿠버네티스 운영서버에도 접속하여 동일한 과정을 통해 세팅을 해줍니다.

EKS 배포 후 실습 편의를 위한 설정 : macOS

실습을 완료한 이후에는 환경변수 설정을 삭제하시기 바랍니다.

# 변수 지정
export CLUSTER_NAME=myeks
export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" --query 'Vpcs[*].VpcId' --output text)
export PubSubnet1=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet2=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet2" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet3=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet3" --query "Subnets[0].[SubnetId]" --output text)
export N1=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2a" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export N2=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2b" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export N3=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2c" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export CERT_ARN=$(aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text) #사용 리전의 인증서 ARN 확인
MyDomain=gasida.link # 각자 자신의 도메인 이름 입력
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "$MyDomain." --query "HostedZones[0].Id" --output text)

# 실습 완료 후 삭제 할 것!
cat << EOF >> ~/.zshrc

# eksworkshop
export CLUSTER_NAME=myeks
export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" --query 'Vpcs[*].VpcId' --output text)
export PubSubnet1=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet2=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet2" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet3=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet3" --query "Subnets[0].[SubnetId]" --output text)
export N1=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2a" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export N2=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2b" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export N3=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2c" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export CERT_ARN=$(aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text)
MyDomain=gasida.link # 각자 자신의 도메인 이름 입력
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "$MyDomain." --query "HostedZones[0].Id" --output text)
EOF

# [신규 터미널] 확인
echo $CLUSTER_NAME $VPCID $PubSubnet1 $PubSubnet2 $PubSubnet3
echo $N1 $N2 $N3 $MyDomain $MyDnzHostedZoneId
tail -n 15 ~/.zshrc

1.2 ALB Controller, ExternalDNS, gp3 storageclass, kube-ops-view(Ingress) 설치

ALB Controller 설치

helm 으로 배포 합니다.

helm repo add eks https://aws.github.io/eks-charts
helm install aws-load-balancer-controller **eks/aws-load-balancer-controller** -n kube-system --set clusterName=$CLUSTER_NAME \
  --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller

ExternalDNS 구성 및 개인 도메인 적용

echo $MyDomain
curl -s https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml | MyDomain=**$MyDomain** MyDnzHostedZoneId=**$MyDnzHostedZoneId** envsubst | kubectl apply -f -

스토리지클래스 생성

PV로 사용할 스토리지를 생성합니다. gp3 타입

# gp3 스토리지 클래스 생성
cat <<EOF | kubectl apply -f -
kind: **StorageClass**
apiVersion: storage.k8s.io/v1
metadata:
  name: **gp3**
  annotations:
    **storageclass.kubernetes.io/is-default-class: "true"**
allowVolumeExpansion: true
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
  **type: gp3**
  allowAutoIOPSPerGBIncrease: 'true'
  encrypted: 'true'
  **fsType**: **xfs** # 기본값이 ext4
EOF
kubectl get sc

k-ops viewer 설치

파드스케줄링 모니터링을 실시간으로 확인하기위해 k-ops viewer 를 설치합니다.

# kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=**ClusterIP**  --set env.TZ="Asia/Seoul" --namespace kube-system
# kubeopsview 용 Ingress 설정 : group 설정으로 1대의 ALB를 여러개의 ingress 에서 공용 사용
echo $CERT_ARN
**cat <<EOF | kubectl apply -f -**
apiVersion: networking.k8s.io/v1
kind: **Ingress**
metadata:
  annotations:
    alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
    alb.ingress.kubernetes.io/**group.name: study**
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/load-balancer-name: $CLUSTER_NAME**-ingress-alb**
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/success-codes: 200-399
    alb.ingress.kubernetes.io/target-type: ip
  labels:
    app.kubernetes.io/name: kubeopsview
  name: kubeopsview
  namespace: kube-system
spec:
  ingressClassName: alb
  rules:
  - host: kubeopsview.$MyDomain
    http:
      paths:
      - backend:
          service:
            name: kube-ops-view
            port:
              **number: 8080**  # name: http
        path: /
        pathType: Prefix
**EOF**

ALB가 프로비저닝 될때 까지 잠시 기다렸다가 페이지에 접속합니다.

1.3 프로메테우스 & 그라파나(admin / prom-operator) 설치 : 대시보드 Import 17900 (Link)

이전 포스팅에서 프로메테우스와 그라파나 스택을 통해서 k8s 모니터링 실습한 내용을 실제로 적용하고 오토스케일링을 커스텀하기 위해서 프로메테우스와 그라파나를 설치하여줍니다.

배포 하기전에 그라파나 및 프로메테우스를 커스텀할 파라미터를 설정해줍니다. 금번 실습에서는 지난 실습과 다르게 편의를 위해서 프로메테우스/그라파나용 PV를 사용하지는 않습니다.

cat <<EOT > monitor-values.yaml
**prometheus**:
  prometheusSpec:
    scrapeInterval: "15s"
    evaluationInterval: "15s"
    podMonitorSelectorNilUsesHelmValues: false
    serviceMonitorSelectorNilUsesHelmValues: false
    retention: 5d
    retentionSize: "10GiB"

  # Enable vertical pod autoscaler support for prometheus-operator
  **verticalPodAutoscaler**:
    enabled: true

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - prometheus.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'

**grafana**:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: prom-operator
  **defaultDashboardsEnabled: false**

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - grafana.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'

**kube-state-metrics:**
  **rbac**:
    **extraRules**:
      - apiGroups: ["autoscaling.k8s.io"]
        resources: ["verticalpodautoscalers"]
        verbs: ["list", "watch"]
  **customResourceState**:
    enabled: true
    config:
      kind: **CustomResourceStateMetrics**
      spec:
        resources:
          - groupVersionKind:
              group: autoscaling.k8s.io
              kind: "VerticalPodAutoscaler"
              version: "v1"
            labelsFromPath:
              verticalpodautoscaler: [metadata, name]
              namespace: [metadata, namespace]
              target_api_version: [apiVersion]
              target_kind: [spec, targetRef, kind]
              target_name: [spec, targetRef, name]
            **metrics**:
              - name: "**vpa_containerrecommendations_target**"
                help: "VPA container recommendations for memory."
                each:
                  type: Gauge
                  gauge:
                    path: [status, recommendation, containerRecommendations]
                    valueFrom: [target, **memory**]
                    labelsFromPath:
                      container: [containerName]
                commonLabels:
                  resource: "memory"
                  unit: "byte"
              - name: "**vpa_containerrecommendations_target**"
                help: "VPA container recommendations for cpu."
                each:
                  type: Gauge
                  gauge:
                    path: [status, recommendation, containerRecommendations]
                    valueFrom: [target, **cpu**]
                    labelsFromPath:
                      container: [containerName]
                commonLabels:
                  resource: "cpu"
                  unit: "core"
  selfMonitor:
    enabled: true

**alertmanager:
  enabled: false
defaultRules:
  create: false**
**kubeControllerManager:
  enabled: false
kubeEtcd:
  enabled: false
kubeScheduler:
  enabled: false
prometheus-windows-exporter:
  prometheus:
    monitor:
      enabled: false**
EOT
  • Scrape Interval 및 Evaluation Interval: Prometheus가 메트릭을 수집하고 평가하는 간격을 각각 15초로 설정합니다.
  • Vertical Pod Autoscaler (VPA): Prometheus에 대한 VPA 지원을 활성화합니다.
  • Custom Resource State: CustomResourceStateMetrics를 활성화하고, VerticalPodAutoscaler에 대한 메트릭을 수집합니다. 이는 커스텀 메트릭을 사용하기 위한 설정입니다. 커스텀 메트릭은 다음과 같이 설정하였습니다.
    • vpa_containerrecommendations_target (cpu)
    • vpa_containerrecommendations_target (mem)

프로메테우스, 그라파나 접속하여 설치 확인하기

echo -e "https://prometheus.$MyDomain"
open "https://prometheus.$MyDomain" # macOS

# 그라파나 웹 접속 : **admin / prom-operator**
echo -e "https://grafana.$MyDomain"
    open "https://grafana.$MyDomain" # macOS

웹콘솔에서 인그레스 대상그룹 생성 되었는지 확인

⚠️ 이때 kube-opsviewer 포트가 조금 이상한데 이는 kube-opsviewer를 배포할 때 prometheus, grafna와 다르게 포트를 정수형이 아니라 http 문자열로 사용했는데 AWS 에서 인식이 안돼서 그런것으로 의심됩니다. 하지만 대상 통신을 하는데는 딱히 문제가 없으니 넘어가도 될듯합니다.

상세 리소스 확인

kube-state-metrics 의 서비스 어카운트 생성을 확인합니다. 서비스 어카운트의 config가 볼륨에 있는 config를 읽어와서 적용하는 것을 볼 수 있습니다.

kubectl get pod -n monitoring -l app.kubernetes.io/name=kube-state-metrics
**kubectl describe pod -n monitoring -l app.kubernetes.io/name=kube-state-metrics**
...
Service Account:  **kube-prometheus-stack-kube-state-metrics**
...
    Args:
      --port=8080
      --**resources**=certificatesigningrequests,configmaps,cronjobs,daemonsets,deployments,endpoints,horizontalpodautoscalers,ingresses,jobs,leases,limitranges,mutatingwebhookconfigurations,namespaces,networkpolicies,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,secrets,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments
      --**custom-resource-state-config-file**=/etc/customresourcestate/config.yaml
    ...
**Volumes**:
  **customresourcestate-config**:
    Type:      **ConfigMap** (a volume populated by a ConfigMap)
    Name:      kube-prometheus-stack-kube-state-metrics-customresourcestate-config
    Optional:  false
...
**kubectl describe cm -n monitoring kube-prometheus-stack-kube-state-metrics-customresourcestate-config**
...

kube-prometheus-stack-kube-state-metrics 의 clusterrole 을 살펴보면 이 ClusterRole이 verticalpodautoscalers ****라는 리소스에 접근할 수 있는 권한을 가지고 있다는 것을 나타냅니다.

해당 권한은 list, watch에 대한 권한으로 이는 Kube-State-Metrics가 VerticalPodAutoscaler의 상태를 모니터링할 수 있도록 허용하는 설정입니다.

kubectl get clusterrole kube-prometheus-stack-kube-state-metrics
kubectl describe clusterrole kube-prometheus-stack-kube-state-metrics
**kubectl describe clusterrole kube-prometheus-stack-kube-state-metrics | grep verticalpodautoscalers**
  verticalpodautoscalers.autoscaling.k8s.io                     []                 []              [list watch]

1.4 EKS Node Viewer 설치

⚠️ EKS Node Viewer는 노드 할당 가능 용량요청 request 리소스를 표시하는 것 입니다. 실제 파드 리소스 사용량 X (링크). 따라서 실제 Pod 사용량을 모니터링 하기 위해서 별도의 모니터링 환경을 구성해야 합니다. (ex., prometheus-grafana, cloudwatch)

설치하기

# 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분 정도 소요

사용 방법

아래 코드를 실행하면서 실습을 해보도록 합니다.

# 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 화면

eks-node-viewer --resources cpu,memory

라벨 필터 적용. extra-labes를 활용합니다. eks-node-viewer --resources cpu,memory --extra-labels eks-node-viewer/node-age

2. HPA - Horizontal Pod Autoscaler

2.1 그라파나 대시보드 설정

파드를 모니터링하기 위해서 그라파나 대시보드를 설정해줍니다.

대시보드 ID: 22128, 22251

현재는 데이터가 없기 때문에 메트릭이 출력되지 않습니다. 이제 샘플애플리케이션을 배포해서 메트릭을 확인해 봅니다.

2.2 hpa-example 배포

부하를 주기 위한 샘플 애플리케이션을 실행 시킬 것입니다. hpa-example : Dockerfile , index.php (CPU 과부하 연산 수행 , 100만번 덧셈 수행)

※ 참고: 애플리케이션 소스

FROM php:5-apache
COPY index.php /var/www/html/index.php
RUN chmod a+rx index.php
<?php
$x = 0.0001;
for ($i = 0; $i <= 1000000; $i++) {
            $x += sqrt($x);
}
echo "OK!";
?>

이제 애플리케이션을 배포해봅니다.

# 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 파드의 메트릭이 들어오는 지 확인합니다.

2.3 HPA 정책 생성 및 부하 발생 후 오토 스케일링 테스트

2.3.1 HPA 정책 생성

HPA 파드 오토스케일링을 하려면 트리거가 될 기준 정책이 필요합니다. 보통 autoscaling policy 라고 하며 이번실습에서는 다음과 같은 정책을 설정하도록 합니다.

증가 시 기본 대기 시간(30초), 감소 시 기본 대기 시간(5분) → 조정 가능

# 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
...
Events:
  Type    Reason             Age    From                       Message
  ----    ------             ----   ----                       -------
  Normal  SuccessfulRescale  13m    horizontal-pod-autoscaler  New size: 2; reason: cpu resource utilization (percentage of request) above target
  Normal  SuccessfulRescale  11m    horizontal-pod-autoscaler  New size: 3; reason: cpu resource utilization (percentage of request) above target
  Normal  SuccessfulRescale  11m    horizontal-pod-autoscaler  New size: 6; reason: cpu resource utilization (percentage of request) above target
  Normal  SuccessfulRescale  10m    horizontal-pod-autoscaler  New size: 8; reason: cpu resource utilization (percentage of request) above target
  Normal  SuccessfulRescale  5m35s  horizontal-pod-autoscaler  New size: 7; reason: All metrics below target
  Normal  SuccessfulRescale  4m35s  horizontal-pod-autoscaler  New size: 5; reason: All metrics below target
  Normal  SuccessfulRescale  4m5s   horizontal-pod-autoscaler  New size: 2; reason: All metrics below target
  Normal  SuccessfulRescale  3m50s  horizontal-pod-autoscaler  New size: 1; reason: All metrics below target

hpa 리소스가 제대로 설정되었는지, 정책이 제대로 적용되었는 지 확인합니다.

2.3.2 부하 발생

  • while true;do curl -s $PODIP; sleep 0.5; done 혹은
  • 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를 확인해보면 9/9 로 가득찬걸 확인할 수 있습니다.

그라파나에서도 파드가 급격히 증가하는 것을 확인할 수 있습니다.

프로메테우스 지표에서도 확인이 가능합니다. PromQL 입력창에 다음 지표를 입력합니다. kube_horizontalpodautoscaler_status_current_replicas

❓왜 일정수준 증가하다가 떨어지고 유지될까

⇒ 파드가 특정 수준까지 올라가면 해당 애플리케이션의 부하를 다수의 파드가 처리하기 때문에 임계치 수준(cpu 50%) 이상 까지 올라가지 않기 때문입니다. 로드밸런싱의 중요성을 확인할 수 있습니다.

❓스케일 아웃과 스케일 인의 진행속도 차이가 다른 이유

A구간은 부하를 발생시켜 스케일아웃이 일어나는 과정이고 B는 트래픽을 중단 시켜 스케일 인이 일어나는 과정입니다. 이에 있어서 속도가 차이나는 것은 보통 오토스케일링을 할때는 급격한 스케일 인으로 일어날 수 있는 장애 방지를 위해서 보수적으로 시간 간격을 잡기 때문입니다.

➕ More: Multiple Metrics 적용하기

다음과 같은 다양한 메트릭을 조합하여 평가하고 오토스케일링에 적용합니다.

  • Resource Metrics:
    • CPU, 메모리와 같은 리소스 사용률을 기반으로 스케일링합니다.
  • Pod Metrics:
    • Pod의 특정 메트릭(예: 패킷당 초당 수)을 평균화하여 스케일링합니다.
  • Object Metrics:
    • 특정 Kubernetes 객체(예: Ingress)와 관련된 메트릭을 사용하여 스케일링합니다.
    • 예: Ingress의 요청당 초당 수에 따라 스케일링.
  • External Metrics:
    • Kubernetes 외부의 메트릭(예: 큐의 대기 메시지 수)을 사용하여 스케일링합니다.
    • 예: 큐에 30개의 메시지가 대기 중일 때 스케일링.

(예시)

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:
        type: Utilization
        averageUtilization: 50
  - type: Pods
    pods:
      metric:
        name: packets-per-second
        target:
          type: AverageValue
          averageValue: 1k
  - type: Object
    object:
      metric:
        name: requests-per-second
        describedObject:
          apiVersion: networking.k8s.io/v1
          kind: Ingress
          name: main-route
        target:
          type: Value
          value: 10k

위 설정에서는 CPU 사용률, 패킷당 초당 수, Ingress의 요청당 초당 수를 모두 고려하여 스케일링을 결정합니다. HPA는 각 메트릭에 대해 제안된 복제본 수를 계산하고, 그 중 가장 높은 수를 선택하여 스케일링합니다.

2.4 관련 오브젝트 삭제

hpa 적용과 확인이 끝났으면 실습에 사용한 오브젝트를 정리하여 줍니다.

kubectl delete deploy,svc,hpa,pod --all

3. KEDA - Kubernetes based Event Driven Autoscaler

3.1 KEDA 란?

https://keda.sh/docs/2.16/

 

기존의 HPA(Horizontal Pod Autoscaler)는 리소스(CPU, Memory) 메트릭을 기반으로 스케일 여부를 결정하게 됩니다. 반면에 KEDA특정 이벤트를 기반으로 스케일 여부를 결정할 수 있습니다.

예를 들면 다음과 같은 상황이 있을 수 있습니다.

  • airflow는 metadb를 통해 현재 실행 중이거나 대기 중인 task가 얼마나 존재하는지 알 수 있습니다.
  • 이러한 이벤트를 활용하여 worker의 scale을 결정한다면 queue에 task가 많이 추가되는 시점에 더 빠르게 확장할 수 있습니다.
  • mariadb DB 커넥션이 100개 이상 증가하였을 때
  • kafka lag 증가 시 등

3.2 KEDA 실습

본 포스팅에서는 cron 잡을 트리거로 오토스케일링을 구현하는 실습을 하도록 합니다.

3.2.1 KEDA 대시보드 구성 (Grafana)

다음 링크에 접속해 그라파나 대시보드 config를 복사합니다. https://github.com/kedacore/keda/blob/main/config/grafana/keda-dashboard.json

3.2.2 KEDA 설치

KEDA를 설치하기 전에 기본 Metrics api를 확인해봅니다(순정). KEDA 를 설치한 이후에는 external api를 쓸수 있게 됩니다.

kubectl get --raw "/apis/metrics.k8s.io" | jq

 

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: **8020**
    serviceMonitor:
      # Enables ServiceMonitor creation for the Prometheus webhooks
      enabled: true
EOT

 

KEDA 설치 확인

# 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

External metric api 확인하기

external.metrics.k8s.io API를 통해서 이제 외부의 메트릭을 사용으로 오토스케일링 트리거를 설정할 수 있게 됩니다.

 

# 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

 

 

KEDA를 적용하기 위해 샘플 애플리케이션을 배포합니다. 위에서 배포했던 애플리케이션을 재활용합니다.

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

 

KEDA 오토스케일링 정책 설정

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**
  • triggers: cron 으로 시간에 맞춰 오토스케일링을 진행
  • timezone 설정이 가능합니다.
  • start/ end 는 pod의 up/down 과 동일합니다.
  • desiredReplicas: 1. 하나의 파드를 스케일링 합니다.

KEDA는 hpa 에 적용될 메트릭을 external 로 사용하는 것을 확인할 수 있습니다.

 

그라파나에서 KEDA 대시보드에 들어간 후 네임스페이스를 keda로 변경해 줍니다.

메트릭 서버가 인식된 것을 확인할 수 있습니다.

 

파드의 라이프사이클을 확인합니다.

 

Horizontal Pod Autoscaler (HPA) 대시보드로 오면 위에서 KEDA cron에 설정한 시간 간격으로 파드가 1개 올라왔다 내려온 것을 확인할 수 있습니다.

3.2.3 KEDA 오브젝트 삭제

실습에 사용한 오브젝트를 삭제합니다.

# 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

4. VPA - Vertical Pod Autoscaler

4.1 VPA 란?

pod resources.request을 최대한 최적값으로 수정하는 것입니다.

HPA와는 다르게 파드 수를 조정하는 것이 아니라 파드에 할당된 리소스를 조정하는 것입니다.

4.1.1 VPA 고려사항

  • VPA는 HPA와 같이 사용할 수 없습니다.
  • ⚠️ VPA는 pod자원을 최적값으로 수정하기 위해 pod를 재실행(기존 pod를 종료하고 새로운 pod실행)합니다. 따라서 실제 운영환경에서 주의할 필요가 있습니다.
  • 계산 방식 : ‘기준값(파드가 동작하는데 필요한 최소한의 값)’ 결정 → ‘마진(약간의 적절한 버퍼)’ 추가 → 상세정리 Link

4.1.2 VPA 워크플로우

위 이미지는 VPA가 Pod의 리소스를 자동으로 최적화하여 효율적인 클러스터 운영을 지원하는 과정을 보여줍니다.

  1. VPA 구성:
    • 사용자가 VPA를 설정합니다. 여기에는 리소스 추천 정책 및 대상 Deployment를 지정하는 작업이 포함됩니다.
  2. Metrics Server로부터 메트릭 수집:
    • Metrics Server는 Pod의 리소스 사용량(예: CPU, 메모리)을 모니터링하고 데이터를 제공합니다.
  3. VPA Recommender가 리소스 추천 계산:
    • VPA Recommender는 Metrics Server에서 수집한 데이터를 기반으로 Pod의 적절한 리소스 요구 사항을 계산합니다.
  4. Pod 리소스 추천 제공:
    • VPA Recommender는 계산된 리소스 추천을 VPA Updater와 VPA Admission Controller로 전달합니다.
  5. Pod 종료 (VPA Updater):
    • VPA Updater는 기존 Pod를 종료하여 새로운 리소스 스펙을 적용할 준비를 합니다.
  6. 새로운 Pod 생성 (Deployment):
    • Deployment가 새로운 Pod를 생성합니다.
  7. VPA Admission Controller가 리소스 추천 적용:
    • 새로운 Pod가 생성될 때, VPA Admission Controller는 추천된 리소스를 Pod 스펙에 적용합니다.
  8. 최종 리소스 스펙 적용:
    • 새롭게 생성된 Pod에 추천된 리소스 스펙(예: CPU 500m)이 적용됩니다.

리소스를 계산하는 로직은 다음 devocean 블로그에 잘 정리가 되어있습니다. https://devocean.sk.com/blog/techBoardDetail.do?ID=164786

4.2 VPA 실습

4.2.1 그라파나 대시보드 설정

그라파나 대시보드 ID: 14588

4.2.2 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

4.2.3 VPA 발생 (부하 시나리오)

공식 예제 : pod가 실행되면 약 2~3분 뒤에 pod resource.reqeust가 VPA에 의해 수정됩니다. - 링크

vpa에 spec.updatePolicy.updateModeOff 로 변경 시 파드에 Spec을 자동으로 변경 재실행 하지 않습니다. 기본값(Auto)

# 모니터링
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**
2m16s       Normal    EvictedByVPA             pod/hamster-5bccbb88c6-s6jkp         Pod was evicted by VPA Updater to apply resource recommendation.
76s         Normal    EvictedByVPA             pod/hamster-5bccbb88c6-jc6gq         Pod was evicted by VPA Updater to apply resource recommendation.

위 명세서를 배포하면 다음과 같은 리소스가 생성됩니다.

  • 햄스터 라는 이름의 deployment
    • 애플리케이션은 반복적으로 0.5초 동안 CPU를 사용하고, 0.5초 동안 대기하는 작업을 수행합니다.
    • 리소스 요청:
      • CPU: 100m
      • 메모리: 50Mi
  • VPA는 Pod의 리소스 요청(CPU 및 메모리)을 동적으로 조정합니다.
    • 최소 리소스 제한: CPU 100m, 메모리 50Mi
    • 최대 리소스 제한: CPU 1, 메모리 500Mi

 

시간이 지나면서 새로운 파드가 생성 되었다가 리소스(requests)가 증가합니다.

초기 CPU가 100m 이었던게 587m으로 바뀌었고 메모리도 request가 늘어난 것을 확인할 수 있습니다.

 

기존에 100m, 50Mi 로 생성됐던 파드는 내려간 것을 볼수 있습니다.

 

이는 kubectl events 에서도 확인 가능합니다.

 

그라파나에서도 해당 과정을 살펴볼 수 있습니다.

4.3 실습 오브젝트 지우기

kubectl delete -f examples/hamster.yaml && cd ~/autoscaler/vertical-pod-autoscaler/ && ./hack/vpa-down.sh