1、架构

Kubernetes HPA之custom-metrics-server

2、装置custom metrics adapter


$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
$ helm repo update
$ helm install my-release prometheus-community/prometheus-adapter

3、目标发现和装备展现(Metrics Discovery and Presentation Configuration)

prometheus-adapter经过一组“Discovery”规矩确认揭露哪些目标以及怎么揭露它们。每个规矩都是独立履行的,并指定prometheus-adapter 在API 中揭露目标所需采取的每个过程。以下面规矩为例:

rules:
# this rule matches cumulative cAdvisor metrics measured in seconds
- seriesQuery: '{__name__=~"^container_.*",container!="POD",namespace!="",pod!=""}'
  resources:
    # skip specifying generic resource<->label mappings, and just
    # attach only pod and namespace resources by mapping label names to group-resources
    overrides:
      namespace: {resource: "namespace"}
      pod: {resource: "pod"}
  # specify that the `container_` and `_seconds_total` suffixes should be removed.
  # this also introduces an implicit filter on metric family names
  name:
    # we use the value of the capture group implicitly as the API name
    # we could also explicitly write `as: "$1"`
    matches: "^container_(.*)_seconds_total$"
  # specify how to construct a query to fetch samples for a given series
  # This is a Go template where the `.Series` and `.LabelMatchers` string values
  # are available, and the delimiters are `<<` and `>>` to avoid conflicts with
  # the prometheus query language
  metricsQuery: "sum(rate(<<.Series>>{<<.LabelMatchers>>,container!="POD"}[2m])) by (<<.GroupBy>>)"

每个规矩能够大致分为四个部分:

  • Discovery,它指定prometheus-adapter应怎么找到此规矩的一切 Prometheus 目标,对应装备文件中seriesQuery
  • Association,它指定prometheus-adapter应怎么确认特定目标与哪些 Kubernetes 资源相相关,对应装备文件中resources
  • Naming,它指定prometheus-adapter应怎么在custom-metrics API 中揭露目标,对应装备文件中name
  • Querying,它指定对一个或多个 Kubernetes 目标的特定目标的恳求应怎么转换为对 Prometheus 的查询,对应装备文件中metricsQuery

每个部分的细节可参考:github.com/kubernetes-…

4、咱们以获取Per-pod HTTP Requests为例

4.1 背景

假定咱们有一些web服务,咱们正在测验为 Prometheus adapter编写一个装备,以便咱们能够依据它每秒接纳到的 HTTP 恳求自动缩放它。

在开始之前,咱们现已开始运用目标http_requests_total对咱们的web服务器进行检测,它运用单个标签揭露,method按 HTTP 动词分化恳求。

咱们装备了 Prometheus 来搜集目标,并增加了kubernetes_namespacekubernetes_pod_name标签,别离代表命namespace 和 pod。

假如咱们查询Prometheus,咱们看到的系列看起来像

http_requests_total{method="GET",kubernetes_namespace="production",kubernetes_pod_name="frontend-server-abcd-0123"}
4.2 装备适配器

适配器经过以下方法考虑目标:

  1. 首要,它发现可用的目标 (Discovery)
  2. 然后,它找出每个目标与哪些 Kubernetes 资源相相关(Association
  3. 然后,它会弄清楚应该怎么将它们露出给自定义目标 API(name
  4. 最后,它弄清楚应该怎么查询 Prometheus 以获取实践数字(Querying

咱们需求告知适配器它应该怎么为咱们的目标履行这些过程中的每一个,http_requests_total因而咱们需求增加一个新规矩。适配器中的每个规矩都对这些过程进行编码。让咱们在装备中增加一个新的:

rules:
- {}

http_requests_total假如咱们想自己在 Prometheus仪表板中查找一切系列,咱们将编写代码以查找与命名空间和 pod 相关的http_requests_total{kubernetes_namespace!="",kubernetes_pod_name!=""}一切系列。http_requests_total

咱们能够将其增加到咱们的规矩中seriesQuery,告知适配器怎么发现正确的系列:

rules:
- seriesQuery: 'http_requests_total{kubernetes_namespace!="",kubernetes_pod_name!=""}'

接下来,咱们需求告知适配器怎么确认哪些 Kubernetes 资源与目标相关, kubernetes_namespace代表namespace称号,kubernetes_pod_name代表pod称号。由于这些称号不太遵从共同的方式,因而咱们在规矩中运用字段overrides部分 :resources

rules:
- seriesQuery: 'http_requests_total{kubernetes_namespace!="",kubernetes_pod_name!=""}'
  resources:
    overrides:
      kubernetes_namespace: {resource: "namespace"}
      kubernetes_pod_name: {resource: "pod"}

这表明每个标签代表其对应的资源。由于资源坐落“core”kubernetes API 中,因而咱们不需求指定组。适配器会自动处理复数,所以咱们能够指定podorpods,和运用kubectl get是一样的。资源能够是 kubernetes 集群中可用的任何资源,只需您有相应的标签即可。

假如咱们的标签遵从共同的方式,比如kubernetes_<resource>,咱们能够指定resources: {template: "kubernetes_<<.Resource>>"}而不是为每个资源指定overrides。假如您想检查集群中当时可用的一切资源,能够运用该kubectl api-resources指令(但可用资源列表或许会随着您增加或删除 CRD 或聚合的 API 服务器而改变), 现在,累积目标(如以 结束的目标_total)对于自动缩放不是特别有用,因而咱们期望将它们转换为 API 中的速率目标。咱们将调用咱们的目标的rate versionhttp_requests_per_second。咱们能够运用name该字段来告知适配器:

rules:
- seriesQuery: 'http_requests_total{kubernetes_namespace!="",kubernetes_pod_name!=""}'
  resources:
    overrides:
      kubernetes_namespace: {resource: "namespace"}
      kubernetes_pod_name: {resource: "pod"}
  name:
    matches: "^(.*)_total"
    as: "${1}_per_second"

在这里,咱们现已说过,咱们应该将称号匹配<something>_total,并将其转换为<something>_per_second

最后,咱们需求告知适配器怎么实践查询 Prometheus 以获得一些数字。由于咱们想要一个速率,咱们或许会写:sum(rate(http_requests_total{kubernetes_namespace="production",kubernetes_pod_name=~"frontend-server-abcd-0123|fronted-server-abcd-4567"}) by (kubernetes_pod_name),这将使咱们得到每个pod 每秒的总恳求数,经过动词求和。

咱们能够在适配器中编写相似的东西,运用metricsQuery字段:

rules:
- seriesQuery: 'http_requests_total{kubernetes_namespace!="",kubernetes_pod_name!=""}'
  resources:
    overrides:
      kubernetes_namespace: {resource: "namespace"}
      kubernetes_pod_name: {resource: "pod"}
  name:
    matches: "^(.*)_total"
    as: "${1}_per_second"
  metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)'

适配器将依据咱们放入 API 中的内容自动填充正确的系列称号、标签匹配器和分组依据子句。由于无论怎么咱们只运用一个目标,咱们能够用http_requests_total替换<<.Series>>.

4.3 查询api

现在,假如咱们运用此装备运行 Prometheus 适配器的实例,咱们能够运用下面的指令检查目标

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

请注意,咱们获得了“pods”和“namespaces”的条目——适配器露出了咱们与metric相关的每个资源的metric(而且一切命名空间资源有必要与命名空间相关),并将填充<<.GroupBy>>依据咱们的要求,携带了合适的label。

检查每一个pod露出的metric

$ kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/production/pods/*/http_requests_per_second
{
  "kind": "MetricValueList",
  "apiVersion": "custom.metrics.k8s.io/v1beta1",
  "metadata": {
    "selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/production/pods/*/http_requests_per_second",
  },
  "items": [
    {
      "describedObject": {
        "kind": "Pod",
        "name": "frontend-server-abcd-0123",
        "apiVersion": "/__internal",
      },
      "metricName": "http_requests_per_second",
      "timestamp": "2018-08-07T17:45:22Z",
      "value": "16m"
    },
    {
      "describedObject": {
        "kind": "Pod",
        "name": "frontend-server-abcd-4567",
        "apiVersion": "/__internal",
      },
      "metricName": "http_requests_per_second",
      "timestamp": "2018-08-07T17:45:22Z",
      "value": "22m"
    }
  ]
}

这表明咱们的pod 每秒接纳 16 和 22 毫恳求(取决于 pod),即每秒 0.016 和 0.022 恳求,写为小数。这就是咱们对除了Prometheus scrape之外几乎没有流量的期望。

假如咱们向咱们的 pod 增加一些流量,咱们或许会看到1or20而不是16m,这将是1or20requests per second。咱们或许还会看到20500m,这意味着每秒 20500 毫恳求,或者以十进制方式表明每秒 20.5 个恳求。