HPA

HPA는 Deployment, StatefulSet과 같은 workload resource를 자동으로 업데이트하며 workload의 크기를 자동으로 수요에 맞게 하는 것을 목표로 한다.

a HorizontalPodAutoscaler automatically updates a workload resource (such as a Deployment or StatefulSet), with the aim of automatically scaling the workload to match demand.

HPA를 사용하기 위해서는 Metric Server를 별도로 배포하여야 한다. Metric Server Install, Metric Server Configuration


1. 동작방법

metric-server-architecture

hpa001

  1. cAdvisor가 Pod내 Container들의 Metrics을 수집함.
  2. kubelet이 cAdvisor가 노출한 메트릭을 수집함.
  3. metric-server가 kubelet으로 부터 metric 리소스를 수집함. 이 주기는 –metric-resolution=15s 옵션에 따라 수집 됨
     $ kubectl logs -f metrics-server-58fd485c7-nnltl -n kube-system
     # Kubelet으로부터 메트릭 정보를 수집하고 있다. 
     I1222 15:01:24.847741       1 scraper.go:115] "Scraping metrics from nodes" nodeCount=2
     I1222 15:01:24.855038       1 scraper.go:137] "Scraping node" node="ip-10-20-10-235.ap-northeast-2.compute.internal"
     I1222 15:01:24.857187       1 scraper.go:137] "Scraping node" node="ip-10-20-11-91.ap-northeast-2.compute.internal"
     I1222 15:01:24.877046       1 scraper.go:172] "Scrape finished" duration="29.273444ms" nodeCount=2 podCount=17
    
  4. API 서버는 Metrics API를 제공하므로써 사용자, HPA등에게 API를 제공할 수 있다.
    따라서, 아래 kubectl 커맨드를 통해 메트릭을 조회하면 실제로는 메트릭 서버에서 아래와 같이 로그가 보인다. (configured by - –v=5)
     $ kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes/ip-10-20-10-235.ap-northeast-2.compute.internal" | jq '.'
    
     $ kubectl logs -f metrics-server-58fd485c7-nnltl -n kube-system
     # srcIP=x-ENI의 주소 중 하나다. 내 로컬 -> API server -> metrics-server로 왔기 때문에. 
     I1222 15:01:10.534483       1 handler.go:143] metrics-server: GET "/apis/metrics.k8s.io/v1beta1/nodes/ip-10-20-10-235.ap-northeast-2.compute.internal" satisfied by gorestful with webservice /apis/metrics.k8s.io/v1beta1
     I1222 15:01:10.534721       1 httplog.go:129] "HTTP" verb="GET" URI="/apis/metrics.k8s.io/v1beta1/nodes/ip-10-20-10-235.ap-northeast-2.compute.internal" latency="432.784µs" userAgent="kubectl/v1.26.0 (linux/amd64) kubernetes/b46a3f8" audit-ID="ae15fa19-9c33-4ebf-8f7d-15931ba355e0" srcIP="10.20.10.23:40266" resp=200
    

    아래는 HPA를 배포한 리소스에 대한 Metrics Server로그이다.

       # 아래도 주기적으로 관찰되는 로그이며 이는 php-apche hpa에의해 주기적으로 15초마다 조회되는 결과다.  
       I1222 15:01:37.041375       1 handler.go:143] metrics-server: GET "/apis/metrics.k8s.io/v1beta1/namespaces/default/pods" satisfied by gorestful with webservice /apis/metrics.k8s.io/v1beta1
       I1222 15:01:37.041560       1 httplog.go:129] "HTTP" verb="LIST" URI="/apis/metrics.k8s.io/v1beta1/namespaces/default/pods?labelSelector=run%3Dphp-apache" latency="4.32772ms" userAgent="kube-controller-manager/v1.21.14 (linux/amd64) kubernetes/b07006b/system:serviceaccount:kube-system:horizontal-pod-autoscaler" audit-ID="de1229ce-5f8f-4636-8698-0ff3003aca05" srcIP="10.20.11.208:32858" resp=200
    
       I1222 15:01:52.104167       1 handler.go:143] metrics-server: GET "/apis/metrics.k8s.io/v1beta1/namespaces/default/pods" satisfied by gorestful with webservice /apis/metrics.k8s.io/v1beta1
       I1222 15:01:52.104352       1 httplog.go:129] "HTTP" verb="LIST" URI="/apis/metrics.k8s.io/v1beta1/namespaces/default/pods?labelSelector=run%3Dphp-apache" latency="3.059605ms" userAgent="kube-controller-manager/v1.21.14 (linux/amd64) kubernetes/b07006b/system:serviceaccount:kube-system:horizontal-pod-autoscaler" audit-ID="b57ab53f-0194-41e2-99b1-50c7dc429e03" srcIP="10.20.11.208:32858" resp=200
    
       I1222 15:02:07.127963       1 handler.go:143] metrics-server: GET "/apis/metrics.k8s.io/v1beta1/namespaces/default/pods" satisfied by gorestful with webservice /apis/metrics.k8s.io/v1beta1
       I1222 15:02:07.128194       1 httplog.go:129] "HTTP" verb="LIST" URI="/apis/metrics.k8s.io/v1beta1/namespaces/default/pods?labelSelector=run%3Dphp-apache" latency="4.949269ms" userAgent="kube-controller-manager/v1.21.14 (linux/amd64) kubernetes/b07006b/system:serviceaccount:kube-system:horizontal-pod-autoscaler" audit-ID="5f545f72-bb1b-4cce-a7e6-a5681235f1bc" srcIP="10.20.11.208:32858" resp=200
    
       I1222 15:02:22.205786       1 handler.go:143] metrics-server: GET "/apis/metrics.k8s.io/v1beta1/namespaces/default/pods" satisfied by gorestful with webservice /apis/metrics.k8s.io/v1beta1
       I1222 15:02:22.206026       1 httplog.go:129] "HTTP" verb="LIST" URI="/apis/metrics.k8s.io/v1beta1/namespaces/default/pods?labelSelector=run%3Dphp-apache" latency="3.820725ms" userAgent="kube-controller-manager/v1.21.14 (linux/amd64) kubernetes/b07006b/system:serviceaccount:kube-system:horizontal-pod-autoscaler" audit-ID="b591499b-e8f3-4313-9f7a-ab2395eaac70" srcIP="10.20.11.208:32858" resp=200
    
  5. kube-controller(HPA controller)가 metrics API의 데이터를 보고 RS replica를 조정함 (15초)

2. HPA 테스트

  1. 아래와 같이 php-apache에 대한 Deployment, Service, HPA 리소스를 배포한다.

     apiVersion: apps/v1
     kind: Deployment
     metadata:
       name: php-apache1
     spec:
       selector:
         matchLabels:
           run: php-apache1
       replicas: 2
       template:
         metadata:
           labels:
             run: php-apache1
         spec:
           containers:
           - name: php-apache1
             image: registry.k8s.io/hpa-example
             ports:
             - containerPort: 80
             resources:
               limits:
                 cpu: 500m
               requests:
                 cpu: 200m
     ---
     apiVersion: v1
     kind: Service
     metadata:
       name: php-apache1
       labels:
         run: php-apache1
     spec:
       ports:
       - port: 80
       selector:
         run: php-apache1
     ---
     apiVersion: autoscaling/v1
     kind: HorizontalPodAutoscaler
     metadata:
       name: php-apache1
       namespace: default
     spec:
       maxReplicas: 10
       minReplicas: 2
       scaleTargetRef:
         apiVersion: apps/v1
         kind: Deployment
         name: php-apache1
       targetCPUUtilizationPercentage: 50
    
  2. 아래와 같이 curl을 요청할 수 있는 Deployment를 배포한다.

     apiVersion: apps/v1
     kind: Deployment
     metadata:
       name: netutil
       labels:
         app: netutil
     spec:
       replicas: 1
       selector:
         matchLabels:
           app: netutil
       template:
         metadata:
           labels:
             app: netutil
         spec:
           containers:
           - name: netutil
             image: praqma/network-multitool
    

HPA 로직

HPA는 desired수를 아래와 같이 계산한다.
desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]

만약 HPA에 CPU기반 scale 설정이 되었으나 파드 Manifest에 request.cpu가 정의되지 않은 경우에는 HPA가 동작하지 않는다.

부하 & 결과 확인

Test1

부하 발생

$ kubectl exec -it netutil-669f67cb94-2hdns -- /bin/bash
$ while true; do curl http://php-apache1; sleep 0.3; done

결과 확인

$ while true; do date; k get hpa php-apache1; sleep 10; done
#################### Result ####################
Thu Jan  5 09:57:28 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   0%/50%    2         10        2          39h
Thu Jan  5 09:57:38 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   26%/50%   2         10        2          39h
Thu Jan  5 09:57:48 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   26%/50%   2         10        2          39h
Thu Jan  5 09:57:58 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   52%/50%   2         10        2          39h
Thu Jan  5 09:58:08 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   51%/50%   2         10        2          39h
Thu Jan  5 09:58:18 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   51%/50%   2         10        2          39h
Thu Jan  5 09:58:29 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   52%/50%   2         10        2          39h
Thu Jan  5 09:58:39 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   52%/50%   2         10        2          39h
Thu Jan  5 09:58:49 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   52%/50%   2         10        2          39h
Thu Jan  5 09:58:59 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   50%/50%   2         10        2          39h
Thu Jan  5 09:59:09 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   51%/50%   2         10        2          39h
Thu Jan  5 09:59:19 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   51%/50%   2         10        2          39h
Thu Jan  5 09:59:29 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   52%/50%   2         10        2          39h
Thu Jan  5 09:59:40 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   56%/50%   2         10        2          39h
Thu Jan  5 09:59:50 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   56%/50%   2         10        2          39h
Thu Jan  5 10:00:00 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   49%/50%   2         10        3          39h
Thu Jan  5 10:00:10 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   33%/50%   2         10        3          39h
Thu Jan  5 10:00:20 KST 2023
$ while true; do date; k get po -l run=php-apache1; sleep 5; done
#################### Result ####################
Thu Jan  5 09:59:33 KST 2023
NAME                          READY   STATUS    RESTARTS   AGE
php-apache1-8445df799-bdlln   1/1     Running   0          39h
php-apache1-8445df799-jnmtx   1/1     Running   0          39h
Thu Jan  5 09:59:36 KST 2023
NAME                          READY   STATUS              RESTARTS   AGE
php-apache1-8445df799-67jjv   0/1     ContainerCreating   0          0s
php-apache1-8445df799-bdlln   1/1     Running             0          39h
php-apache1-8445df799-jnmtx   1/1     Running             0          39h
Thu Jan  5 09:59:39 KST 2023
NAME                          READY   STATUS    RESTARTS   AGE
php-apache1-8445df799-67jjv   1/1     Running   0          3s
php-apache1-8445df799-bdlln   1/1     Running   0          39h
php-apache1-8445df799-jnmtx   1/1     Running   0          39h

Test2

부하 발생

$ kubectl exec -it netutil-669f67cb94-2hdns -- /bin/bash
$ while true; do curl http://php-apache1; sleep 0.1; done

결과 확인

$ while true; do date; k get hpa php-apache1; sleep 10; done
#################### Result ####################
Thu Jan  5 10:28:49 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   0%/50%    2         10        2          40h
Thu Jan  5 10:28:59 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   0%/50%    2         10        2          40h
Thu Jan  5 10:29:09 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   16%/50%   2         10        2          40h
Thu Jan  5 10:29:19 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   16%/50%   2         10        2          40h
Thu Jan  5 10:29:30 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   91%/50%   2         10        2          40h
Thu Jan  5 10:29:40 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   93%/50%   2         10        4          40h
Thu Jan  5 10:29:50 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   93%/50%   2         10        4          40h
Thu Jan  5 10:30:00 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   45%/50%   2         10        4          40h
Thu Jan  5 10:30:10 KST 2023
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache1   Deployment/php-apache1   48%/50%   2         10        4          40h
$ while true; do date; k get po -l run=php-apache1; sleep 5; done
#################### Result ####################
NAME                          READY   STATUS    RESTARTS   AGE
php-apache1-8445df799-67jjv   1/1     Running   0          29m
php-apache1-8445df799-bdlln   1/1     Running   0          39h
Thu Jan  5 10:29:23 KST 2023
NAME                          READY   STATUS              RESTARTS   AGE
php-apache1-8445df799-67jjv   1/1     Running             0          29m
php-apache1-8445df799-bdlln   1/1     Running             0          39h
php-apache1-8445df799-ffff6   0/1     ContainerCreating   0          1s
php-apache1-8445df799-r5z8p   0/1     ContainerCreating   0          1s
Thu Jan  5 10:29:26 KST 2023
NAME                          READY   STATUS    RESTARTS   AGE
php-apache1-8445df799-67jjv   1/1     Running   0          29m
php-apache1-8445df799-bdlln   1/1     Running   0          39h
php-apache1-8445df799-ffff6   1/1     Running   0          4s
php-apache1-8445df799-r5z8p   1/1     Running   0          4s

📚 References

[1] kubernetes-sigs/metrics-server

[2] High Availability

[3] metric-server 가용성

[4] Resource metrics pipeline