使用k8s-prometheus-adapter实现HPA

使用k8s-prometheus-adapter实现HPA

环境:

kubernetes 1.9+node


自定义metric HPA原理:

首选须要建立一个apiservice(custom metrics AP)。nginx

adapter会根据配置的rules从Prometheus抓取并处理metrics,在处理(如重命名metrics等)完后将其发送到kubernetes的custom metrics API。后续HPA会经过该custom metrics API获取metrics的value进行扩缩容git


Adapter config

部署adapter前须要配置adapter的rule,默认配置为manifests/custom-metrics-config-map.yaml。adapter的配置主要分为4个:github

  • Discovery:指定如何获取Prometheus的metrics数据,经过seriesQuery挑选须要处理的metrics集合,能够经过seriesFilters精确过滤metrics。正则表达式

    seriesQuery能够根据标签进行查找(以下),也能够直接指定metric name查找shell

    seriesQuery: '{__name__=~"^container_.*_total",container_name!="POD",namespace!="",pod_name!=""}'
    seriesFilters:
      - isNot: "^container_.*_seconds_total"

    seriesFilters:json

    is: <regex>, 匹配包含该正则表达式的metrics.
    isNot: <regex>, 匹配不包含该正则表达式的metrics.
  • Association:设置metric与kubernetes resources的映射关系,kubernetes resorces能够经过kubectl api-resources命令查看。overrides会将Prometheus label与一个kubernetes resource(下例为deployment)关联。须要注意的是该label必须是一个真实的kubernetes resource,如metric的pod_name能够映射为kubernetes的pod resource,但不能将container_image映射为kubernetes的pod resource,映射错误可能会致使没法经过custom metrics API获取正确的值。这也表示metric中必须存在一个真实的resource 名称,将其转化为kubernetes resource。bootstrap

    resources:
      overrides:
        microservice: {group: "apps", resource: "deployment"}
  • Naming:用于将prometheus metrics名称转化为custom metrics API所使用的metrics名称。若是不须要能够不执行这一步。api

    # match turn any name <name>_total to <name>_per_second
    # e.g. http_requests_total becomes http_requests_per_second
    name:
      matches: "^(.*)_total$"
      as: "${1}_per_second"

    如本例中HPA后续能够经过/apis/{APIService-name}/v1beta1/namespaces/{namespaces-name}/pods/*/http_requests_per_second获取metricsbash

  • Querying:处理调用custom metrics API获取到的metrics的value

    # convert cumulative cAdvisor metrics into rates calculated over 2 minutes
    metricsQuery: "sum(rate(<<.Series>>{<<.LabelMatchers>>,container_name!="POD"}[2m])) by (<<.GroupBy>>)"

    metricsQuery 字段使用Go template将URL请求转变为Prometheus的请求,它会提取custom metrics APIq请求中的字段,并将其划分为metric name,group-resource,以及group-resource中的一个或多个objects,对应以下字段:

    • Series: metric名称
    • LabelMatchers: 以逗号分割的objects,当前表示特定group-resource加上命名空间的label(若是该group-resource 是namespaced的)
    • GroupBy:以逗号分割的label的集合,当前表示LabelMatchers中的group-resource label

    假设metrics http_requests_per_second以下

    http_requests_per_second{pod="pod1",service="nginx1",namespace="somens"}
    http_requests_per_second{pod="pod2",service="nginx2",namespace="somens"}

    当调用kubectl get --raw "/apis/{APIService-name}/v1beta1/namespaces/somens/pods/*/http_request_per_second"时,metricsQuery字段的模板的实际内容以下:

    • Series: "http_requests_total"
    • LabelMatchers: "pod=~\"pod1|pod2",namespace="somens"
    • GroupBy:pod

部署:

  • github下载k8s-prometheus-adapter

  • 参照官方文档部署

    • pull镜像:directxman12/k8s-prometheus-adapter:latest,修改镜像tag并push到本地镜像仓库

    • 运行gencerts.sh生成cm-adapter-serving-certs.yaml,并将其拷贝到manifests/目录下

      #!/usr/bin/env bash
      # exit immediately when a command fails
      set -e
      # only exit with zero if all commands of the pipeline exit successfully
      set -o pipefail
      # error on unset variables
      set -u
      
      # Detect if we are on mac or should use GNU base64 options
      case $(uname) in
              Darwin)
                  b64_opts='-b=0'
                  ;; 
              *)
                  b64_opts='--wrap=0'
      esac
      
      go get -v -u github.com/cloudflare/cfssl/cmd/...
      
      export PURPOSE=metrics
      openssl req -x509 -sha256 -new -nodes -days 365 -newkey rsa:2048 -keyout ${PURPOSE}-ca.key -out ${PURPOSE}-ca.crt -subj "/CN=ca"
      echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","'${PURPOSE}'"]}}}' > "${PURPOSE}-ca-config.json"
      
      export SERVICE_NAME=custom-metrics-apiserver
      export ALT_NAMES='"custom-metrics-apiserver.monitoring","custom-metrics-apiserver.monitoring.svc"'
      echo "{\"CN\":\"${SERVICE_NAME}\", \"hosts\": [${ALT_NAMES}], \"key\": {\"algo\": \"rsa\",\"size\": 2048}}" | \
              cfssl gencert -ca=metrics-ca.crt -ca-key=metrics-ca.key -config=metrics-ca-config.json - | cfssljson -bare apiserver
      
      cat <<-EOF > cm-adapter-serving-certs.yaml
      apiVersion: v1
      kind: Secret
      metadata:
        name: cm-adapter-serving-certs
      data:
        serving.crt: $(base64 ${b64_opts} < apiserver.pem)
        serving.key: $(base64 ${b64_opts} < apiserver-key.pem)
      EOF
    • 建立命名空间:kubectl create namespace custom-metrics

    • openshift的kube-system下面可能没有role extension-apiserver-authentication-reader,若是不存在,则须要建立

      apiVersion: rbac.authorization.k8s.io/v1
      kind: Role
      metadata:
        annotations:
          rbac.authorization.kubernetes.io/autoupdate: "true"
        labels:
          kubernetes.io/bootstrapping: rbac-defaults
        name: extension-apiserver-authentication-reader
        namespace: kube-system
      rules:
      - apiGroups:
        - ""
        resourceNames:
        - extension-apiserver-authentication
        resources:
        - configmaps
        verbs:
        - get
    • 修改custom-metrics-apiserver-deployment.yaml的--prometheus-url字段,指向正确的prometheus

    • 建立其余组件:kubectl create -f manifests/,

校验


HPA的配置

HPA支持的metrics类型有4种(下述为v2beta2的格式):

  • resource:目前仅支持cpumemory。target能够指定数值(targetAverageValue)和比例(targetAverageUtilization)进行扩缩容

  • pods:custom metrics,这类metrics描述了pod,target仅支持按指定数值(targetAverageValue)进行扩缩容。targetAverageValue 用于计算全部相关pods上的metrics的平均值

    type: Pods
    pods:
      metric:
        name: packets-per-second
      target:
        type: AverageValue
        averageValue: 1k
  • object:custom metrics,这类metrics描述了相同命名空间下的(非pod)对象。target支持经过valueAverageValue进行扩缩容,前者直接将metric与target比较进行扩缩容,后者经过metric/相关的pod数目与target比较进行扩缩容

    type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: extensions/v1beta1
        kind: Ingress
        name: main-route
      target:
        type: Value
        value: 2k
  • external:kubernetes 1.10+。这类metrics与kubernetes集群无关。与object相似,target支持经过valueAverageValue进行扩缩容。因为external能够尝试匹配全部metrics,所以实际中不建议使用该类型。

    - type: External
      external:
        metric:
          name: queue_messages_ready
          selector: "queue=worker_tasks"
        target:
          type: AverageValue
          averageValue: 30
  • 1.6版本支持多metrics的扩缩容,当其中一个metrics达到扩容标准时就会建立pod副本(当前副本<maxReplicas)

注:target的value的一个单位能够划分为1000份,每一份以m为单位,如500m表示1/2个单位。参见Quantity


kubernetes metrics的获取

假设注册的APIService为custom.metrics.k8s.io/v1beta1,在注册好APIService后HorizontalPodAutoscaler controller会从以/apis/custom.metrics.k8s.io/v1beta1为根API的路径上抓取metrics。metrics的API path能够分为namespacednon-namespaced类型的。经过以下方式校验HPA是否能够获取到metrics:

namespaced

  • 获取指定namespace下指定object类型和名称的metrics
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/{namespace-name}/{object-type}/{object-name}/{metric-name...}"

如获取monitor命名空间下名为grafana的pod的start_time_seconds metric

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/monitor/pods/grafana/start_time_seconds"
  • 获取指定namespace下全部特定object类型的metrics
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/{namespace-name}/pods/*/{metric-name...}"

如获取monitor命名空间下名为全部pod的start_time_seconds metric

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/monitor/pods/*/start_time_seconds"
  • 使用labelSelector能够选择带有特定label的object
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/{namespace-name}/{object-type}/{object-name}/{metric-name...}?labelSelector={label-name}"
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/{namespace-name}/pods/*/{metric-name...}?labelSelector={label-name}"

non-namespaced

non-namespaced和namespaced的相似,主要有node,namespace,PersistentVolume等。non-namespaced访问有些与custom metrics API描述不一致。

  • 访问object为namespace的方式以下以下
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/{namespace-name}/metrics/{metric-name...}"
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/*/metrics/{metric-name...}"
  • 访问node的方式以下
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/nodes/{node-name}/{metric-name...}"

DEBUG:

  • 使用以下方式查看注册的APIService发现的全部rules

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

    若是获取失败,能够看下使用oc get apiservice v1beta1.custom.metrics.k8s.io -oyaml查看statusmessage的相关信息

  • 经过以下方式查看完整的请求过程(--v=8)

    kubectl get --raw “/apis/custom.metrics.k8s.io/v1beta1/namespaces/{namespace-name}/pods/*/{metric-name...}" --v=8
  • 若是上述过程正确,但获取到的items为空
    • 首先保证k8s-prometheus-adapter的参数--metrics-relist-interval设置值大于Prometheus的参数scrape_interval
    • 确保k8s-prometheus-adapter rulesseriesQuery规则能够抓取到Prometheus的数据
    • 确保k8s-prometheus-adapter rulesmetricsQuery规则能够抓取到计算出数据,此处须要注意的是,若是使用到了计算某段时间的数据,若是时间设置太短,可能致使没有数据生成

TIPS:

遗留问题:

HPA是如何感知注册的apiservice的?

apiservice的工做模型

相关文章
相关标签/搜索