排查 Kubernetes HPA 透過 Prometheus 獲取不到 http_requests 指標的問題

dudu發表於2020-01-18

部署好了 kube-prometheus 與 k8s-prometheus-adapter (詳見之前的博文 k8s 安裝 prometheus 過程記錄),使用下面的配置檔案部署 HPA(Horizontal Pod Autoscaling) 卻失敗。

apiVersion: autoscaling/v2beta2 
kind: HorizontalPodAutoscaler
metadata: 
  name: blog-web
spec: 
  scaleTargetRef: 
    apiVersion: apps/v1 
    kind: Deployment 
    name: blog-web
  minReplicas: 2
  maxReplicas: 12 
  metrics: 
    - type: Pods
      pods:
        metric:
          name: http_requests
        target:
          type: AverageValue
          averageValue: 100

錯誤資訊如下:

unable to get metric http_requests: unable to fetch metrics from custom metrics API: the server could not find the metric http_requests for pods

透過下面的命令檢視 custom.metrics.k8s.io api 支援的 http_requests(每秒請求數QPS)監控指標:

$kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/ | jq . | egrep pods/.*http_requests 
      "name": "pods/alertmanager_http_requests_in_flight",
      "name": "pods/prometheus_http_requests"

發現只有 prometheus_http_requests 指標 ,沒有所需的 http_requests 開頭的指標。

開啟 prometheus 控制檯,發現 /service-discovery 中沒有出現我們想監控的應用 blog-web ,網上查詢資料後知道了需要部署 ServiceMonitor 讓 prometheus 發現所監控的 service 。

新增下面的 ServiceMonitor 配置檔案:

kind: ServiceMonitor
apiVersion: monitoring.coreos.com/v1
metadata:
  name: blog-web-monitor
  labels:
    app: blog-web-monitor
spec:
  selector:
    matchLabels:
      app: blog-web
  endpoints: 
  - port: http

部署後還是沒有被 prometheus 發現,檢視 prometheus 的日誌發現下面的錯誤:

Failed to list *v1.Pod: pods is forbidden: User \"system:serviceaccount:monitoring:prometheus-k8s\" cannot list resource \"pods\" in API group \"\" at the cluster scope

在園子裡的博文 PrometheusOperator服務自動發現-監控redis樣例 中找到了解決方法,將 prometheus-clusterRole.yaml 改為下面的配置:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus-k8s
rules:
- apiGroups:
  - ""
  resources:
  - nodes
  - services
  - endpoints
  - pods
  - nodes/proxy
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - configmaps
  - nodes/metrics
  verbs:
  - get
- nonResourceURLs:
  - /metrics
  verbs:
  - get

重新部署即可

kubectl apply -f prometheus-clusterRole.yaml

注1:如果採用上面的方法還是沒被發現,需要強制重新整理 prometheus 的配置,參考 部署 ServiceMonitor 之後如何讓 Prometheus 立即發現
注2:也可以將 prometheus 配置為自動發現 service 與 pod ,參考園子裡的博文 prometheus配置pod和svc的自動發現和監控PrometheusOperator服務自動發現-監控redis樣例

但是這時還有問題,雖然 service 被 prometheus 發現了,但 service 所對應的 pod 一個都沒被發現。

production/blog-web-monitor/0 (0/19 active targets)

排查後發現是因為 ServiceMonitor 與 Service 配置不對應,Service 配置檔案中缺少 ServiceMonitor 配置中 matchLabels 所對應的 label ,ServiceMonitor 中的 port 沒有對應 Service 中的 ports 配置,修正後的配置如下:
service-blog-web.yaml

apiVersion: v1
kind: Service
metadata:
  name: blog-web
  labels:
    app: blog-web
spec:
  type: NodePort
  selector:
    app: blog-web
  ports:
  - name: http-blog-web 
    nodePort: 30080
    port: 80
    targetPort: 80

servicemonitor-blog-web.yaml

kind: ServiceMonitor
apiVersion: monitoring.coreos.com/v1
metadata:
  name: blog-web-monitor 
  labels:
    app: blog-web
spec:
  selector:
    matchLabels:
      app: blog-web
  endpoints: 
  - port: http-blog-web

用修正後的配置部署後,pod 終於被發現了:

production/blog-web-monitor/0 (0/5 up) 

但是這些 pod 全部處於 down 狀態。

Endpoint	                      State	 Scrape Duration	Error
http://192.168.107.233:80/metrics DOWN	 server returned HTTP status 400 Bad Request

透過園子裡的博文 使用Kubernetes演示金絲雀釋出 知道了原來需要應用自己提供 metrics 監控指標資料讓 prometheus 抓取。

標準Tomcat自帶的應用沒有/metrics這個路徑,prometheus獲取不到它能識別的格式資料,而指標資料就是從/metrics這裡獲取的。所以我們使用標準Tomcat不行或者你就算有這個/metrics這個路徑,但是返回的格式不符合prometheus的規範也是不行的。

我們的應用是用 ASP.NET Core 開發的,所以選用了 prometheus-net ,由它提供 metrics 資料給 prometheus 抓取。

  • 安裝 nuget 包
dotnet add package prometheus-net.AspNetCore
  • 新增 HttpMetrics 中介軟體
app.UseRouting();
app.UseHttpMetrics();
  • 新增 MapMetric 路由
app.UseEndpoints(endpoints =>
{
   endpoints.MapMetrics();
};

當透過下面的命令確認透過 /metrics 路徑可以獲取監控資料時,

$ docker exec -t $(docker ps -f name=blog-web_blog-web -q | head -1) curl 127.0.0.1/metrics | grep http_request_duration_seconds_sum
http_request_duration_seconds_sum{code="200",method="GET",controller="AggSite",action="SiteHome"} 0.44973779999999997
http_request_duration_seconds_sum{code="200",method="GET",controller="",action=""} 0.0631272

Prometheus 控制檯 /targets 頁面就能看到 blog-web 對應的 pod 都處於 up 狀態。

production/blog-web-monitor/0 (5/5 up)

這時透過 custom metrics api 可以查詢到一些 http_requests 相關的指標。

$ kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/ | jq . | egrep pods/*/http_requests 
      "name": "pods/http_requests_in_progress",
      "name": "pods/http_requests_received"

這裡的 http_requests_received 就是 QPS(每秒請求數) 指標資料,用下面的命令請求 custom metrics api 獲取資料:

kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/production/pods/*/http_requests_received | jq .

其中1個 pod 的 http_requests_received 指標資料如下:

{
  "kind": "MetricValueList",
  "apiVersion": "custom.metrics.k8s.io/v1beta1",
  "metadata": {
    "selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/production/pods/%2A/http_requests_received"
  },
  "items": [
    {
      "describedObject": {
        "kind": "Pod",
        "namespace": "production",
        "name": "blog-web-65f7bdc996-8qp5c",
        "apiVersion": "/v1"
      },
      "metricName": "http_requests_received",
      "timestamp": "2020-01-18T14:35:34Z",
      "value": "133m",
      "selector": null
    }
  ]
}

其中的 133m 表示 0.133

然後就可以在 HPA 配置檔案中基於這個指標進行自動伸縮

apiVersion: autoscaling/v2beta2 
kind: HorizontalPodAutoscaler
metadata: 
  name: blog-web
spec: 
  scaleTargetRef: 
    apiVersion: apps/v1 
    kind: Deployment 
    name: blog-web
  minReplicas: 5
  maxReplicas: 12 
  metrics: 
  - type: Pods
    pods:
      metric:
        name: http_requests_received
      target:
        type: AverageValue
        averageValue: 100

終於搞定了!

# kubectl get hpa
NAME       REFERENCE             TARGETS    MINPODS   MAXPODS   REPLICAS   AGE
blog-web   Deployment/blog-web   133m/100   5         12        5          4d

相關文章