1. AutoScaling 이론 및 실습환경
2. HPA - Horizontal Pod Autoscaler
3. VPA - Vertical Pod Autoscaler
4. CAS - Cluster Autoscaler
5. Karpenter : K8S Native AutoScaler
6. 후기
1. Autoscaling 이론 & 실습환경
리소스 부족대응 기술
- VPA : 서비스를 처리할 파드 자원이 부족한 경우 파드 교체(자동 or 수동) → 파드 Scale Up
- HPA : 서비스를 처리할 파드 자원이 부족한 경우 신규 파드 Provisioning → 파드 Scale Out
- CAS : 파드를 배포할 노드가 부족한 경우 신규 노드 Provisioning → 노드 Scale Out
- Karpenter : Unscheduled 파드가 있는 경우 새로운 노드 및 파드 Provisioning → 노드 Scale Up/Out

|
AWS Auto Scaling 한계
- 오토스케일링 전략은 EC2의 오토스케일링 그룹 사용을 중심으로 동작합니다
- 노드 그룹에서 인스턴스 타입이 동일하다고 가정합니다.
- 혼합 인스턴스 타입은 가능한 CPU와 메모리가 균등하게 되어야 합니다.
- 다양한 인스턴스 타입을 지원하기 위해서는 여러 노드 그룹이 필요합니다.
- 모범사례는 AZ당 노드 그룹을 가지는 것입니다.
카펜터 :
- 유연한 노드 프로비저닝, 빠른 스케일링 및 비용 최적화, 똑똑한 통합 관리 및 간소화된 설정,
향상된 리소스 활용 및 확장성
실습환경
| # 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 자신의 PC에서 AWS EKS 설치 확인 # 변수 지정 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 # 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 |
변수설정
| 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=hey-aws.click MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "$MyDomain." --query "HostedZones[0].Id" --output text) echo $CLUSTER_NAME $VPCID $PubSubnet1 $PubSubnet2 $PubSubnet3 echo $N1 $N2 $N3 $MyDomain $MyDnzHostedZoneId |
AWS LoadBalancer Controller, ExternalDNS, gp3 storageclass, kube-ops-view(Ingress) 설치
| # AWS LoadBalancerController 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 - # 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 # 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 |
| # repo 추가 helm repo add prometheus-community https://prometheus-community.github.io/helm-charts # 파라미터 파일 생성 : PV/PVC(AWS EBS) 삭제에 불편하니, 4주차 실습과 다르게 PV/PVC 미사용 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 cat monitor-values.yaml # helm 배포 helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 69.3.1 \ -f monitor-values.yaml --create-namespace --namespace monitoring # helm 확인 helm get values -n monitoring kube-prometheus-stack # PV 사용하지 않음 kubectl get pv,pvc -A kubectl df-pv # 프로메테우스 웹 접속 echo -e "https://prometheus.$MyDomain" open "https://prometheus.$MyDomain" # macOS # 그라파나 웹 접속 : admin / prom-operator echo -e "https://grafana.$MyDomain" open "https://grafana.$MyDomain" # macOS # kubectl get targetgroupbindings.elbv2.k8s.aws -A # 상세 확인 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 ... # 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] (옵션) 4주차 노션 확인하여 17900 대시보드에 PromQL/Variables 수정 ![]() |
EKS Node Viewer : 노드 할당 가능 용량과 요청 request 리소스 표시, 실제 파드 리소스 사용량 X
- Node마다 할당 가능한 용량과 스케줄링된 POD(컨테이너)의 Resource 중 request 값을 표시한다.
- 실제 POD(컨테이너) 리소스 사용량은 아니다. /pkg/model/pod.go 파일을 보면 컨테이너의 request 합을 반환하며, init containers는 미포함
| # Windows 에 WSL2 (Ubuntu) 설치 sudo apt install golang-go go install github.com/awslabs/eks-node-viewer/cmd/eks-node-viewer@latest # 설치 시 2~3분 정도 소요 에러 발생 go: downloading github.com/awslabs/eks-node-viewer v0.7.1 go: github.com/awslabs/eks-node-viewer/cmd/eks-node-viewer@latest (in github.com/awslabs/eks-node-viewer@v0.7.1): go.mod:3: invalid go version '1.22.5': must match format 1.23 go.mod:5: unknown directive: toolchain go 재설치 wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz sudo tar -C /usr/local -xzf go1.23.0.linux-amd64.tar.gz export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:/root/go/bin # 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 |
2. HPA - Horizontal Pod Autoscaler
hpa-example : Dockerfile , index.php (CPU 과부하 연산 수행 , 100만번 덧셈 수행)
- 그라파나(22128 , 22251) 대시보드 Import 설정

| # 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 정책 생성 및 부하 발생 후 오토 스케일링 테스트 : 증가 시 기본 대기 시간(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 ![]() # HPA 설정 확인 kubectl get hpa php-apache -o yaml | kubectl neat 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 (서비스명 도메인으로 파드들 분산 접속) >> 증가 확인(몇개까지 증가되는가? 그 이유는?) 후 중지 ## >> [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 ![]() |
| kube_horizontalpodautoscaler_status_current_replicas kube_horizontalpodautoscaler_status_desired_replicas kube_horizontalpodautoscaler_status_target_metric kube_horizontalpodautoscaler_status_condition kube_horizontalpodautoscaler_spec_target_metric kube_horizontalpodautoscaler_spec_min_replicas kube_horizontalpodautoscaler_spec_max_replicas # [운영서버 EC2] kubectl get pod -n monitoring -l app.kubernetes.io/name=kube-state-metrics -owide kubectl get pod -n monitoring -l app.kubernetes.io/name=kube-state-metrics -o jsonpath="{.items[*].status.podIP}" PODIP=$(kubectl get pod -n monitoring -l app.kubernetes.io/name=kube-state-metrics -o jsonpath="{.items[*].status.podIP}") curl -s http://$PODIP:8080/metrics | grep -i horizontalpodautoscaler | grep HELP curl -s http://$PODIP:8080/metrics | grep -i horizontalpodautoscaler |


HPA : Autoscaling on multiple metrics and custom metrics
- kubectl apply -f https://k8s.io/examples/application/php-apache.yaml https://k8s.io/examples/application/hpa/php-apache.yaml
| apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: php-apache spec: minReplicas: 1 maxReplicas: 10 scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: php-apache 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 |
새로 만들어진 HorizontalPodAutoscaler의 현재 상태를 확인

부하증가
| 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" |


HorizontalPodAutoscaler를 생성하기 위해 kubectl autoscale 명령어를 사용하지 않고, 명시적으로 다음 매니페스트를 사용하여 만들 수 있다.
| apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: name: php-apache spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: php-apache minReplicas: 1 maxReplicas: 10 targetCPUUtilizationPercentage: 50 |
2. KEDA - Kubernetes based Event Driven Autoscaler
기존의 HPA(Horizontal Pod Autoscaler)는 리소스(CPU, Memory) 메트릭을 기반으로 스케일 여부를 결정하게 됩니다.
반면에 KEDA는 특정 이벤트를 기반으로 스케일 여부를 결정
KEDA 대시보드
Import : https://github.com/kedacore/keda/blob/main/config/grafana/keda-dashboard.json
# 설치 전 기존 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 설치
| 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" ] } ] } |
# kubectl apply -f php-apache.yaml -n keda
| 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 |
# ScaledObject 정책 생성 : cron
| # 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
# 그라파나 대시보드 추가 : 대시보드 상단에 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 { "maxReplicas": 2, "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" } ], "minReplicas": 1, "scaleTargetRef": { "apiVersion": "apps/v1", "kind": "Deployment", "name": "php-apache" } } |
15분 후 Scale up 된 것을 볼수 있다.

# 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 |
3. VPA - Vertical Pod Autoscaler
VPA : pod resources.request을 최대한 최적값으로 수정
- VPA는 HPA와 같이 사용할 수 없습니다.
- VPA는 pod자원을 최적값으로 수정하기 위해 pod를 재실행(기존 pod를 종료하고 새로운 pod실행)합니다.
- 계산 방식 : ‘기준값(파드가 동작하는데 필요한 최소한의 값)’ 결정 → ‘마진(약간의 적절한 버퍼)’ 추가

그라파나 대시보드 : 상단 cluster 는 현재 프로메테우스 없음 14588
운영서버 접근
| ssh -i ssh-250209.pem ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text) |
| # [운영서버 EC2] 코드 다운로드 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 # 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 |
| # 모니터링 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 ![]() # VPA에 의해 기존 파드 삭제되고 신규 파드가 생성됨 kubectl get events --sort-by=".metadata.creationTimestamp" | grep VPA ![]() |
그라파나 확인

4. CAS - Cluster Autoscaler
- Cluster Autoscale 동작을 하기 위한 cluster-autoscaler 파드(디플로이먼트)를 배치합니다.
- Cluster Autoscaler(CAS)는 pending 상태인 파드가 존재할 경우, 워커 노드를 스케일 아웃합니다.
- 특정 시간을 간격으로 사용률을 확인하여 스케일 인/아웃을 수행합니다. 그리고 AWS에서는 Auto Scaling Group(ASG)을 사용하여 Cluster Autoscaler를 적용합니다.
설정확인
# 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 json | jq aws ec2 describe-instances --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Reservations[*].Instances[*].Tags[*]" --output yaml . ![]() 태그에는 보이지 않는다. ![]() # 현재 autoscaling(ASG) 정보 확인 aws autoscaling describe-auto-scaling-groups \ --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" \ --output table ![]() # 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 ![]() curl -s -O https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml - ./cluster-autoscaler - --v=4 - --stderrthreshold=info - --cloud-provider=aws - --skip-nodes-with-local-storage=false # 로컬 스토리지를 가진 노드를 autoscaler가 scale down할지 결정, false(가능!) - --expander=least-waste # 노드를 확장할 때 어떤 노드 그룹을 선택할지를 결정, least-waste는 리소스 낭비를 최소화하는 방식으로 새로운 노드를 선택. - --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/<YOUR CLUSTER NAME> ... sed -i -e "s|<YOUR CLUSTER NAME>|$CLUSTER_NAME|g" cluster-autoscaler-autodiscover.yaml kubectl apply -f cluster-autoscaler-autodiscover.yamlhttps://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml kubectl get pod -n kube-system | grep cluster-autoscaler kubectl describe deployments.apps -n kube-system cluster-autoscaler kubectl describe deployments.apps -n kube-system cluster-autoscaler | grep node-group-auto-discovery --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/myeks #cluster-autoscaler 파드가 동작하는 워커 노드가 퇴출(evict) 되지 않게 설정 kubectl -n kube-system annotate deployment.apps/cluster-autoscaler cluster-autoscaler.kubernetes.io/safe-to-evict="false" |
테스트
| # 모니터링 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 --resources cpu,memory 혹은 eks-node-viewer ![]() ![]() ![]() ![]() # [운영서버 EC2] 최근 1시간 Fleet API 호출 확인 # https://ap-northeast-2.console.aws.amazon.com/cloudtrailv2/home?region=ap-northeast-2#/events?EventName=CreateFleet aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=CreateFleet \ --start-time "$(date -d '1 hour ago' --utc +%Y-%m-%dT%H:%M:%SZ)" \ --end-time "$(date --utc +%Y-%m-%dT%H:%M:%SZ)" ![]() # (참고) Event name : UpdateAutoScalingGroup # https://ap-northeast-2.console.aws.amazon.com/cloudtrailv2/home?region=ap-northeast-2#/events?EventName=UpdateAutoScalingGroup # 디플로이먼트 삭제 kubectl delete -f nginx.yaml && date # [scale-down] 노드 갯수 축소 : 기본은 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. 10분 후 축소된것을 확인할 수 있다. ![]() |
5. Karpenter : K8S Native AutoScaler
- 고성능의 지능형 k8s 컴퓨팅 프로비저닝 및 관리 솔루션, 수초 이내에 대응 가능, 더 낮은 컴퓨팅 비용으로 노드 선택
- 지능형의 동적인 인스턴스 유형 선택 - Spot, AWS Graviton 등
- 자동 워크로드 Consolidation 기능
- 일관성 있는 더 빠른 노드 구동시간을 통해 시간/비용 낭비 최소화



Consolidation 동작 흐름
1. 지속적인 상태 분석
- Karpenter는 클러스터 상태를 실시간으로 분석하며, 노드의 자원 사용률과 Pod 요구사항을 모니터링합니다.
2. 비효율적 노드 발견
- 낮은 자원 사용률의 노드를 발견하거나, 더 효율적으로 활용 가능한 노드를 찾습니다.
3.임시 시뮬레이션(드라이런) 수행
- Karpenter는 즉시 노드를 삭제하지 않고, 미리 시뮬레이션하여 Pod를 다른 노드에 옮길 수 있는지 점검합니다.
- 이 과정에서 Pod Disruption Budget(PDB), Node Affinity, Taint/Toleration 같은 Kubernetes 제약사항도 함께 고려됩니다.
4. Pod 이동 및 노드 축소(Scale-in)
- 시뮬레이션이 성공하면, 새로운 노드를 생성하거나 이미 존재하는 다른 노드로 Pod를 안전하게 옮긴 후, 비효율적인 노드를 축소(제거)합니다.
Consolidation의 장점
- 비용 절감: 불필요한 리소스 낭비를 최소화하여 클라우드 비용을 절약합니다.
- 효율적인 리소스 관리: 클러스터 내 리소스의 낭비를 방지하여 전체 효율성을 높입니다.
- 자동화된 최적화: 지속적으로 모니터링하고 최적화를 수행하여 별도의 수동 작업 없이 클러스터를 최적 상태로 유지합니다.
주요 고려사항 (주의사항)
- Consolidation이 수행될 때 Pod의 짧은 다운타임이 발생할 수 있습니다.
이를 방지하려면 Pod Disruption Budget(PDB)를 명확하게 설정하는 것이 권장됩니다. - 너무 잦은 Consolidation은 오히려 빈번한 노드 재배치를 일으켜 성능에 영향을 줄 수 있으므로, Karpenter 설정에서 Consolidation 주기와 민감도를 적절히 조정할 필요가 있습니다.
Consolidation 설정 옵션
- enabled: Consolidation 활성화 여부 (true/false)
- policy: Consolidation 정책을 지정하며, 보통 WhenUnderutilized를 사용합니다.
실습환경 구성
환경변수
| export KARPENTER_NAMESPACE="kube-system" export KARPENTER_VERSION="1.2.1" export K8S_VERSION="1.32" export AWS_PARTITION="aws" # if you are not using standard partitions, you may need to configure to aws-cn / aws-us-gov export CLUSTER_NAME="250308-karpenter-demo" # ${USER}-karpenter-demo export AWS_DEFAULT_REGION="ap-northeast-2" export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)" export TEMPOUT="$(mktemp)" 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 "${KARPENTER_NAMESPACE}" "${KARPENTER_VERSION}" "${K8S_VERSION}" "${CLUSTER_NAME}" "${AWS_DEFAULT_REGION}" "${AWS_ACCOUNT_ID}" "${TEMPOUT}" "${ARM_AMI_ID}" "${AMD_AMI_ID}" "${GPU_AMI_ID}" ![]() |
EKS 설치
| curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/v"${KARPENTER_VERSION}"/website/content/en/preview/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}" # 클러스터 생성 : EKS 클러스터 생성 15분 정도 소요 eksctl create cluster -f - <<EOF --- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: ${CLUSTER_NAME} region: ${AWS_DEFAULT_REGION} version: "${K8S_VERSION}" tags: karpenter.sh/discovery: ${CLUSTER_NAME} iam: withOIDC: true podIdentityAssociations: - namespace: "${KARPENTER_NAMESPACE}" serviceAccountName: karpenter roleName: ${CLUSTER_NAME}-karpenter permissionPolicyARNs: - arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy-${CLUSTER_NAME} iamIdentityMappings: - arn: "arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}" username: system:node:{{EC2PrivateDNSName}} groups: - system:bootstrappers - system:nodes ## If you intend to run Windows workloads, the kube-proxy group should be specified. # For more information, see https://github.com/aws/karpenter/issues/5099. # - eks:kube-proxy-windows managedNodeGroups: - instanceType: m5.large amiFamily: AmazonLinux2023 name: ${CLUSTER_NAME}-ng desiredCapacity: 2 minSize: 1 maxSize: 10 iam: withAddonPolicies: externalDNS: true addons: - name: eks-pod-identity-agent EOF # eks 배포 확인 eksctl get cluster eksctl get nodegroup --cluster $CLUSTER_NAME eksctl get iamidentitymapping --cluster $CLUSTER_NAME eksctl get iamserviceaccount --cluster $CLUSTER_NAME eksctl get addon --cluster $CLUSTER_NAME ![]() # kubectl ctx kubectl config rename-context "admin@250308-karpenter-demo.ap-northeast-2.eksctl.io" "karpenter-demo" # k8s 확인 kubectl ns default kubectl cluster-info kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone kubectl get pod -n kube-system -owide kubectl get pdb -A kubectl describe cm -n kube-system aws-auth # EC2 Spot Fleet의 service-linked-role 생성 확인 aws iam create-service-linked-role --aws-service-name spot.amazonaws.com || true |
install Karpenter
| # Logout of helm registry to perform an unauthenticated pull against the public ECR helm registry logout public.ecr.aws # Karpenter 설치를 위한 변수 설정 및 확인 export CLUSTER_ENDPOINT="$(aws eks describe-cluster --name "${CLUSTER_NAME}" --query "cluster.endpoint" --output text)" export KARPENTER_IAM_ROLE_ARN="arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:role/${CLUSTER_NAME}-karpenter" echo "${CLUSTER_ENDPOINT} ${KARPENTER_IAM_ROLE_ARN}" # karpenter 설치 helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version "${KARPENTER_VERSION}" --namespace "${KARPENTER_NAMESPACE}" --create-namespace \ --set "settings.clusterName=${CLUSTER_NAME}" \ --set "settings.interruptionQueue=${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 \ --wait # 확인 helm list -n kube-system kubectl get-all -n $KARPENTER_NAMESPACE kubectl get all -n $KARPENTER_NAMESPACE kubectl get crd | grep 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 # 프로메테우스 얼럿매니저 미사용으로 삭제 kubetl delete sts -n monitoring prometheus-alertmanager # 프로메테우스 접속 설정 export POD_NAME=$(kubectl get pods --namespace monitoring -l "app.kubernetes.io/name=prometheus,app.kubernetes.io/instance=prometheus" -o jsonpath="{.items[0].metadata.name}") kubectl --namespace monitoring port-forward $POD_NAME 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 datasources: datasources.yaml: apiVersion: 1 datasources: - name: Prometheus type: prometheus version: 1 url: http://prometheus-server:80 access: proxy dashboardProviders: dashboardproviders.yaml: apiVersion: 1 providers: - name: 'default' orgId: 1 folder: '' type: file disableDeletion: false editable: true options: path: /var/lib/grafana/dashboards/default dashboards: default: capacity-dashboard: url: https://karpenter.sh/preview/getting-started/getting-started-with-karpenter/karpenter-capacity-dashboard.json performance-dashboard: url: https://karpenter.sh/preview/getting-started/getting-started-with-karpenter/karpenter-performance-dashboard.json # admin 암호 kubectl get secret --namespace monitoring grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo Q8LA2NVEPPTs6MZIBYwxNoFgSVDvoWvDmdnquqtJ # 그라파나 접속 kubectl port-forward --namespace monitoring svc/grafana 3000:80 & open http://127.0.0.1:3000 |
Create NodePool
| echo $ALIAS_VERSION v20250228 # 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 ![]() |
디플로이먼트 배포
# pause 파드 1개에 CPU 1개 최소 보장 할당할 수 있게 디플로이먼트 배포 cat <<EOF | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: inflate spec: replicas: 0 selector: matchLabels: app: inflate template: metadata: labels: app: inflate spec: terminationGracePeriodSeconds: 0 securityContext: runAsUser: 1000 runAsGroup: 3000 fsGroup: 2000 containers: - name: inflate image: public.ecr.aws/eks-distro/kubernetes/pause:3.7 resources: requests: cpu: 1 securityContext: allowPrivilegeEscalation: false EOF # [신규 터미널] 모니터링 eks-node-viewer --resources cpu,memory eks-node-viewer --resources cpu,memory --node-selector "karpenter.sh/registered=true" --extra-labels eks-node-viewer/node-age # Scale up kubectl get pod kubectl scale deployment inflate --replicas 5 # 출력 로그 분석해보자! kubectl logs -f -n "${KARPENTER_NAMESPACE}" -l app.kubernetes.io/name=karpenter -c controller kubectl logs -f -n "${KARPENTER_NAMESPACE}" -l app.kubernetes.io/name=karpenter -c controller | jq '.' kubectl logs -n "${KARPENTER_NAMESPACE}" -l app.kubernetes.io/name=karpenter -c controller | grep 'launched nodeclaim' | jq '.' ![]() # 확인 kubectl get nodeclaims NAME TYPE CAPACITY ZONE NODE READY AGE default-8f5vd c5a.2xlarge on-demand ap-northeast-2c ip-192-168-176-171.ap-northeast-2.compute.internal True 79s kubectl describe nodeclaims ![]() # (옵션) Scale up kubectl scale deployment inflate --replicas 30 10개 일떄 ![]() 15 개일때 ![]() 삭제 후 ![]() 후 삭제되는 것을 볼수있다. ![]() ![]() |
6. 후기
여러 Auto Scaling 기법을 테스트 할 수 있었으며
localhost에 쿠버네티스 서비스 portforwording 해서 쓰는 방식도 생각보다 편하게 서비스를 접근할 수 있어 참고해야겠다.


































