Kubernetes 集群监控:Prometheus + alertmanager+ grafana
现在开始重做K8S
集群监控,之前做的不完善,如果你不了解Prometheus/alertmanager/node_exporter
看本文会比较吃力,先来了解一下在k8s
中要监控哪些东西,一般是分为两个指标,一个是K8S
本身,在一个就是Pod
监控。
Kubernetes本身监控
- Node资源利用率
- Node数量
- pod数量
- 资源对象状态
Pod 监控
- Pod数量,指的是某个项目运行了多少个
pod
- 容器资源利用率
- 应用程序,类型自定义监控
- Pod数量,指的是某个项目运行了多少个
具体使用哪些东西去收集这些指标,如下。
指标 | 实现 | 举例 |
---|---|---|
Pod性能 | cAdvisor,kubelet内置 | 容器CPU,内存利用率 |
Node性能 | node-exporter | 节点CPU,内存利用率 |
K8S资源对象 | kube-state-metrics | Pod/Deployment/Service |
比较重要的一点,K8S
中所有资源都是动态的,拿Pod
来说,很久之前就提过Pod
是短暂的,你不可能在Prometheus
中去配置我要去监控哪个pod
,这个方法根本行不通的,不要想了,所以现在涉及到Prometheus
基于kubernetes
的服务发现,他这个是原生就支持的,他会在你kube-apiserver
中去发现要抓取的目标,之前也提过,点击这里深入了解,下面看一下要实施的监控方案。
使用cAdvisor/node_exporter+Prometheus+Grafana
来做,也是目前比较主流的方案,架构图如下,
cAdvisor
已经集成到kubelet
中了,所以我们不需要再借助外部插件对pod
进行监控了,我们只需要拿到他自身暴露出的指标就可以了,拿到指标之后Prometheus
进行分析存储,通过Grafana
进行展示,对node
进行监控还是用node_exports
。
还有一个k8s
常见的资源状态,例如deployment
运行的数量等等这些,你想要获取这些东西需要在apiserver
中获取,这些东西都是存储在etcd
数据库中,所以要通过一个名为kube-state-metrics
的组件来实现监控资源状态,这个组件作用就是在k8s
中获取到资源状态的监控指标给暴露出来,最后通过alertmanager
进行告警,其实Grafana
也可以告警,他的设置方式是对图表设置阈值,超了我就告警,比较麻烦,所以告警这块还是用alertmanager
吧,下面开始在K8S
集群中部署Prometheus
在K8S中部署Prometheus
这次部署主要是参考官方的部署方式,GitHub地址,这个链接的位置就是插件目录下,也就是这个位置,
YAML
文件比较多,我要用最新的,所以我在clone
一下,把文件复制到/root
[root@master-1 ~/k8s]# mkdir Prometheus && cd Prometheus/
[root@master-1 ~/k8s/Prometheus]# git clone https://github.com/kubernetes/kubernetes.git
[root@master-1 ~/k8s/Prometheus]# cp -r kubernetes-master/cluster/addons/prometheus /root/
[root@master-1 ~/prometheus]# cd /root/prometheus/
这些文件主要分为四部分,第一部分是prometheus
的部署文件,包含了configmap&rbac&service&statefulset
,共四个文件,第二部分是kube-state-metrics
,获取资源对象的部署文件,第三部分为node-exporter
部署文件,第四部分alertmanager
部署文件,这个是用来告警的,开始部署吧,先部署prometheus
。
首先需要部署rbac&configmap
[root@master-1 ~/prometheus]# kubectl apply -f prometheus-rbac.yaml
[root@master-1 ~/prometheus]# kubectl apply -f prometheus-configmap.yaml
部署prometheus
的方式为statefulset
,也就是有状态服务,这个也用到了PV
动态供给,它默认的模板里指定了storageClassName
类名为standard
,也就是这里定义的,
但是我这里莫得这个类哇,只有一个这个,
所以改成这个之后就可创建了,改这一处就够了,直接创建吧,
[root@master-1 ~/prometheus]# kubectl apply -f prometheus-statefulset.yaml
最后是Services
,这里加一个NodePort
,改完后直接创建了,
spec:
type: NodePort
ports:
- name: http
port: 9090
protocol: TCP
targetPort: 9090
nodePort: 39090
selector:
k8s-app: prometheus
[root@master-1 ~/prometheus]# kubectl get service -n kube-system
这样就创建完了,访问一下Node:39090
,看到这个页面就说明没啥子问题。
时间差的比较大忽略,一会再解决这个问题,现在进到容器里面去,看一下他的主配置文件,
基于K8S服务发现的配置解析
先看一下部署prometheus
的部署文件,他是启动了两个容器,其中一个是这个,
- name: prometheus-server-configmap-reload
image: "jimmidyson/configmap-reload:v0.1"
imagePullPolicy: "IfNotPresent"
这个容器的作用是这样,当他发现configmap
有更新的时候,会自动重载prometheus
,现在进入到prometheus-server
容器中解析一下他的配置文件,先是这一段,采集本机的我就不贴了,
[root@master-1 ~]# kubectl exec -it -n kube-system prometheus-0 -c prometheus-server sh
/prometheus $ cat /etc/config/prometheus.yml
- job_name: kubernetes-apiservers
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- action: keep
regex: default;kubernetes;https
source_labels:
- __meta_kubernetes_namespace
- __meta_kubernetes_service_name
- __meta_kubernetes_endpoint_port_name
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
job_name
为kubernetes-apiservers
,动态发现类型为kubernetes_sd_configs
,也就是基于kubernetes
的服务发现,这一段的配置采集的是这个,
下面又使用了重新标记的标签,使用了keep
,也就是只保留正则匹配的标签,源标签的值包含default;kubernetes;https
,我只采集这些,所以这一段的作用就是采集kube-apiserver
,使用的方法是https
,最后就是普罗米修斯访问apiserver
使用的CA
和token
,这是两个默认的,任何一个pod
都会有这两个文件,管理员权限,再下面,这一段,
- job_name: kubernetes-nodes-kubelet
kubernetes_sd_configs:
- role: node
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
他是采集了每个节点的10250/metrics
,这个10250
就是kubelet
集成的cAdvisor
单独暴露的端口,这个接口是采集kubelet
本身暴露出来的一些指标,他的角色为node
,访问的方式还是https
,和上面的方式是一样的,再往下看,
- job_name: kubernetes-service-endpoints
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- action: keep
regex: true
source_labels:
- __meta_kubernetes_service_annotation_prometheus_io_scrape
- action: replace
regex: (https?)
source_labels:
- __meta_kubernetes_service_annotation_prometheus_io_scheme
target_label: __scheme__
- action: replace
regex: (.+)
source_labels:
- __meta_kubernetes_service_annotation_prometheus_io_path
target_label: __metrics_path__
- action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
source_labels:
- __address__
- __meta_kubernetes_service_annotation_prometheus_io_port
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_service_label_(.+)
- action: replace
source_labels:
- __meta_kubernetes_namespace
target_label: kubernetes_namespace
- action: replace
source_labels:
- __meta_kubernetes_service_name
target_label: kubernetes_name
这里是用来发现endpoints
的,__
开头的都是prometheus
自有的,一般情况下不会直接使用,如果直接用的话需要重新打标签,看页面,目前只是采集到一个coredns
的endpoints
,我目前coredns
启动了两个,
为毛endpoints
这么多只是收集到了一个,原因就是relabel_configs
那里也做了一个keep
,也就是这一段
- action: keep
regex: true
source_labels:
- __meta_kubernetes_service_annotation_prometheus_io_scrape
这里判断了你元数据里面有没有prometheus_io_scrape
这个KEY
,这个prometheus_io_scrape
就定义了我要不要采集这个目标,所以需要在你创建的时候声明一下,如果prometheus_io_scrape
值为true
,我就采集他,如果没有任何声明,就默认不采集,所以说白了就是只保留目标源标签包含prometheus_io_scrape
的,这东西具体在哪配的,看一下coredns service
是怎么写的,主要内容,
[root@master-1 ~]# kubectl get service -n kube-system kube-dns -o yaml
所以你的endpoints
想被prometheus
抓取就需要加这个声明,是否被采集,采集的端口是什么,使用什么协议,都需要声明,再下面,
- job_name: kubernetes-services
kubernetes_sd_configs:
- role: service
metrics_path: /probe
params:
module:
- http_2xx
relabel_configs:
- action: keep
regex: true
source_labels:
- __meta_kubernetes_service_annotation_prometheus_io_probe
- source_labels:
- __address__
target_label: __param_target
- replacement: blackbox
target_label: __address__
- 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
这里是黑盒探测services
是否可用,这个之前没提过,他用了blackbox
组件,探测路径为/probe
,http
探测,下面的就不贴了,都是类似的东西了,prometheus
在采集你资源的时候用的值都是在被创建对象中动态获取的,也就是需要你去定义的,下面开始监控K8S
集群中的pod
监控集群中pod
开始监控资源了,最重要的指标就是pod
了,要想监控pod
很简单,上面提到了监控pod
需要使用cAdvisor
,而cAdvisor
已经集成到kubelet
中了,cAdvisor
会采集所有pod
的指标,包括宿主机的内存CPU
,cAdvisor
有两个暴露地址,分别是NodeIP:{10255,10250}/metrice/cadvisor
,这是在kubelet
配置文件中指定的,也就是这里,
port: 10250 #暴露kubelet自身指标端口
readOnlyPort: 10255 #单独cAdvisor端口
现在获取kubelet
指标的端口是这个,
建议使用10250
端口去获取指标,默认就是10250
端口,现在已经有数据了,需要展示数据了,所以部署一下Grafana
部署Grafana
直接在K8S
中部署吧,用StatefulSet
去部署了,文件如下,我直接跑了,
[root@master-1 ~/prometheus]# cat grafana.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: grafana
namespace: kube-system
spec:
serviceName: "grafana"
replicas: 1
selector:
matchLabels:
app: grafana
template:
metadata:
labels:
app: grafana
spec:
containers:
- name: grafana
image: grafana/grafana
livenessProbe:
httpGet:
path: /login
port: 3000
scheme: HTTP
initialDelaySeconds: 10
timeoutSeconds: 10
failureThreshold: 10
readinessProbe:
httpGet:
path: /login
port: 3000
scheme: HTTP
initialDelaySeconds: 10
timeoutSeconds: 10
failureThreshold: 10
ports:
- name: grafana
containerPort: 3000
protocol: TCP
resources:
limits:
cpu: 300m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
volumeMounts:
- name: grafana-data
mountPath: /var/lib/grafana
subPath: grafana
volumeClaimTemplates:
- metadata:
name: grafana-data
spec:
storageClassName: managed-nfs-storage
accessModes:
- ReadWriteOnce
resources:
requests:
storage: "1Gi"
---
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: kube-system
spec:
type: NodePort
ports:
- port : 80
name: grafana
targetPort: 3000
nodePort: 33000
selector:
app: grafana
[root@master-1 ~/prometheus]# kubectl apply -f grafana.yaml
statefulset.apps/grafana created
service/grafana created
创建好之后访问node
节点的33000
就可以了,还是常规操作,添加数据源,自行添加吧,然后导入一个仪表盘,集群资源监控模板建议使用3119
,先把这个导入进来吧,这些操作我就不贴图了,导入之后发现Cluster filesystem usage
没有值,我看了一下他的sql
,查的是设备名是xvda
,我的是sda
,所以把这个改一下就有数据了,下面两个操作方式一致,所以大概长这样,
这个新版的grafana
图形显示这里貌似有点问题,先不管了,下面开始监控集群Node
监控K8S集群node
也是使用node_exports
来收集指标,node_exports
这里就不用daemonsets
方式去部署了,如果使用daemonsets
方式部署问题比较多,可能会收集不到某些指标,所以我直接在每个node
上直接部署node_exports
了,部署方式和之前的部署方式一样,写个脚本,如下,
[root@master-1 ~/prometheus]# cat node-exporter.sh
#!/bin/bash
## 解压重命名
tar zxf /tmp/node_exporter-0.18.1.linux-amd64.tar.gz -C /usr/local/
mv /usr/local/node_exporter-0.18.1.linux-amd64 /usr/local/node_exporter
##添加系统服务
cat > /usr/lib/systemd/system/node_exporter.service <<OEF
[Unit]
Description=node_exporter
[Service]
Restart=on-failure
ExecStart=/usr/local/node_exporter/node_exporter --collector.systemd --collector.systemd.unit-whitelist=(docker|kubelet|kube-proxy|flanneld).service
[Install]
WantedBy=multi-user.target
OEF
##启动node_exporter
systemctl daemon-reload
systemctl enable node_exporter
systemctl restart node_exporter
我顺便监控了node
组件的状态,如果node_exporter
是跑在容器里的这个功能是没办法实现的,这个脚本很简单粗暴,由于软件包下载很慢,我把之前的拿过来了,直接用ansible
把软件包传到目标的/tmp
目录,剩下的就交给脚本去跑就够了,
[root@master-1 ~/prometheus]# ansible node -m copy -a "src=./node_exporter-0.18.1.linux-amd64.tar.gz dest=/tmp/"
[root@master-1 ~/prometheus]# ansible node -m script -a "./node-exporter.sh"
这样就行了,现在去改一下Configmap
吧,自动发现是没办法发现我的node
了,我把我的node
和master
分开了,master
顺便监控了master
组件的运行状态,还有需要添加一下数据采集的间隔时间,默认是一分钟,现在改成30s
,所以在Configmap
里面加了这些东西,
global:
scrape_interval: 30s
- job_name: k8s-nodes
static_configs:
- targets:
- 192.168.1.202:9100
- 192.168.1.203:9100
- 192.168.1.204:9100
- 192.168.1.165:9100
- 192.168.1.169:9100
- 192.168.1.172:9100
- job_name: k8s-master
static_configs:
- targets:
- 192.168.1.200:9100
- 192.168.1.201:9100
更新一下ConfigMap
,
[root@master-1 ~/prometheus]# kubectl apply -f prometheus-configmap.yaml
configmap/prometheus-config configured
这样就会自动重载prometheus
,还有一个问题要解决,那就是prometheus
时间的问题,差了八小时,查出来的数据也有问题,所以直接把宿主的localtime
文件挂到容器内就行了,所以statefulset
也要加点东西撒,
- name: localtime
mountPath: /etc/localtime
- name: localtime
hostPath:
path: /etc/localtime
更新一下,等重启完了看一下web
页面,能看到这些就对了,
然后在grafana
导入一个模板,ID
为9276
,稍等一会就有数据了,
这样就行了,下面开始监控K8S
资源对象
监控K8S资源对象
这里监控的就是K8S
创建资源对象的状态信息,譬如说service/deployment/replicaset/endpoints
等等,前面也提到了要监控这些东西需要用到kube-state-metrics
组件,这个组件就是专门采集K8S
中各种资源对象信息,比较全面,官方地址,下面开始部署吧,先RBAC
授权吧,
[root@master-1 ~/prometheus]# kubectl apply -f kube-state-metrics-rbac.yaml
然后开始部署deployment
,有个镜像的地址需要换一下撒,也就是这个,
image: k8s.gcr.io/addon-resizer:1.8.5
国内无法拉取的撒,所以地址换成这个吧,
image: bairuijie/addon-resizer:1.8.5
这样就可以了,直接部署,Services
也直接创建了,
[root@master-1 ~/prometheus]# kubectl apply -f kube-state-metrics-deployment.yaml
deployment.apps/kube-state-metrics created
configmap/kube-state-metrics-config created
[root@master-1 ~/prometheus]# kubectl apply -f kube-state-metrics-service.yaml
service/kube-state-metrics created
他会滚动启动两个副本,第二启动后第一个被莫名其妙的Terminating
了,不是很理解,总之最后就是启动成功了,在页面看一下,有没有采集到的数据,
已经有东西了,以kube_
开头的指标就是kube-state-metrics
采集的,再导入一个模板进来,ID
为6417
,仪表盘有很多,具体的看这里,我只是导入了自己感觉不错的,导入模板之后就是这样,
还是发现有没数据的,这些东西只能去自行调整了,这里面列出了很多东西,我看了一下下面的pod
情况,当前有22
个pod
正在运行,有一个pod
出问题了,页面显示是这样,
我用命令看了一下,有个pod
居然被驱逐了,我擦,
直接删了就好了,说明这里的数据是没啥子问题的,目前仪表盘里面只有一个deployment
,像是statefulset
什么的还没有图表,数据已经有了,所以你得手动去创建图表了,照葫芦画瓢吧,我尝试加了一下statefulset
,大概是这种效果,
我没找到statefulset
不可用的指标,就随便加了个指标,就是这种效果,复制然后改改里面的sql
就行了,开始准备做告警吧,还是用alertmanager
来做告警
在K8S中部署alertmanager
直接在K8S
中部署alertmanager
吧,Configmap
文件存的就是alertmanager
主配置文件,现在是默认的,暂时不改,一会再改,deployment
自行看一下吧,用了PVC
,这个存储类得改一下,改成你自己的存储类,如果你没有动态供给就改成别的方式吧,我这里已经改掉了,我直接部署了,
[root@master-1 ~/prometheus]# kubectl apply -f alertmanager-configmap.yaml
configmap/alertmanager-config created
[root@master-1 ~/prometheus]# kubectl apply -f alertmanager-pvc.yaml
persistentvolumeclaim/alertmanager created
[root@master-1 ~/prometheus]# kubectl apply -f alertmanager-deployment.yaml
deployment.apps/alertmanager created
[root@master-1 ~/prometheus]# kubectl apply -f alertmanager-service.yaml
service/alertmanager created
现在需要配置prometheus
与alertmanager
进行通讯,去编辑prometheus
的Configmap
吧,你会发现目前配置的是动态发现,直接改了吧,改成静态的,
alerting:
alertmanagers:
- static_configs:
- targets: ["alertmanager:80"]
[root@master-1 ~/prometheus]# kubectl apply -f prometheus-configmap.yaml
configmap/prometheus-config configured
这样就行了哇,看一下配置文件,已经生效了,
下面开始写告警规则吧,得先指定一个rules
目录,改Configmap
rule_files:
- /etc/config/rules/*.rules
改完之后应用Configmap
,然后还需要写configmap
,这个是来存储告警规则的,我直接把之前写的copy
过来了,顺便加了两条,存在了configmap
里,YAML
文件如下,
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-rules
namespace: kube-system
data:
collector.rules: |
groups:
- name: collector.rules
rules:
- alert: 采集器凉了
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} 采集器凉了撒"
description: " {{ $labels.instance }} 采集器凉了有一分钟了撒"
node.rules: |
groups:
- name: memeory_rules
rules:
- alert: 内存炸了
expr: (node_memory_MemTotal_bytes - node_memory_MemFree_bytes - node_memory_Buffers_bytes - node_memory_Cached_bytes) / (node_memory_MemTotal_bytes )* 100 > 80
for: 1m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} 内存炸了"
description: "{{ $labels.instance }} 内存炸了,当前使用率为 {{ $value }}"
- alert: CPU炸了
expr: 100 - (avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) * 100) > 80
for: 1m
labels:
severity: warning
annotations:
summary: "Instance {{ $labels.instance }} CPU炸了"
description: "{{ $labels.instance }}CPU炸了 当前使用率为 {{ $value }}"
- alert: 磁盘剩余空间过低
expr: 100 - (node_filesystem_free_bytes{fstype=~"ext4|xfs"} / node_filesystem_size_bytes{fstype=~"ext4|xfs"} * 100) > 80
for: 1m
labels:
severity: warning
annotations:
summary: "Instance {{ $labels.instance }} : {{ $labels.mountpoint }} 分区剩余空间较小"
description: "{{ $labels.instance }}: {{ $labels.mountpoint }} 分区剩余空间较小,当前使用率为 {{ $value }}"
我直接创建了,创建完了还不算完事,得把这个configmap
挂载到prometheus
的/etc/config/rules/
目录下,去改部署文件吧,
- name: prometheus-rules
mountPath: /etc/config/rules
- name: prometheus-rules
configMap:
name: prometheus-rules
重新应用正常启动后看这里能看到的撒,
莫得问题,接下来就是要去改alertmanager
的Configmap
了,也就是主配置文件,最后一步了,写了一个最简单的,如下,
apiVersion: v1
kind: ConfigMap
metadata:
name: alertmanager-config
namespace: kube-system
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: EnsureExists
data:
alertmanager.yml: |
global:
resolve_timeout: 5m
smtp_smarthost: 'smtp.163.com:25'
smtp_from: 'xxx@163.com'
smtp_auth_username: 'xxx@163.com'
smtp_auth_password: 'xxx'
smtp_require_tls: false
route:
group_by: ['alertname']
group_wait: 10s
group_interval: 10s
repeat_interval: 1m
receiver: 'email'
receivers:
- name: 'email'
email_configs:
- to: 'xxx@aliyun.com'
然后我让某个节点的CPU
炸一次,
收到的邮件,
就是这种效果撒,没什么问题,告警信息已经能正常发送出来了。
上面就算是将监控K8S
集群的流程过了一遍,其实现在监控指标已经很多了,接下来就是要去写告警规则画图表了,再就是自定义监控,当收集器收集的指标无法满足你需求的时候你就需要进行自定义监控了,自定义监控说白了就是需要你自己写收集器了,获取到你想要的东西把指标暴露出来,然后Prometheus
去收集,收集到指标之后写告警规则,这个可没有zabbix
自定义监控那么简单了,本篇就到这里了,也算是入门了。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。