kube-prometheus 部署

7 minute read

概览

Prometheus

Prometheus 是一个开源的系统监控和警报工具包,最初在 SoundCloud 建立。自2012年成立以来,许多公司和组织都采用了 Prometheus,该项目有一个非常活跃的开发者和用户社区。它现在是一个独立的开源项目,独立于任何公司进行维护。为了强调这一点,并明确项目的治理结构,Prometheus 在2016年加入了云原生计算基金会(Cloud Native Computing Foundation),成为继 Kubernetes 之后的第二个托管项目。

Prometheus 以时间序列数据的形式收集和存储其指标,也就是说,指标信息与记录的时间戳一起存储,同时还有被称为标签的可选键值对。

首先需要明确指出的是,Kubernetes 项目的监控体系曾经非常繁杂,在社区中也有很多方案。但这套体系发展到今天,已经完全演变成了以 Prometheus 项目为核心的一套统一的方案。

实际上,Prometheus 项目是当年 CNCF 基金会起家时的“第二把交椅”。而这个项目发展到今天,已经全面接管了 Kubernetes 项目的整套监控体系。

比较有意思的是,Prometheus 项目与 Kubernetes 项目一样,也来自于 Google 的 Borg 体系,它的原型系统,叫作 BorgMon,是一个几乎与 Borg 同时诞生的内部监控系统。而 Prometheus 项目的发起原因也跟 Kubernetes 很类似,都是希望通过对用户更友好的方式,将 Google 内部系统的设计理念,传递给用户和开发者。

Prometheus 的架构和它的一些生态系统组件:

Prometheus Operator

Prometheus Operator 使用 Kubernetes custom resources 来简化 Prometheus, Alertmanager 和相关监控组件的部署和配置。

kube-prometheus

kube-prometheus 提供了基于 Prometheus 和 Prometheus Operator 的完整集群监控栈的配置示例。这包括部署多个 Prometheus 和 Alertmanager 实例,收集节点指标的指标导出器(如 node_exporter),连接 Prometheus 和各种指标端点的 scrape 目标配置,以及用于通知集群中潜在问题的警报规则示例。

该软件包中包括的组件:

CustomResourceDefinitions

Prometheus Operator 的一个核心功能是监控 Kubernetes API 服务器对特定对象的更改,并确保当前的 Prometheus部署与这些对象相符。Operator 对以下自定义资源定义(CRD)采取行动:

  • Prometheus, 定义了一个期望的 Prometheus 部署。
  • Alertmanager, 定义了一个期望的 Alertmanager 部署。
  • ThanosRuler, 定义了一个理想的 Thanos Ruler 部署。
  • ServiceMonitor, 其中声明性地指定应如何监控 Kubernetes services。Operator 根据 API server 中对象的当前状态自动生成 Prometheus 的 scrape 配置。
  • PodMonitor, 其中声明性地指定了应如何监控 pod 组。Operator 会根据 API server 中对象的当前状态自动生成 Prometheus 的 scrape 配置。
  • Probe, 其中声明性地指定应如何监控入口组或静态目标。Operator 会根据定义自动生成 Prometheus 的 scrape 配置。
  • PrometheusRule, 其中定义了一套所需的 Prometheus 警报和/或记录规则s。Operator 生成一个规则文件,可由 Prometheus 实例使用。
  • AlertmanagerConfig, 其中声明性地指定了Alertmanager 配置的子部分,允许将警报路由到自定义接收者,并设置抑制规则。

Prometheus Operator 自动检测 Kubernetes API server 中对上述任何对象的变化,并确保匹配的部署和配置保持同步。

获取 kube-prometheus 项目

从 GitHub 拉取 kube-prometheus:

$ git clone https://github.com/prometheus-operator/kube-prometheus.git
$ cd kube-prometheus/

或下载当前主分支的 zip 文件并解压其内容:

github.com/prometheus-operator/kube-prometheus/archive/main.zip

一旦你的机器上有了这些文件,就可以进入项目的根目录。

初始化设置

清单整理

这个项目的 Kubernetes 清单文件都放在 manifests 目录下,而且比较繁多,为了方便后续区分管理,将这些文件进行整理分类。

创建相应的文件夹:

$ cd manifests
$ mkdir -p alertmanager blackbox-exporter grafana kube-state-metrics node-exporter prometheus prometheus-adapter prometheus-operator prometheus-rule service-monitor

移动各文件到相应目录:

$ mv alertmanager-* alertmanager/
$ mv blackboxExporter-* blackbox-exporter/
$ mv grafana-* grafana/
$ mv *-serviceMonitor* service-monitor/
$ mv kubeStateMetrics-* kube-state-metrics/
$ mv nodeExporter-* node-exporter/
$ mv prometheus-*.yaml prometheus/
$ mv prometheusAdapter-* prometheus-adapter/
$ mv prometheusOperator-* prometheus-operator/
$ mv *-prometheusRule* prometheus-rule/

移动完毕后目录结构如下:

$ tree ../manifests/
../manifests/
├── alertmanager
│   ├── alertmanager-alertmanager.yaml
│   ├── alertmanager-networkPolicy.yaml
│   ├── alertmanager-podDisruptionBudget.yaml
│   ├── alertmanager-prometheusRule.yaml
│   ├── alertmanager-secret.yaml
│   ├── alertmanager-serviceAccount.yaml
│   ├── alertmanager-serviceMonitor.yaml
│   └── alertmanager-service.yaml
├── blackbox-exporter
│   ├── blackboxExporter-clusterRoleBinding.yaml
│   ├── blackboxExporter-clusterRole.yaml
│   ├── blackboxExporter-configuration.yaml
│   ├── blackboxExporter-deployment.yaml
│   ├── blackboxExporter-networkPolicy.yaml
│   ├── blackboxExporter-serviceAccount.yaml
│   ├── blackboxExporter-serviceMonitor.yaml
│   └── blackboxExporter-service.yaml
├── grafana
│   ├── grafana-config.yaml
│   ├── grafana-dashboardDatasources.yaml
│   ├── grafana-dashboardDefinitions.yaml
│   ├── grafana-dashboardSources.yaml
│   ├── grafana-deployment.yaml
│   ├── grafana-networkPolicy.yaml
│   ├── grafana-prometheusRule.yaml
│   ├── grafana-pvc.yaml
│   ├── grafana-serviceAccount.yaml
│   ├── grafana-serviceMonitor.yaml
│   └── grafana-service.yaml
├── kube-state-metrics
│   ├── kubeStateMetrics-clusterRoleBinding.yaml
│   ├── kubeStateMetrics-clusterRole.yaml
│   ├── kubeStateMetrics-deployment.yaml
│   ├── kubeStateMetrics-networkPolicy.yaml
│   ├── kubeStateMetrics-prometheusRule.yaml
│   ├── kubeStateMetrics-serviceAccount.yaml
│   └── kubeStateMetrics-service.yaml
├── node-exporter
│   ├── nodeExporter-clusterRoleBinding.yaml
│   ├── nodeExporter-clusterRole.yaml
│   ├── nodeExporter-daemonset.yaml
│   ├── nodeExporter-networkPolicy.yaml
│   ├── nodeExporter-prometheusRule.yaml
│   ├── nodeExporter-serviceAccount.yaml
│   └── nodeExporter-service.yaml
├── prometheus
│   ├── prometheus-clusterRoleBinding.yaml
│   ├── prometheus-clusterRole.yaml
│   ├── prometheus-networkPolicy.yaml
│   ├── prometheus-podDisruptionBudget.yaml
│   ├── prometheus-prometheusRule.yaml
│   ├── prometheus-prometheus.yaml
│   ├── prometheus-roleBindingConfig.yaml
│   ├── prometheus-roleBindingSpecificNamespaces.yaml
│   ├── prometheus-roleConfig.yaml
│   ├── prometheus-roleSpecificNamespaces.yaml
│   ├── prometheus-serviceAccount.yaml
│   └── prometheus-service.yaml
├── prometheus-adapter
│   ├── prometheusAdapter-apiService.yaml
│   ├── prometheusAdapter-clusterRoleAggregatedMetricsReader.yaml
│   ├── prometheusAdapter-clusterRoleBindingDelegator.yaml
│   ├── prometheusAdapter-clusterRoleBinding.yaml
│   ├── prometheusAdapter-clusterRoleServerResources.yaml
│   ├── prometheusAdapter-clusterRole.yaml
│   ├── prometheusAdapter-configMap.yaml
│   ├── prometheusAdapter-deployment.yaml
│   ├── prometheusAdapter-networkPolicy.yaml
│   ├── prometheusAdapter-podDisruptionBudget.yaml
│   ├── prometheusAdapter-roleBindingAuthReader.yaml
│   ├── prometheusAdapter-serviceAccount.yaml
│   └── prometheusAdapter-service.yaml
├── prometheus-operator
│   ├── prometheusOperator-clusterRoleBinding.yaml
│   ├── prometheusOperator-clusterRole.yaml
│   ├── prometheusOperator-deployment.yaml
│   ├── prometheusOperator-networkPolicy.yaml
│   ├── prometheusOperator-prometheusRule.yaml
│   ├── prometheusOperator-serviceAccount.yaml
│   └── prometheusOperator-service.yaml
├── prometheus-rule
│   ├── kubePrometheus-prometheusRule.yaml
│   └── kubernetesControlPlane-prometheusRule.yaml
├── service-monitor
│   ├── kubernetesControlPlane-serviceMonitorApiserver.yaml
│   ├── kubernetesControlPlane-serviceMonitorCoreDNS.yaml
│   ├── kubernetesControlPlane-serviceMonitorKubeControllerManager.yaml
│   ├── kubernetesControlPlane-serviceMonitorKubelet.yaml
│   ├── kubernetesControlPlane-serviceMonitorKubeScheduler.yaml
│   ├── kubeStateMetrics-serviceMonitor.yaml
│   ├── nodeExporter-serviceMonitor.yaml
│   ├── prometheusAdapter-serviceMonitor.yaml
│   ├── prometheusOperator-serviceMonitor.yaml
│   └── prometheus-serviceMonitor.yaml
└── setup
    ├── 0alertmanagerConfigCustomResourceDefinition.yaml
    ├── 0alertmanagerCustomResourceDefinition.yaml
    ├── 0podmonitorCustomResourceDefinition.yaml
    ├── 0probeCustomResourceDefinition.yaml
    ├── 0prometheusCustomResourceDefinition.yaml
    ├── 0prometheusruleCustomResourceDefinition.yaml
    ├── 0servicemonitorCustomResourceDefinition.yaml
    ├── 0thanosrulerCustomResourceDefinition.yaml
    └── namespace.yaml

11 directories, 94 files

镜像修改

由于这个堆栈的一些镜像来自 registry.k8s.io 镜像仓库,国内无法访问,所以需要修改为国内的镜像。

# kube-state-metrics
$ sed -i "s,image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.7.0,image: registry.cn-shenzhen.aliyuncs.com/aluopyy/kube-state-metrics:v2.7.0," kube-state-metrics/kubeStateMetrics-deployment.yaml

# prometheus-adapter
$ sed -i "s,image: registry.k8s.io/prometheus-adapter/prometheus-adapter:v0.10.0,image: registry.cn-shenzhen.aliyuncs.com/aluopyy/prometheus-adapter:v0.10.0," prometheus-adapter/prometheusAdapter-deployment.yaml

数据持久化配置

kube-prometheus 默认是通过 emptyDir 进行挂载的,emptyDir 挂载的数据的生命周期和 Pod 生命周期一致,如果 Pod 挂掉了,那么数据也就丢失了,这也就是为什么我们重建 Pod 后之前的数据就没有了的原因,所以这里修改它的持久化配置。

Prometheus 持久化

prometheus 是使用的 StatefulSet 方式部署的,所以直接将 StorageClass 配置到 yaml 文件里面,在 prometheus/prometheus-prometheus.yaml 文件最下面添加持久化配置:

apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  labels:
    app.kubernetes.io/component: prometheus
    app.kubernetes.io/instance: k8s
    app.kubernetes.io/name: prometheus
    app.kubernetes.io/part-of: kube-prometheus
    app.kubernetes.io/version: 2.41.0
  name: k8s
  namespace: monitoring
spec:
  alerting:
    alertmanagers:
    - apiVersion: v2
      name: alertmanager-main
      namespace: monitoring
      port: web
  enableFeatures: []
  externalLabels: {}
  image: quay.io/prometheus/prometheus:v2.41.0
  nodeSelector:
    kubernetes.io/os: linux
  podMetadata:
    labels:
      app.kubernetes.io/component: prometheus
      app.kubernetes.io/instance: k8s
      app.kubernetes.io/name: prometheus
      app.kubernetes.io/part-of: kube-prometheus
      app.kubernetes.io/version: 2.41.0
  podMonitorNamespaceSelector: {}
  podMonitorSelector: {}
  probeNamespaceSelector: {}
  probeSelector: {}
  replicas: 2
  resources:
    requests:
      memory: 400Mi
  ruleNamespaceSelector: {}
  ruleSelector: {}
  securityContext:
    fsGroup: 2000
    runAsNonRoot: true
    runAsUser: 1000
  serviceAccountName: prometheus-k8s
  serviceMonitorNamespaceSelector: {}
  serviceMonitorSelector: {}
  version: 2.41.0
  # 添加如下配置
  storage:
    volumeClaimTemplate:
      spec:
        storageClassName: nfs-client-2
        resources:
          requests:
            storage: 10Gi

Grafana 持久化

由于 Grafana 是使用的 Deployment 方式部署的,所以提前为其创建一个 PVC,新建 grafana/grafana-pvc.yaml 文件内容如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: grafana
  namespace: monitoring
spec:
  storageClassName: nfs-client-2
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

应用 grafana/grafana-pvc.yaml 文件,创建 PVC:

$ kubectl apply -f grafana/grafana-pvc.yaml

修改 grafana/grafana-deployment.yaml 文件持久化配置,应用上面的 PVC:

# 将 emptyDir 修改为使用上面创建的 PVC: grafana
... ...
      volumes:
      #- emptyDir: {}
      - persistentVolumeClaim:
          claimName: grafana
        name: grafana-storage
... ...

部署 kube-prometheus

创建 namespace 和 CRDs:

$ kubectl create -f setup/

安装 prometheus-operator:

$ kubectl apply -f prometheus-operator/

查看 Pod,等待 pod 正常启动后再进行下一步:

$ kubectl get pod -n monitoring
NAME                                   READY   STATUS    RESTARTS   AGE
prometheus-operator-5cd6dbfd4f-r9bwf   2/2     Running   0          20h

安装其他组件:

$ kubectl apply -f alertmanager/
$ kubectl apply -f blackbox-exporter/
$ kubectl apply -f grafana/
$ kubectl apply -f node-exporter/
$ kubectl apply -f kube-state-metrics/
$ kubectl apply -f prometheus/
$ kubectl apply -f prometheus-adapter/
$ kubectl apply -f prometheus-rule/
$ kubectl apply -f service-monitor/

查看 pod 状态:

$ kubectl get pod -n monitoring 
NAME                                   READY   STATUS    RESTARTS   AGE
alertmanager-main-0                    2/2     Running   0          20h
alertmanager-main-1                    2/2     Running   0          20h
alertmanager-main-2                    2/2     Running   0          20h
blackbox-exporter-6fd586b445-crgmj     3/3     Running   0          20h
grafana-7b4c5698c8-5knnk               1/1     Running   0          20h
kube-state-metrics-745b5d8dc9-d9qq9    3/3     Running   0          20h
node-exporter-2k9f8                    2/2     Running   0          19h
... ...
node-exporter-zxsg6                    2/2     Running   0          19h
prometheus-adapter-7f957d749d-59fln    1/1     Running   0          20h
prometheus-adapter-7f957d749d-9c6n8    1/1     Running   0          20h
prometheus-k8s-0                       2/2     Running   0          20h
prometheus-k8s-1                       2/2     Running   0          20h
prometheus-operator-5cd6dbfd4f-r9bwf   2/2     Running   0          20h

使用 Ingress 暴露 Prometheus 相关服务

默认情况下,限制访问 prometheus 组件的 NetworkPolicies 被添加,修改相应的 NetworkPolicies 使 prometheus 相关组件能够被访问。

prometheus/prometheus-networkPolicy.yaml, alertmanager/alertmanager-networkPolicy.yaml, grafana/grafana-networkPolicy.yaml 文件中的相应位置添加如下内容:

...
  ingress:
  - from:
    # 在 NetworkPolicies 中添加 Ingress 命名空间
    - namespaceSelector:
        matchLabels:
          app.kubernetes.io/name: ingress-nginx
...

应用更改:

$ kubectl apply -f prometheus/prometheus-networkPolicy.yaml -f alertmanager/alertmanager-networkPolicy.yaml -f grafana/grafana-networkPolicy.yaml

新建 prometheus-stack-ingress.yaml 文件内容如下:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: prometheus-stack
  namespace: monitoring
  labels:
    app.kubernetes.io/name: prometheus-stack
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/affinity: cookie
spec:
  ingressClassName: nginx
  #tls:
  #- hosts:
  #    - prometheus.mydomain.com
  #  secretName: mydomain-tls-secret
  #- hosts:
  #    - alertmanager.mydomain.com
  #  secretName: mydomain-tls-secret
  #- hosts:
  #    - grafana.mydomain.com
  #  secretName: mydomain-tls-secret
  rules:
  - host: prometheus.mydomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: prometheus-k8s
            port:
              name: web
  - host: alertmanager.mydomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: alertmanager-main
            port:
              name: web
  - host: grafana.mydomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: grafana
            port:
              name: http

应用 yaml 文件,创建 ingress 资源:

$ kubectl apply -f prometheus-stack-ingress.yaml

查看 ingress 资源:

$ kubectl get ingress -n monitoring 
NAME               CLASS   HOSTS                                                           ADDRESS         PORTS   AGE
prometheus-stack   nginx   prometheus.mydomain.com,alertmanager.mydomain.com,grafana.mydomain.com   192.168.30.30   80      19h

添加域名解析测试访问。

K8S-Prometheus 监控体系汇总

安装好 Prometheus 之后,我们就可以按照 Metrics 数据的来源,来对 Kubernetes 的监控体系做一个汇总了。

  • 第一种 Metrics,是宿主机的监控数据。这部分数据的提供,需要借助一个由 Prometheus 维护的 Node Exporter 工具。一般来说,Node Exporter 会以 DaemonSet 的方式运行在宿主机上。其实,所谓的 Exporter,就是代替被监控对象来对 Prometheus 暴露出可以被“抓取”的 Metrics 信息的一个辅助进程。

    而 Node Exporter 可以暴露给 Prometheus 采集的 Metrics 数据, 也不单单是节点的负载(Load)、CPU 、内存、磁盘以及网络这样的常规信息,它的 Metrics 指标可以说是“包罗万象”,你可以查看这个列表来感受一下。

  • 第二种 Metrics,是来自于 Kubernetes 的 API Server、kubelet 等组件的 /metrics API。除了常规的 CPU、内存的信息外,这部分信息还主要包括了各个组件的核心监控指标。比如,对于 API Server 来说,它就会在 /metrics API 里,暴露出各个 Controller 的工作队列(Work Queue)的长度、请求的 QPS 和延迟数据等等。这些信息,是检查 Kubernetes 本身工作情况的主要依据。

  • 第三种 Metrics,是 Kubernetes 相关的监控数据。这部分数据,一般叫作 Kubernetes 核心监控数据(core metrics)。这其中包括了 Pod、Node、容器、Service 等主要 Kubernetes 核心概念的 Metrics。

    其中,容器相关的 Metrics 主要来自于 kubelet 内置的 cAdvisor 服务。在 kubelet 启动后,cAdvisor 服务也随之启动,而它能够提供的信息,可以细化到每一个容器的 CPU 、文件系统、内存、网络等资源的使用情况。

    需要注意的是,这里提到的 Kubernetes 核心监控数据,其实使用的是 Kubernetes 的一个非常重要的扩展能力,叫作 Metrics Server。

Troubleshooting

prometheus-adapter: failed querying node metrics

kube-prometheus 安装后,kubectl top nodes/pods 停止工作。

$ kubectl get apiservice v1beta1.metrics.k8s.io
NAME                     SERVICE                         AVAILABLE   AGE
v1beta1.metrics.k8s.io   monitoring/prometheus-adapter   True        24h

$ kubectl top node
error: metrics not available yet

$ kubectl top pod -n monitoring 
error: Metrics not available for pod monitoring/alertmanager-main-0, age: 18h9m40.422618556s

prometheus-adapter log:

E0914 02:18:28.625558       1 provider.go:284] failed querying node metrics: unable to fetch node CPU metrics: unable to execute query: Get "http://prometheus-k8s.monitoring.svc:9090/api/v1/query?query=sum+by+%28node%29+%28%0A++1+-+irate%28%0A++++node_cpu_seconds_total%7Bmode%3D%22idle%22%7D%5B60s%5D%0A++%29%0A++%2A+on%28namespace%2C+pod%29+group_left%28node%29+%28%0A++++node_namespace_pod%3Akube_pod_info%3A%7Bnode%3D~%22m1%7Cw1%7Cw2%22%7D%0A++%29%0A%29%0Aor+sum+by+%28node%29+%28%0A++1+-+irate%28%0A++++windows_cpu_time_total%7Bmode%3D%22idle%22%2C+job%3D%22windows-exporter%22%2Cnode%3D~%22m1%7Cw1%7Cw2%22%7D%5B4m%5D%0A++%29%0A%29%0A&time=1663121878.624": dial tcp 10.96.221.253:9090: i/o timeout

遇到这个问题,可以通过添加如下 NetworkPolicy 解决:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  labels:
    app.kubernetes.io/component: prometheus
    app.kubernetes.io/instance: k8s
    app.kubernetes.io/name: prometheus
    app.kubernetes.io/part-of: kube-prometheus
    app.kubernetes.io/version: 2.41.0
  name: prometheus-k8s-adapter
  namespace: monitoring
spec:
  egress:
  - {}
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app.kubernetes.io/name: prometheus-adapter
    ports:
    - port: 9090
      protocol: TCP
  podSelector:
    matchLabels:
      app.kubernetes.io/component: prometheus
      app.kubernetes.io/instance: k8s
      app.kubernetes.io/name: prometheus
      app.kubernetes.io/part-of: kube-prometheus
  policyTypes:
  - Egress
  - Ingress
status: {}

Reference documentation