Prometheus监控实践:Kubernetes集群监控
2017-12-29
本文将总结一下我们目前使用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的配置示例:
1- job_name: 'federate'
2 scrape_interval: 15s
3
4 honor_labels: true
5 metrics_path: '/federate'
6
7 params:
8 'match[]':
9 - '{job="prometheus"}'
10 - '{__name__=~"job:.*"}'
11
12 static_configs:
13 - targets:
14 - 'source-prometheus-1:9090'
15 - 'source-prometheus-2:9090'
16 - '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源码中库中的例子:
1apiVersion: rbac.authorization.k8s.io/v1
2kind: ClusterRole
3metadata:
4 name: prometheus
5rules:
6- apiGroups: [""]
7 resources:
8 - nodes
9 - nodes/proxy
10 - services
11 - endpoints
12 - pods
13 verbs: ["get", "list", "watch"]
14- apiGroups:
15 - extensions
16 resources:
17 - ingresses
18 verbs: ["get", "list", "watch"]
19- nonResourceURLs: ["/metrics"]
20 verbs: ["get"]
21---
22apiVersion: v1
23kind: ServiceAccount
24metadata:
25 name: prometheus
26 namespace: kube-system
27---
28apiVersion: rbac.authorization.k8s.io/v1
29kind: ClusterRoleBinding
30metadata:
31 name: prometheus
32roleRef:
33 apiGroup: rbac.authorization.k8s.io
34 kind: ClusterRole
35 name: prometheus
36subjects:
37- kind: ServiceAccount
38 name: prometheus
39 namespace: kube-system
prometheus.config.yml configmap中的prometheus的配置文件,参考Prometheus源码中库中的例子:
1apiVersion: v1
2kind: ConfigMap
3metadata:
4 name: prometheus-config
5 namespace: kube-system
6data:
7 prometheus.yml: |
8 global:
9 scrape_interval: 15s
10 evaluation_interval: 15s
11 scrape_configs:
12
13 - job_name: 'kubernetes-apiservers'
14 kubernetes_sd_configs:
15 - role: endpoints
16 scheme: https
17 tls_config:
18 ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
19 bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
20 relabel_configs:
21 - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
22 action: keep
23 regex: default;kubernetes;https
24
25 - job_name: 'kubernetes-nodes'
26 kubernetes_sd_configs:
27 - role: node
28 scheme: https
29 tls_config:
30 ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
31 bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
32 relabel_configs:
33 - action: labelmap
34 regex: __meta_kubernetes_node_label_(.+)
35 - target_label: __address__
36 replacement: kubernetes.default.svc:443
37 - source_labels: [__meta_kubernetes_node_name]
38 regex: (.+)
39 target_label: __metrics_path__
40 replacement: /api/v1/nodes/${1}/proxy/metrics
41
42 - job_name: 'kubernetes-cadvisor'
43 kubernetes_sd_configs:
44 - role: node
45 scheme: https
46 tls_config:
47 ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
48 bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
49 relabel_configs:
50 - action: labelmap
51 regex: __meta_kubernetes_node_label_(.+)
52 - target_label: __address__
53 replacement: kubernetes.default.svc:443
54 - source_labels: [__meta_kubernetes_node_name]
55 regex: (.+)
56 target_label: __metrics_path__
57 replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
58
59 - job_name: 'kubernetes-service-endpoints'
60 kubernetes_sd_configs:
61 - role: endpoints
62 relabel_configs:
63 - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
64 action: keep
65 regex: true
66 - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
67 action: replace
68 target_label: __scheme__
69 regex: (https?)
70 - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
71 action: replace
72 target_label: __metrics_path__
73 regex: (.+)
74 - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
75 action: replace
76 target_label: __address__
77 regex: ([^:]+)(?::\d+)?;(\d+)
78 replacement: $1:$2
79 - action: labelmap
80 regex: __meta_kubernetes_service_label_(.+)
81 - source_labels: [__meta_kubernetes_namespace]
82 action: replace
83 target_label: kubernetes_namespace
84 - source_labels: [__meta_kubernetes_service_name]
85 action: replace
86 target_label: kubernetes_name
87
88 - job_name: 'kubernetes-services'
89 kubernetes_sd_configs:
90 - role: service
91 metrics_path: /probe
92 params:
93 module: [http_2xx]
94 relabel_configs:
95 - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
96 action: keep
97 regex: true
98 - source_labels: [__address__]
99 target_label: __param_target
100 - target_label: __address__
101 replacement: blackbox-exporter.example.com:9115
102 - source_labels: [__param_target]
103 target_label: instance
104 - action: labelmap
105 regex: __meta_kubernetes_service_label_(.+)
106 - source_labels: [__meta_kubernetes_namespace]
107 target_label: kubernetes_namespace
108 - source_labels: [__meta_kubernetes_service_name]
109 target_label: kubernetes_name
110
111 - job_name: 'kubernetes-ingresses'
112 kubernetes_sd_configs:
113 - role: ingress
114 relabel_configs:
115 - source_labels: [__meta_kubernetes_ingress_annotation_prometheus_io_probe]
116 action: keep
117 regex: true
118 - source_labels: [__meta_kubernetes_ingress_scheme,__address__,__meta_kubernetes_ingress_path]
119 regex: (.+);(.+);(.+)
120 replacement: ${1}://${2}${3}
121 target_label: __param_target
122 - target_label: __address__
123 replacement: blackbox-exporter.example.com:9115
124 - source_labels: [__param_target]
125 target_label: instance
126 - action: labelmap
127 regex: __meta_kubernetes_ingress_label_(.+)
128 - source_labels: [__meta_kubernetes_namespace]
129 target_label: kubernetes_namespace
130 - source_labels: [__meta_kubernetes_ingress_name]
131 target_label: kubernetes_name
132
133 - job_name: 'kubernetes-pods'
134 kubernetes_sd_configs:
135 - role: pod
136 relabel_configs:
137 - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
138 action: keep
139 regex: true
140 - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
141 action: replace
142 target_label: __metrics_path__
143 regex: (.+)
144 - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
145 action: replace
146 regex: ([^:]+)(?::\d+)?;(\d+)
147 replacement: $1:$2
148 target_label: __address__
149 - action: labelmap
150 regex: __meta_kubernetes_pod_label_(.+)
151 - source_labels: [__meta_kubernetes_namespace]
152 action: replace
153 target_label: kubernetes_namespace
154 - source_labels: [__meta_kubernetes_pod_name]
155 action: replace
156 target_label: kubernetes_pod_name
prometheus.deploy.yml定义Prometheus的部署:
1---
2apiVersion: apps/v1beta2
3kind: Deployment
4metadata:
5 labels:
6 name: prometheus-deployment
7 name: prometheus
8 namespace: kube-system
9spec:
10 replicas: 1
11 selector:
12 matchLabels:
13 app: prometheus
14 template:
15 metadata:
16 labels:
17 app: prometheus
18 spec:
19 containers:
20 - image: harbor.frognew.com/prom/prometheus:2.0.0
21 name: prometheus
22 command:
23 - "/bin/prometheus"
24 args:
25 - "--config.file=/etc/prometheus/prometheus.yml"
26 - "--storage.tsdb.path=/prometheus"
27 - "--storage.tsdb.retention=24h"
28 ports:
29 - containerPort: 9090
30 protocol: TCP
31 volumeMounts:
32 - mountPath: "/prometheus"
33 name: data
34 - mountPath: "/etc/prometheus"
35 name: config-volume
36 resources:
37 requests:
38 cpu: 100m
39 memory: 100Mi
40 limits:
41 cpu: 500m
42 memory: 2500Mi
43 serviceAccountName: prometheus
44 imagePullSecrets:
45 - name: regsecret
46 volumes:
47 - name: data
48 emptyDir: {}
49 - name: config-volume
50 configMap:
51 name: prometheus-config
prometheus.svc.yml定义Prometheus的Servic,需要将Prometheus以NodePort, LoadBalancer或使用Ingress暴露到集群外部,这样外部的Prometheus才能访问它:
1---
2kind: Service
3apiVersion: v1
4metadata:
5 labels:
6 app: prometheus
7 name: prometheus
8 namespace: kube-system
9spec:
10 type: NodePort
11 ports:
12 - port: 9090
13 targetPort: 9090
14 nodePort: 30003
15 selector:
16 app: prometheus
2.3 配置Prometheus Federation #
完成Kubernetes集群上的Prometheus的部署之后,下面将配置集群外部的Prometheus使其从集群内部的Prometheus拉取数据。 实际上只需以静态配置的形式添加一个job就可以:
1- job_name: 'federate'
2 scrape_interval: 15s
3 honor_labels: true
4 metrics_path: '/federate'
5 params:
6 'match[]':
7 - '{job=~"kubernetes-.*"}'
8 static_configs:
9 - targets:
10 - '<nodeip>:30003'
注意上面的配置是外部Prometheus拉取k8s集群上面所有名称以kubernetes-
的job的监控数据。
2.4 Kubernetes集群Grafana Dashboard #
监控Dashboard使用Kubernetes cluster monitoring (via Prometheus)这个即可。 另外关于Pod和Deployment还有这两个Dashboard:Kubernetes Pod Metrics和Kubernetes Deployment metrics。
2.5 Kubernetes集群告警规则 #
可以对apiserver和kubelet两个关键组件的存活状态进行监控,规则如下:
1up{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-metrics
Service的定义包含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
- 集群中存在非Bound状态的PVC:
kube_persistentvolumeclaim_status_phase{phase=~"Lost|Pending"}==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的告警的。