수평형 포드 자동 확장 처리(HPA)

이 문서에서는 Google Cloud Managed Service for Prometheus에 수평형 포드 자동 확장(HPA)을 사용 설정하는 방법을 설명합니다. 다음 중 하나를 수행하여 HPA를 사용 설정할 수 있습니다.

두 가지 방법 중 하나를 선택해야 합니다. 문제 해결에 설명된 대로 리소스 정의가 중복되므로 둘 다 사용할 수 없습니다.

커스텀 측정항목 Stackdriver 어댑터 사용

커스텀 측정항목 Stackdriver 어댑터는 어댑터 버전 v0.13.1로 시작하는 Managed Service for Prometheus에서 측정항목을 쿼리하도록 지원합니다.

커스텀 측정항목 Stackdriver 어댑터를 사용하여 HPA 구성 예시를 설정하려면 다음을 수행합니다.

  1. 클러스터의 관리 컬렉션을 설정합니다.
  2. 클러스터에 커스텀 측정항목 Stackdriver 어댑터를 설치합니다.

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml
    
  3. 예시 Prometheus 측정항목 내보내기 및 HPA 리소스를 배포합니다.

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/examples/prometheus-to-sd/custom-metrics-prometheus-sd.yaml
    

    이 명령어는 측정항목 foo 및 HPA 리소스를 내보내는 내보내기 도구 애플리케이션을 배포합니다. HPA는 이 애플리케이션을 최대 5개의 복제본으로 확장하여 측정항목 foo의 목표 값을 달성합니다.

  4. podmonitoring.yaml이라는 파일에 다음 구성을 배치하여 PodMonitoring 리소스를 정의합니다.

    apiVersion: monitoring.googleapis.com/v1
    kind: PodMonitoring
    metadata:
      name: prom-example
    spec:
      selector:
        matchLabels:
          run: custom-metric-prometheus-sd
      endpoints:
      - port: 8080
        interval: 30s
    
  5. 새 PodMonitoring 리소스를 배포합니다.

    kubectl -n default apply -f podmonitoring.yaml
    

    Prometheus용 관리형 서비스는 몇 분 안에 내보내기에서 스크레이핑한 측정항목을 처리하고 긴 이름을 사용하여 Cloud Monitoring에 저장합니다. Prometheus 측정항목은 다음 규칙으로 저장됩니다.

    • prometheus.googleapis.com 프리픽스
    • 이 서픽스는 일반적으로 gauge, counter, summary 또는 histogram 중 하나이지만 유형화되지 않은 측정항목에는 unknown 또는 unknown:counter 서픽스가 있을 수 있습니다. 서픽스를 확인하려면 측정항목 탐색기를 사용하여 Cloud Monitoring에서 측정항목을 찾습니다.
  6. 배포된 HPA를 업데이트하여 Cloud Monitoring에서 측정항목을 쿼리합니다. foo 측정항목은 prometheus.googleapis.com/foo/gauge으로 수집됩니다. 배포된 HorizontalPodAutoscaler 리소스로 측정항목을 쿼리할 수 있게 하려면 배포된 HPA에서 긴 형식의 이름을 사용하지만 모든 슬래시(/)를 파이프 문자(|)로 대체하여 수정해야 합니다: prometheus.googleapis.com|foo|gauge. 자세한 내용은 커스텀 측정항목 Stackdriver 어댑터 저장소의 Stackdriver에서 제공되는 측정항목 섹션을 참조하세요.

    1. 다음 명령어를 실행하여 배포된 HPA를 업데이트합니다.

      kubectl edit hpa custom-metric-prometheus-sd
      
    2. pods.metric.name 필드 값을 foo에서 prometheus.googleapis.com|foo|gauge로 변경합니다. spec 섹션은 다음과 같이 표시되어야 합니다.

      spec:
         maxReplicas: 5
         metrics:
         - pods:
             metric:
               name: prometheus.googleapis.com|foo|gauge
             target:
               averageValue: "20"
               type: AverageValue
           type: Pods
         minReplicas: 1
      

    이 예시에서 HPA 구성은 prometheus.googleapis.com/foo/gauge 측정항목의 평균 값을 20으로 찾습니다. 이 배포에서 측정항목의 값을 40으로 설정하므로 HPA 컨트롤러는 maxReplicas(5) 필드 값까지 포드 수를 늘려 모든 포드에서 메트릭의 평균 값을 20으로 줄이려고 시도합니다.

    HPA 쿼리는 HPA 리소스가 설치된 네임스페이스와 클러스터로 범위가 지정되므로 다른 클러스터 및 네임스페이스의 동일한 측정항목은 자동 확장에 영향을 미치지 않습니다.

  7. 워크로드 수직 확장을 확인하려면 다음 명령어를 실행합니다.

    kubectl get hpa custom-metric-prometheus-sd --watch
    

    REPLICAS 필드 값이 1에서 5로 변경됩니다.

    NAME                          REFERENCE                                TARGETS        MINPODS   MAXPODS   REPLICAS   AGE
    custom-metric-prometheus-sd   Deployment/custom-metric-prometheus-sd   40/20          1         5         5          *
    
  8. 배포를 축소하려면 대상 측정항목 값을 내보낸 측정항목 값보다 높게 업데이트합니다. 이 예시에서 배포는 prometheus.googleapis.com/foo/gauge 측정항목의 값을 40으로 설정합니다. 대상 값을 40보다 큰 숫자로 설정하면 배포가 축소됩니다.

    예를 들어 HPA 구성의 pods.target.averageValue 필드 값을 20에서 100으로 변경하려면 kubectl edit를 사용합니다.

    kubectl edit hpa custom-metric-prometheus-sd
    

    다음과 일치하도록 사양 섹션을 수정합니다.

    spec:
      maxReplicas: 5
      metrics:
      - pods:
          metric:
            name: prometheus.googleapis.com|foo|gauge
          target:
            averageValue: "100"
            type: AverageValue
      type: Pods
      minReplicas: 1
    
  9. 워크로드 축소를 보려면 다음 명령어를 실행합니다.

    kubectl get hpa custom-metric-prometheus-sd --watch
    

    REPLICAS 필드 값이 5에서 1로 변경됩니다. 이는 설계상 포드 수를 늘릴 때보다 더 느리게 발생합니다.

    NAME                          REFERENCE                                TARGETS        MINPODS   MAXPODS   REPLICAS   AGE
    custom-metric-prometheus-sd   Deployment/custom-metric-prometheus-sd   40/100          1         5         1          *
    
  10. 배포된 예시를 삭제하려면 다음 명령어를 실행하세요.

    kubectl delete -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml
    kubectl delete -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/examples/prometheus-to-sd/custom-metrics-prometheus-sd.yaml
    kubectl delete podmonitoring/prom-example
    

자세한 내용은 커스텀 측정항목 Stackdriver 어댑터 저장소의 Prometheus 예시를 참조하거나 애플리케이션 확장을 참조하세요.

Prometheus 어댑터 사용

기존 prometheus-adapter 구성은 몇 가지 변경만으로 자동 확장될 수 있습니다. Managed Service for Prometheus를 사용하여 확장하도록 prometheus-adapter를 구성하는 것은 업스트림 Prometheus를 사용한 확장과 비교하여 두 가지 추가 제한사항이 있습니다.

  • Prometheus API 또는 UI를 사용하여 Managed Service for Prometheus를 쿼리할 때와 마찬가지로 쿼리는 Prometheus 프런트엔드 UI 프록시를 통해 라우팅되어야 합니다. prometheus-adapter의 경우 prometheus-adapter 배포를 수정하여 prometheus-url 값을 다음과 같이 변경합니다.

    --prometheus-url=http://frontend.NAMESPACE_NAME.svc:9090/
    

    여기서 NAMESPACE_NAME은 프런트엔드가 배포된 네임스페이스입니다.

  • 규칙 구성의 .seriesQuery 필드에서 측정항목 이름에 정규식 일치자를 사용할 수 없습니다. 대신 측정항목 이름을 완전히 지정해야 합니다.

업스트림 Prometheus에 비해 Managed Service for Prometheus 내에서 데이터를 사용할 수 있는 데 시간이 조금 더 걸릴 수 있으므로 과도하게 빠른 자동 확장 로직을 구성하면 원치 않는 동작이 발생할 수 있습니다. 데이터 최신 상태에 대한 보장은 없지만 데이터가 Managed Service for Prometheus로 전송된 후 일반적으로 3~7초가 지나면 데이터를 쿼리할 수 있습니다(네트워크 지연 시간 제외).

prometheus-adapter에서 실행하는 모든 쿼리는 전역 범위에 있습니다. 즉, 이름이 같은 측정항목을 내보내는 네임스페이스 두 개에 애플리케이션이 있는 경우 해당 측정항목을 사용하는 HPA 구성은 두 애플리케이션 모두의 데이터로 확장됩니다. 잘못된 데이터로 확장되지 않도록 항상 PromQL에서 namespace 또는 cluster 필터를 사용하는 것이 좋습니다.

prometheus-adapter 및 관리 컬렉션을 사용하여 HPA 구성 예시를 설정하려면 다음 단계를 따르세요.

  1. 클러스터의 관리 컬렉션을 설정합니다.
  2. 클러스터에 Prometheus 프런트엔드 UI 프록시를 배포합니다. 워크로드 아이덴티티를 사용하는 경우 서비스 계정도 구성하고 승인해야 합니다.
  3. prometheus-engine 저장소 내 examples/hpa/ 디렉터리에 매니페스트를 배포합니다.
    • example-app.yaml: 측정항목을 방출하는 배포 및 서비스의 예시입니다.
    • pod-monitoring.yaml: 예시 측정항목 스크래핑을 구성하는 리소스입니다.
    • hpa.yaml: 워크로드의 확장을 구성하는 HPA 리소스입니다.
  4. 클러스터에 prometheus-adapter가 설치되어 있는지 확인합니다. 이렇게 하려면 클러스터에 설치 매니페스트 예시를 배포합니다. 이 매니페스트는 다음과 같이 구성됩니다.

    • default 네임스페이스에 배포된 프런트엔드 프록시를 쿼리합니다.
    • PromQL을 실행하여 배포 예시에서 http_requests_per_second 측정항목을 계산하고 반환합니다.
  5. 다음 명령어를 각각 별도의 터미널 세션에서 실행합니다.

    1. prometheus-example-app 서비스에 대해 HTTP 로드를 생성합니다.
      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://prometheus-example-app; done"
    2. 수평형 포드 자동 확장 처리를 관찰합니다.
      kubectl get hpa prometheus-example-app --watch
    3. 워크로드 확장을 관찰합니다.
      kubectl get po -lapp.kubernetes.io/name=prometheus-example-app --watch
  6. Ctrl+C를 사용하여 HTTP 부하 생성을 중지하고 워크로드 축소를 관찰합니다.

문제 해결

커스텀 측정항목 Stackdriver 어댑터는 Prometheus 어댑터(prometheus-adapter)의 이름과 동일한 이름의 리소스 정의를 사용합니다. 이름이 겹치는 경우 동일한 클러스터에서 어댑터를 두 개 이상 실행하면 오류가 발생합니다.

이전에 Stackdriver 어댑터가 설치된 커스텀 측정항목이 있는 클러스터에 Prometheus 어댑터를 설치하면 이름 충돌로 인해 FailedGetObjectMetric과 같은 오류가 발생할 수 있습니다. 이를 해결하기 위해 이전에 커스텀 측정항목 어댑터에서 등록한 v1beta1.external.metrics.k8s.io, v1beta1.custom.metrics.k8s.io, v1beta2.custom.metrics.k8s.io apiservice를 삭제해야 할 수 있습니다.

문제 해결 팁:

  • Pub/Sub 측정항목과 같은 일부 Cloud Monitoring 시스템 측정항목은 60초 이상 지연됩니다. Prometheus 어댑터는 현재 타임스탬프를 사용하여 쿼리를 실행하므로 Prometheus 어댑터를 사용하여 이러한 측정항목을 쿼리하면 데이터 없음으로 잘못된 결과가 나올 수 있습니다. 지연된 측정항목을 쿼리하려면 PromQL의 offset 한정자를 사용하여 쿼리의 타임스탬프를 필요한 양만큼 변경합니다.

  • 프런트엔드 UI 프록시가 의도한 대로 작동하고 권한 관련 문제가 없는지 확인하려면 터미널에서 다음 명령어를 실행합니다.

    kubectl -n NAMESPACE_NAME port-forward svc/frontend 9090
    

    그런 후 다른 터미널을 열고 다음 명령어를 실행합니다.

    curl --silent 'localhost:9090/api/v1/series?match%5B%5D=up'
    

    프런트엔드 UI 프록시가 올바르게 작동하지 않으면 두 번째 터미널의 응답이 다음과 비슷하게 표시됩니다.

    curl --silent 'localhost:9090/api/v1/series?match%5B%5D=up' | jq .
    {
      "status": "success",
      "data": [
         ...
      ]
    }
    

    403 오류가 수신되면 프런트엔드 UI 프록시가 올바르게 구성되지 않은 것입니다. 403 오류를 해결하는 방법은 서비스 계정 구성 및 승인 가이드를 참조하세요.

  • 커스텀 측정항목 apiserver를 사용할 수 있는지 확인하려면 다음 명령어를 실행합니다.

    kubectl get apiservices.apiregistration.k8s.io v1beta1.custom.metrics.k8s.io
    

    apiserver를 사용할 수 있으면 응답이 다음과 비슷합니다.

    $ kubectl get apiservices.apiregistration.k8s.io v1beta1.custom.metrics.k8s.io
    NAME                            SERVICE                         AVAILABLE   AGE
    v1beta1.custom.metrics.k8s.io   monitoring/prometheus-adapter   True        33m
    
  • HPA가 의도한 대로 작동하는지 확인하려면 다음 명령어를 실행합니다.

    $ kubectl describe hpa prometheus-example-app
    Name:                                  prometheus-example-app
    Namespace:                             default
    Labels:                                
    Annotations:                           
    Reference:                             Deployment/prometheus-example-app
    Metrics:                               ( current / target )
    "http_requests_per_second" on pods:  11500m / 10
    Min replicas:                          1
    Max replicas:                          10
    Deployment pods:                       2 current / 2 desired
    Conditions:
    Type            Status  Reason              Message
    ----            ------  ------              -------
    AbleToScale     True    ReadyForNewScale    recommended size matches current size
    ScalingActive   True    ValidMetricFound    the HPA was able to successfully calculate a replica count from pods metric http_requests_per_second
    ScalingLimited  False   DesiredWithinRange  the desired count is within the acceptable range
    Events:
    Type     Reason               Age                   From                       Message
    ----     ------               ----                  ----                       -------
    Normal   SuccessfulRescale    47s                   horizontal-pod-autoscaler  New size: 2; reason: pods metric http_requests_per_second above target
    

    응답에 FailedGetPodsMetric과 같은 문이 포함되어 있으면 HPA가 실패합니다. 다음은 HPA가 실패할 때 describe 호출에 대한 응답을 보여줍니다.

    $ kubectl describe hpa prometheus-example-app
    Name:                                  prometheus-example-app
    Namespace:                             default
    Reference:                             Deployment/prometheus-example-app
    Metrics:                               ( current / target )
      "http_requests_per_second" on pods:   / 10
    Min replicas:                          1
    Max replicas:                          10
    Deployment pods:                       1 current / 1 desired
    Conditions:
      Type            Status  Reason               Message
      ----            ------  ------               -------
      AbleToScale     True    ReadyForNewScale     recommended size matches current size
      ScalingActive   False   FailedGetPodsMetric  the HPA was unable to compute the replica count: unable to get metric http_requests_per_second: unable to fetch metrics from custom metrics API: the server could not find the metric http_requests_per_second for pods
      ScalingLimited  False   DesiredWithinRange   the desired count is within the acceptable range
    Events:
      Type     Reason               Age                   From                       Message
      ----     ------               ----                  ----                       -------
      Warning  FailedGetPodsMetric  104s (x11 over 16m)   horizontal-pod-autoscaler  unable to get metric http_requests_per_second: unable to fetch metrics from custom metrics API: the server could not find the metric http_requests_per_second for pods
    

    HPA가 실패할 때는 load-generator로 측정항목을 생성해야 합니다. 다음 명령어를 사용하여 커스텀 측정항목 API를 직접 확인할 수 있습니다.

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

    성공한 출력은 다음과 같이 표시됩니다.

    $ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/" | jq .
      {
      "kind": "APIResourceList",
      "apiVersion": "v1",
      "groupVersion": "custom.metrics.k8s.io/v1beta1",
      "resources": [
         {
            "name": "namespaces/http_requests_per_second",
            "singularName": "",
            "namespaced": false,
            "kind": "MetricValueList",
            "verbs": [
            "get"
            ]
         },
         {
            "name": "pods/http_requests_per_second",
            "singularName": "",
            "namespaced": true,
            "kind": "MetricValueList",
            "verbs": [
            "get"
            ]
         }
      ]
      }
    

    측정항목이 없으면 출력에서 "resources"에 데이터가 표시되지 않습니다. 예를 들면 다음과 같습니다.

    kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/" | jq .
    {
    "kind": "APIResourceList",
    "apiVersion": "v1",
    "groupVersion": "custom.metrics.k8s.io/v1beta1",
    "resources": []
    }