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 数据
接下来就来为大家演示如何添加 etcd
、traefik
、kube-controller-manager
、kube-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.matchLabels
在 kube-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
这个注解, 已经成功注册: