Kubernetes集群监控-使用Prometheus Operator自定义监控

Linux系统
369
0
0
2024-03-03
标签   Kubernetes

虚拟化运维KubernetesPrometheus

Kubernetes集群监控-使用Prometheus Operator自定义监控

王先森2023-12-262023-12-26

服务发现简介

在 Prometheus Operator 中, 我们无需手动编辑配置文件添加 kubernetes_sd_config 配置, Prometheus Operator 提供了下述资源:

  • serviceMonitor:创建 endpoints 级别的服务发现
  • podMonitor:创建 pod 级别的服务发现
  • probe:创建 ingress 级别的服务发现(用于黑盒监控)

通过对这三种 CRD 资源的管理实现 prometheus 动态的服务发现。

除了 Kubernetes 集群中的一些资源对象、节点以及组件都需要监控,有的时候可能还需要根据实际的业务需求去添加自定义的监控项,添加一个自定义监控的步骤也是非常简单的。

  • 第一步建立一个 ServiceMonitor 或 podMonitor 对象,用于 Prometheus 添加监控项
  • 第二步为 ServiceMonitor 或 podMonitor 对象关联 metrics 数据接口的一个 Service 对象
  • 第三步确保 Service 或 Pod 对象可以正确获取到 metrics 数据

接下来就来为大家演示如何添加 etcdtraefikkube-controller-managerkube-scheduler的监控。无论是 Kubernetes 集群外的还是安装在集群内部的监控,这里都将其视作集群外的独立集群,因为对于二者的使用方法没什么特殊之处。

Kubernetes组件监控

kube-scheduler监控

Prometheus Operator 提供了kube-scheduler监控配置文件,先来查看下 kube-scheduler 组件对应的 ServiceMonitor 资源的定义,kubernetesControlPlane-serviceMonitorKubeScheduler.yaml

# kubernetesControlPlane-serviceMonitorKubeScheduler.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    app.kubernetes.io/name: kube-scheduler
    app.kubernetes.io/part-of: kube-prometheus
  name: kube-scheduler
  namespace: monitoring
spec:
  endpoints:
    - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
      interval: 30s                # 每30s获取一次信息
      port: https-metrics          # 对应 service 的端口名
      scheme: https                # 注意是使用 https 协议
      tlsConfig:
        insecureSkipVerify: true   # 跳过安全校验
  jobLabel: app.kubernetes.io/name # 用于从中检索任务名称的标签
  namespaceSelector:               # 表示去匹配某一命名空间中的 Service,如果想从所有的namespace中匹配用any:true
    matchNames:
      - kube-system
  selector:                        # 匹配的 Service 的 labels,如果使用 mathLabels,则下面的所有标签都匹配时才会匹配该 service,如果使用 matchExpressions,则至少匹配一个标签的 service 都会被选择
    matchLabels:
      app.kubernetes.io/name: kube-scheduler

上面是一个典型的 ServiceMonitor 资源对象的声明方式,通过 selector.matchLabelskube-system 这个命名空间下面匹配具有 app.kubernetes.io/name=kube-scheduler 这样的 Service,但是这里采用二进制安装,系统中根本就没有对应的 Service。

$ kubectl get svc -n kube-system -l app.kubernetes.io/name=kube-scheduler
No resources found in kube-system namespace.

所以我们需要去创建一个对应的 Service 对象,才能与 ServiceMonitor 进行关联,由于是集群外部的服务,所以要引入到集群中来我们就需要自定义 Endpoints 对象来创建 Service 对象:

apiVersion: v1
kind: Service
metadata:
  name: kube-scheduler
  namespace: kube-system
  labels:                   # 必须和上面的 ServiceMonitor 下面的 matchLabels 保持一致
    app.kubernetes.io/name: kube-scheduler
spec:
  type: ClusterIP
  clusterIP: None       # 一定要设置 clusterIP:None
  ports:
    - name: https-metrics
      port: 10259
---
apiVersion: v1
kind: Endpoints
metadata:
  name: kube-scheduler
  namespace: kube-system
  labels:
    app.kubernetes.io/name: kube-scheduler
subsets:
  - addresses:
      - ip: 10.1.1.100      # 指定kube-scheduler节点地址,如果是集群则继续向下添加
        nodeName: kube-scheduler
    ports:
      - name: https-metrics
        port: 10259         # 需要注意现在版本默认的安全端口是10259
创建的 kube-scheduler 监控是独立于集群之外的,这种情况下面就需要自定义一个 Endpoints,要注意 metadata 区域的内容要和 Service 保持一致,Service 的 clusterIP 设置为 None。
$ kubectl get Endpoints -n kube-system  kube-scheduler          
NAME             ENDPOINTS          AGE
kube-scheduler   10.1.1.100:10259   40d
$ kubectl get svc -n kube-system kube-scheduler 
NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)     AGE
kube-scheduler   ClusterIP   None         <none>        10259/TCP   40d

创建完成后,隔一小会儿后去 Prometheus 页面上查看 targets 下面 kube-scheduler 已经有采集的目标了,如果报了 connect: connection refused 这样的错误,需要检查 kube-scheduler 启动的时候默认绑定的是IP地址。

$ cat kube-scheduler.conf 
KUBE_SCHEDULER_OPTS="--logtostderr=false \
--v=2 \
--log-dir=/opt/kubernetes/logs \
--leader-elect \
--authentication-kubeconfig=/opt/kubernetes/cfg/kube-scheduler.kubeconfig \
--authorization-kubeconfig=/opt/kubernetes/cfg/kube-scheduler.kubeconfig \
--kubeconfig=/opt/kubernetes/cfg/kube-scheduler.kubeconfig \
--bind-address=10.1.1.100"

Grafana 下面的 Dashboard 查看监控图表信息。

可以用同样的方式来修复下 kube-controller-manager 组件的监控

kube-controller-manager 监控

首先检查 kube-controller-manager 启动的时候默认绑定的是IP地址。

$ cat kube-controller-manager.conf 
KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=false \
--v=2 \
--log-dir=/opt/kubernetes/logs \
--leader-elect=true \
--kubeconfig=/opt/kubernetes/cfg/kube-controller-manager.kubeconfig \
--authentication-kubeconfig=/opt/kubernetes/cfg/kube-controller-manager.kubeconfig \
--authorization-kubeconfig=/opt/kubernetes/cfg/kube-controller-manager.kubeconfig \
--bind-address=10.1.1.100 \
--cluster-cidr=172.17.0.0/16 \
--service-cluster-ip-range=192.168.0.0/16 \
--cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \
--cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem  \
--root-ca-file=/opt/kubernetes/ssl/ca.pem \
--service-account-private-key-file=/opt/kubernetes/ssl/serviceaccount-key.pem \
--cluster-signing-duration=87600h0m0s"

引入到集群中来我们就需要自定义 Endpoints 对象来创建 Service 对象

apiVersion: v1
kind: Service
metadata:
  name: kube-controller-manager
  namespace: kube-system
  labels:
    app.kubernetes.io/name: kube-controller-manager
spec:
  type: ClusterIP
  clusterIP: None # 一定要设置 clusterIP:None
  ports:
    - name: https-metrics
      port: 10257
---
apiVersion: v1
kind: Endpoints
metadata:
  name: kube-controller-manager
  namespace: kube-system
  labels:
    app.kubernetes.io/name: kube-controller-manager
subsets:
  - addresses:
      - ip: 10.1.1.100      # 指定kube-controller-manager节点地址,如果是集群则继续向下添加
        nodeName: kube-controller-manager
    ports:
      - name: https-metrics
        port: 10257

Grafana 下面的 Dashboard 查看监控图表信息。

Etcd 监控

同样也可以使用这种方式来监控 etcd集群。

创建对应的 ServiceMonitor 对象

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: etcd-cluster-k8s
  namespace: monitoring
  labels:
    k8s-app: etcd-k8s
spec:
  jobLabel: k8s-app
  endpoints:
    - port: port
      interval: 15s
      scheme: https
      tlsConfig:
        caFile: /opt/etcd/ssl/ca.pem                 # ca证书在Prometheus容器存储位置
        certFile: /opt/etcd/ssl/server.pem           # pem证书在Prometheus容器存储位置
        keyFile: /opt/etcd/ssl/server-key.pem        # key证书在Prometheus容器存储位置
        insecureSkipVerify: true                     # 忽略证书安全性,可以同自建证书
  selector:
    matchLabels:
      k8s-app: etcd
  namespaceSelector:
    matchNames:
      - kube-system

匹配 kube-system 这个命名空间下面的具有 k8s-app=etcd-k8s 这个 label 标签的 Service,jobLabel 表示用于检索 job 任务名称的标签,由于 etcd 的 metrics 接口在 2379 端口下面。

引入到集群中来我们就需要自定义 Endpoints 对象来创建 Service 对象

---
apiVersion: v1
kind: Service
metadata:
  name: etcd-k8s
  namespace: kube-system
  labels:
    k8s-app: etcd
spec:
  type: ClusterIP
  clusterIP: None # 一定要设置 clusterIP:None
  ports:
    - name: port
      port: 2379
---
apiVersion: v1
kind: Endpoints
metadata:
  name: etcd-k8s
  namespace: kube-system
  labels:
    k8s-app: etcd
subsets:
  - addresses:
      - ip: 10.1.1.100      # 指定etcd节点地址,如果是集群则继续向下添加
        nodeName: etcd-cluster-1
      - ip: 10.1.1.120      # 指定etcd节点地址,如果是集群则继续向下添加
        nodeName: etcd-cluster-2
      - ip: 10.1.1.130      # 指定etcd节点地址,如果是集群则继续向下添加
        nodeName: etcd-cluster-3
    ports:
      - name: port
        port: 2379

创建存放证书的Secret

kubectl create secret generic etcd-certs   --from-file=/opt/etcd/ssl/etcd-client-key.pem   --from-file=/opt/etcd/ssl/etcd-client.pem   --from-file=/opt/etcd/ssl/ca.pem   -n monitoring

编辑prometheus-prometheus.yaml

# prometheus-operator/prometheus-prometheus.yaml
......
  volumeMounts:
  - mountPath: /opt/etcd/ssl
    name: etcd-certs
  volumes:
  - name: etcd-certs
    secret:
      secretName: etcd-certs
      defaultMode: 0640
......

创建完成后,隔一会儿去 Prometheus 的 Dashboard 中查看 targets,便会有 etcd 的监控项了

数据采集到后,可以在 grafana 中导入编号为 3070 的 dashboard,就可以获取到 etcd 的监控图表

Traefik 监控

以 Traefik 为例, 使用 podMonitor 资源监控 Traefik。traefik 安装请参考 traefik系列文章

创建 podMonitor

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  labels:
    app.kubernetes.io/name: traefik
  name: traefik
  namespace: monitoring
spec:
  jobLabel: app.kubernetes.io/name
  podMetricsEndpoints:
  - interval: 15s
    path: /metrics
    port: metrics            # 确保与 traefik 的 containerPort 名称一致
  namespaceSelector:
    matchNames:
    - kube-system            # 确保命名空间正确
  selector:
    matchLabels: 
      app: traefik-v2        # 确保 label 配置与pod label 配置正确

查看 prometheus

数据采集到后,可以在 grafana 中导入编号为 4475 的 dashboard,就可以获取到 traefik 的监控图表

集群范围的自动发现

当 k8s 集群中 service 和 pod 达到一定规模后手动一个一个创建 serviceMonitor 和 podMonitor 不免又麻烦了起来, 我们可以使用不限制 namespace 的 kubernetes_sd_configs 实现集群范围内自动发现所有的 exporter 实例

接下来的演示中我们监控集群范围内的所有 endpoints, 并且将带有 prometheus.io/scrape=true 这个 annotations 的 service 注册到 prometheus

rbac

由于需要访问访问集群范围内的资源对象, 继续使用 role+roleBinding 模式显然不适合, prometheus-k8s 这个 serviceAccount 还绑定一个名为 prometheus-k8s 的 clusterRole, 该 clusterRole 默认权限是不够的, 添加需要的权限:

# vim prometheus-clusterRole.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
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.47.2
  name: prometheus-k8s
rules:
- apiGroups:
  - ""
  resources:
  - nodes/metrics
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - services
  - endpoints
  - pods
  verbs:
  - list
  - watch
- nonResourceURLs:
  - /metrics
  verbs:
  - get

自动发现配置

参考文章详解Prometheus高效监控

# vim prometheus-additional.yaml

- job_name: 'kubernetes-pods'
  kubernetes_sd_configs:
  # 角色为 endpoints
    - role: endpoints
  relabel_configs:
  # 重新打标仅抓取到的具有 "prometheus.io/scrape: true" 的annotation的端点,意思是说如果某个service具有prometheus.io/scrape = true annotation声明则抓取
  # annotation本身也是键值结构,所以这里的源标签设置为键,而regex设置值,当值匹配到regex设定的内容时则执行keep动作也就是保留,其余则丢弃.
  # node-exporter这个POD的service里面就有一个叫做prometheus.io/scrape = true的annotations所以就找到了node-exporter这个POD
    - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
    # 动作 删除 regex 与串联不匹配的目标 source_labels
      action: keep
    # 通过正式表达式匹配 true
      regex: true
    - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
    # 匹配源标签__meta_kubernetes_service_annotation_prometheus_io_scheme也就是prometheus.io/scheme annotation
    # 如果源标签的值匹配到regex则把值替换为__scheme__对应的值
      action: replace
      target_label: __scheme__       # 重新设置scheme
      regex: (https?)
    - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
      # 匹配来自 pod annotationname prometheus.io/path 字段
      # 获取POD的 annotation 中定义的"prometheus.io/path: XXX"定义的值,这个值就是你的程序暴露符合prometheus规范的metrics的地址
      # 如果你的metrics的地址不是 /metrics 的话,通过这个标签说,那么这里就会把这个值赋值给 __metrics_path__这个变量,因为prometheus
   # 是通过这个变量获取路径然后进行拼接出来一个完整的URL,并通过这个URL来获取metrics值的,因为prometheus默认使用的就是 http(s)://X.X.X.X/metrics
   # 这样一个路径来获取的
      action: replace
      target_label: __metrics_path__
      regex: (.+)
    - source_labels:          # 匹配出 Pod ip地址和 Port
        [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
      action: replace
      target_label: __address__
      regex: ([^:]+)(?::\d+)?;(\d+) # RE2 正则规则,+是一次多多次,?是0次或1次,其中?:表示非匹配组(意思就是不获取匹配结果)
      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]
    # service 对象的名称
      action: replace
      target_label: kubernetes_name
    - source_labels: [__meta_kubernetes_pod_name]
    # pod对象的名称
      action: replace
      target_label: kubernetes_pod_name

通过 secret 挂载进容器中

kubectl create secret generic additional-scrape-configs -n monitoring --from-file=prometheus-additional.yaml 

修改 prometheus 文件添加了 additionalScrapeConfigs 配置

# kubectl explain Prometheus.spec.additionalScrapeConfigs
spec:
  additionalScrapeConfigs:
    name: additional-scrape-configs  # secret name
    key: prometheus-additional.yaml  # secret key

# 更新prometheus
$ kubectl apply -f prometheus-prometheus.yaml 

验证

创建一个示例应用

# vim node-exporter-deploy-svc.yml
apiVersion: v1
kind: Namespace
metadata:
  name: test
---
apiVersion: v1
kind: Service
metadata:
  name: test-node-exporter
  namespace: test
  labels:
    app: test-node-exporter
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "9200"
spec:
  selector:
    app: test-node-exporter
  ports:
  - name: metrics
    port: 9200
    targetPort: metrics
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-node-exporter
  namespace: test
  labels:
    app: test-node-exporter
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-node-exporter
  template:
    metadata:
      labels:
        app: test-node-exporter
    spec:
      containers:
      - args:
        - --web.listen-address=:9200
        image: prom/node-exporter:v1.6.1
        name: node-exporter
        ports:
        - name: metrics
          containerPort: 9200

# kubectl apply -f node-exporter-deploy-svc.yml

如下, 我们部署的 node-exporter 已经成功注册, 只要 service 设置了 annotations 即可,service coredns 默认有 prometheus.io/scrape=true 这个注解, 已经成功注册: