1. 程式人生 > >Prometheus監控實踐:Kubernetes叢集監控_Kubernetes中文社群

Prometheus監控實踐:Kubernetes叢集監控_Kubernetes中文社群

本文將總結一下我們目前使用Prometheus對Kubernetes叢集監控的實踐。 我們選擇Prometheus作為監控系統主要在以下各層面實現監控:

  • 基礎設施層:監控各個主機伺服器資源(包括Kubernetes的Node和非Kubernetes的Node),如CPU,記憶體,網路吞吐和頻寬佔用,磁碟I/O和磁碟使用等指標。
  • 中介軟體層:監控獨立部署於Kubernetes叢集之外的中介軟體,例如:MySQL、Redis、RabbitMQ、ElasticSearch、Nginx等。
  • Kubernetes叢集:監控Kubernetes叢集本身的關鍵指標
  • Kubernetes叢集上部署的應用:監控部署在Kubernetes叢集上的應用

1.基礎設施層和中介軟體層的監控

其中基礎設施層監控指標的拉取肯定是來在Prometheus的node_exporter,因為我們要監控的伺服器節點既包含Kubernetes節點又包含其他部署獨立中介軟體的節點, 所以我們並沒有將node_exporter以daemonset的形式部署到k8s上,而是使用ansible將node_exporter以二進位制的形式部署到所有要監控的伺服器上。 而負責從node_exporter拉取指標的Prometheus也是用ansible獨立部署在Kubernetes叢集外部的。Prometheus的配置檔案prometheus.yml使用ansible的j2模板生成。

中間層的監控和基礎設施層監控類似,使用ansible在各個中介軟體所在的主機上部署各個中介軟體的exporter,仍然使用上面在Kubernetes叢集外部的這個Prometheus從這些exporter拉取指標,Prometheus的配置檔案prometheus.yml使用ansible的j2模板生成。

2.Kubernetes叢集的監控

要實現對Kubernetes叢集的監控,因為Kubernetes的rbac機制以及證書認證,當然是把Prometheus部署在Kubernetes叢集上最方便。可是我們目前的監控系統是以k8s叢集外部的Prometheus為主的,grafana和告警都是使用這個外部的Prometheus,如果還需要在Kubernetes叢集內部部署一個Prometheus的話一定要把它桶外部的Prometheus聯合起來,好在Prometheus支援Federation。

2.1 Prometheus的Federation簡介

Federation允許一個Prometheus從另一個Prometheus中拉取某些指定的時序資料。Federation是Prometheus提供的擴充套件機制,允許Prometheus從一個節點擴充套件到多個節點,實際使用中一般會擴充套件成樹狀的層級結構。下面是Prometheus官方文件中對federation的配置示例:

- job_name: 'federate'
  scrape_interval: 15s

  honor_labels: true
  metrics_path: '/federate'

  params:
    'match[]':
      - '{job="prometheus"}'
      - '{__name__=~"job:.*"}'

  static_configs:
    - targets:
      - 'source-prometheus-1:9090'
      - 'source-prometheus-2:9090'
      - 'source-prometheus-3:9090'

這段配置所屬的Prometheus將從source-prometheus-1 ~ 3這3個Prometheus的/federate端點拉取監控資料。 match[]引數指定了只拉取帶有job=”prometheus標籤的指標,或者名稱以job開頭的指標。

2.2 在Kubernetes上部署Prometheus

前面已經介紹了將使用Prometheus federation的形式,k8s叢集外部的Prometheus從k8s叢集中Prometheus拉取監控資料,外部的Prometheus才是監控資料的儲存。 k8s叢集中部署Prometheus的資料儲存層可以簡單的使用emptyDir,資料只保留24小時(或更短時間)即可,部署在k8s叢集上的這個Prometheus例項即使發生故障也可以放心的讓它在叢集節點中漂移。

在k8s上部署Prometheus十分簡單,只需要下面4個檔案:prometheus.rbac.yml, prometheus.config.yml, prometheus.deploy.yml, prometheus.svc.yml。 下面給的例子中將Prometheus部署到kube-system名稱空間。

prometheus.rbac.yml定義了Prometheus容器訪問k8s apiserver所需的ServiceAccount和ClusterRole及ClusterRoleBinding,參考Prometheus原始碼中庫中的例子:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
rules:
- apiGroups: [""]
  resources:
  - nodes
  - nodes/proxy
  - services
  - endpoints
  - pods
  verbs: ["get", "list", "watch"]
- apiGroups:
  - extensions
  resources:
  - ingresses
  verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics"]
  verbs: ["get"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: prometheus
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: prometheus
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: prometheus
subjects:
- kind: ServiceAccount
  name: prometheus
  namespace: kube-system

prometheus.config.yml configmap中的prometheus的配置檔案,參考Prometheus原始碼中庫中的例子:

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: kube-system
data:
  prometheus.yml: |
    global:
      scrape_interval:     15s
      evaluation_interval: 15s
    scrape_configs:
    
    - job_name: 'kubernetes-apiservers'
      kubernetes_sd_configs:
      - role: endpoints
      scheme: https
      tls_config:
        ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
      bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
      relabel_configs:
      - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
        action: keep
        regex: default;kubernetes;https
    
    - job_name: 'kubernetes-nodes'
      kubernetes_sd_configs:
      - role: node
      scheme: https
      tls_config:
        ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
      bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
      relabel_configs:
      - action: labelmap
        regex: __meta_kubernetes_node_label_(.+)
      - target_label: __address__
        replacement: kubernetes.default.svc:443
      - source_labels: [__meta_kubernetes_node_name]
        regex: (.+)
        target_label: __metrics_path__
        replacement: /api/v1/nodes/${1}/proxy/metrics

    - job_name: 'kubernetes-cadvisor'
      kubernetes_sd_configs:
      - role: node
      scheme: https
      tls_config:
        ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
      bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
      relabel_configs:
      - action: labelmap
        regex: __meta_kubernetes_node_label_(.+)
      - target_label: __address__
        replacement: kubernetes.default.svc:443
      - source_labels: [__meta_kubernetes_node_name]
        regex: (.+)
        target_label: __metrics_path__
        replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor

    - job_name: 'kubernetes-service-endpoints'
      kubernetes_sd_configs:
      - role: endpoints
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
        action: replace
        target_label: __scheme__
        regex: (https?)
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
      - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
        action: replace
        target_label: __address__
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
      - action: labelmap
        regex: __meta_kubernetes_service_label_(.+)
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: kubernetes_namespace
      - source_labels: [__meta_kubernetes_service_name]
        action: replace
        target_label: kubernetes_name

    - job_name: 'kubernetes-services'
      kubernetes_sd_configs:
      - role: service
      metrics_path: /probe
      params:
        module: [http_2xx]
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
        action: keep
        regex: true
      - source_labels: [__address__]
        target_label: __param_target
      - target_label: __address__
        replacement: blackbox-exporter.example.com:9115
      - source_labels: [__param_target]
        target_label: instance
      - action: labelmap
        regex: __meta_kubernetes_service_label_(.+)
      - source_labels: [__meta_kubernetes_namespace]
        target_label: kubernetes_namespace
      - source_labels: [__meta_kubernetes_service_name]
        target_label: kubernetes_name

    - job_name: 'kubernetes-ingresses'
      kubernetes_sd_configs:
      - role: ingress
      relabel_configs:
      - source_labels: [__meta_kubernetes_ingress_annotation_prometheus_io_probe]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_ingress_scheme,__address__,__meta_kubernetes_ingress_path]
        regex: (.+);(.+);(.+)
        replacement: ${1}://${2}${3}
        target_label: __param_target
      - target_label: __address__
        replacement: blackbox-exporter.example.com:9115
      - source_labels: [__param_target]
        target_label: instance
      - action: labelmap
        regex: __meta_kubernetes_ingress_label_(.+)
      - source_labels: [__meta_kubernetes_namespace]
        target_label: kubernetes_namespace
      - source_labels: [__meta_kubernetes_ingress_name]
        target_label: kubernetes_name

    - job_name: 'kubernetes-pods'
      kubernetes_sd_configs:
      - role: pod
      relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
      - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
        target_label: __address__
      - action: labelmap
        regex: __meta_kubernetes_pod_label_(.+)
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: kubernetes_namespace
      - source_labels: [__meta_kubernetes_pod_name]
        action: replace
        target_label: kubernetes_pod_name

prometheus.deploy.yml定義Prometheus的部署:

---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  labels:
    name: prometheus-deployment
  name: prometheus
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prometheus
  template:
    metadata:
      labels:
        app: prometheus
    spec:
      containers:
      - image: harbor.frognew.com/prom/prometheus:2.0.0
        name: prometheus
        command:
        - "/bin/prometheus"
        args:
        - "--config.file=/etc/prometheus/prometheus.yml"
        - "--storage.tsdb.path=/prometheus"
        - "--storage.tsdb.retention=24h"
        ports:
        - containerPort: 9090
          protocol: TCP
        volumeMounts:
        - mountPath: "/prometheus"
          name: data
        - mountPath: "/etc/prometheus"
          name: config-volume
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
          limits:
            cpu: 500m
            memory: 2500Mi
      serviceAccountName: prometheus
      imagePullSecrets: 
        - name: regsecret
      volumes:
      - name: data
        emptyDir: {}
      - name: config-volume
        configMap:
          name: prometheus-config       

prometheus.svc.yml定義Prometheus的Servic,需要將Prometheus以NodePort, LoadBalancer或使用Ingress暴露到叢集外部,這樣外部的Prometheus才能訪問它:

---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: prometheus
  name: prometheus
  namespace: kube-system
spec:
  type: NodePort
  ports:
  - port: 9090
    targetPort: 9090
    nodePort: 30003
  selector:
    app: prometheus

2.3 配置Prometheus Federation

完成Kubernetes叢集上的Prometheus的部署之後,下面將配置叢集外部的Prometheus使其從叢集內部的Prometheus拉取資料。 實際上只需以靜態配置的形式新增一個job就可以:

- job_name: 'federate'
    scrape_interval: 15s
    honor_labels: true
    metrics_path: '/federate'
    params:
      'match[]':
        - '{job=~"kubernetes-.*"}'
    static_configs:
      - targets:
        - '<nodeip>:30003'

注意上面的配置是外部Prometheus拉取k8s叢集上面所有名稱以kubernetes-的job的監控資料。

2.4 Kubernetes叢集Grafana Dashboard

2.5 Kubernetes叢集告警規則

可以對apiserver和kubelet兩個關鍵元件的存活狀態進行監控,規則如下:

up{job=~"kubernetes-apiservers|kubernetes-nodes|kubernetes-cadvisor"} == 0

更多的告警規則可以通過檢視上面2.4中的grafana dashboard中監控的關鍵指標,選擇和合適的指標進行設定,實際上一套好的監控系統的監控指標和告警規則並不是越多越好。

3.Kubernetes叢集上部署應用的監控

Kubernetes叢集上部署應用的監控需要從兩個方面:

  • Kubernetes叢集上Pod, DaemonSet, Deployment, Job, CronJob等各種資源物件的狀態需要監控,這也反映了使用這些資源部署的應用的狀態。但通過檢視前面Prometheus從k8s叢集拉取的指標(這些指標主要來自apiserver和kubelet中整合的cAdvisor),並沒有具體的各種資源物件的狀態指標。對於Prometheus來說,當然是需要引入新的exporter來暴露這些指標,Kubernetes提供了一個kube-state-metrics正式我們需要。
  • Kubernetes叢集上應用內部的監控,這個與具體應用的開發語言,開發框架和具體技術緊密相關,比如Java應用的JVM監控,Go應用的GC監控等等,這個需要應用自身作為Exporter暴露這些指標或在應用的Pod中起一個exporter的sidecar容器。

這裡將主要介紹kube-state-metrics,而對於應用內部的監控實踐後邊有時間再單獨總結。kube-state-metrics使用kubernetes的go語言客戶端client-go可以從Kubernetes叢集中獲取各種資源物件的指標。

3.1 在Kubernetes上部署kube-state-metrics

kube-state-metrics已經給出了在Kubernetes部署的manifest定義檔案,具體的檔案定義都在這裡

將kube-state-metrics部署到Kubernetes上之後,就會發現Kubernetes叢集中的Prometheus會在kubernetes-service-endpoints這個job下自動服務發現kube-state-metrics,並開始拉取metrics,當然叢集外部的Prometheus也能從叢集中的Prometheus拉取到這些資料了。這是因為上2.2中prometheus.config.yml中Prometheus的配置檔案job kubernetes-service-endpoints的配置。而部署kube-state-metrics的manifest定義檔案kube-state-metrics-service.yaml對kube-state-metricsService的定義包含annotation prometheus.io/scrape: ‘true’,因此kube-state-metrics的endpoint可以被Prometheus自動服務發現。

關於kube-state-metrics暴露的所有監控指標可以參考kube-state-metrics的文件kube-state-metrics Documentation

3.2 告警規則

目前我們根據從kube-state-metrics獲取的監控指標,制定了以下告警規則:

  • 存在執行失敗的Job: kube_job_status_failed{job=”kubernetes-service-endpoints”,k8s_app=”kube-state-metrics”}==1
  • 叢集節點狀態錯誤: kube_node_status_condition{condition=”Ready”,status!=”true”}==1
  • 叢集節點記憶體或磁碟資源短缺: kube_node_status_condition{condition=~”OutOfDisk|MemoryPressure|DiskPressure”,status!=”false”}==1
  • 叢集中存在失敗的PVC:kube_persistentvolumeclaim_status_phase{phase=”Failed”}==1
  • 叢集中存在啟動失敗的Pod:kube_pod_status_phase{phase=~”Failed|Unknown”}==1
  • 最近30分鐘內有Pod容器重啟: changes(kube_pod_container_status_restarts[30m])>0

其中關於Pod狀態的的告警尤為重要,可以在Jenkins完成CI/CD自動釋出後,不用守在Kubernetes Dashboard旁邊確認這個Deployment關聯的Pod已經全部啟動,因為如果出現問題是會收到Prometheus的告警的。

參考