Prometheus Operator监控k8s集群

之前的文章中,使用传统方式安装了prometheus来监控node、mysql、redis等。这样配置非常麻烦,成本非常高。如果还要考虑Prometheus、AlertManager这些组件服务本身的高可用的话,成本就更高了。当然了,我们完全可以用自定义的方式来实现这些需求,我们也知道Prometheus在代码上就已经对Kubernetes有了原生的支持,可以通过服务发现的形式来自动监控集群,因此我们可以使用另外一种更加高级的方式来部署Prometheus:Operator框架。

一、什么是Operator

Operator是由CoreOS公司开发的用来扩展Kubernetes API的特定应用程序控制器,用来创建、配置和管理复杂的有状态应用,例如数据库、缓存和监控系统。Operator基于kubernetes的资源和控制器概念上构建,但同时又包含了应用程序特定的领域知识。创建Operator的关键是CRD(自定义资源)的设计。

Operator是将运维人员对软件操作的只是给代码化,同时利用Kubernetes强大的抽象来管理大规模的软件应用。目前CoreOS官方提供了几种Operator的实现,其中就包括了Prometheus Operator,Operator的核心实现是基于Kubernetes的一下两个概念:

  • 资源:对象的状态定义
  • 控制器:观测、分析和行动,以调节资源的分布

当前CoreOS提供了四种Operator:

  • etcd:创建etcd集群
  • Rook:云原生环境下的文件、块、对象存储服务
  • Prometheus:创建Prometheus监控实例
  • Tectonic:部署Kubernetes集群

Prometheus作为一个核心的控制器,它会创建Prometheus、ServiceMonitor、AlertManager以及我们的prometheus-rule这四个资源对象,operator会一直监控并维持这四个资源对象的状态,其中创建Prometheus资源对象就是最为Prometheus Server进行监控,而ServiceMonitor就是我们用的exporter的各种抽象(exporter就是提供我们各种服务的metrics的工具)。Prometheus就是通过ServiceMonitor提供的metrics数据接口把我们数据pull过来的。现在我们监控prometheus不需要每个服务单独创建修改规则。通过直接管理Operator来进行集群的监控。这里还要说一下,一个ServiceMonitor可以通过我们的label标签去匹配集群内部的service,而我们的prometheus也可以通过label匹配多个ServiceMonitor。
在这里插入图片描述
其中Operator是核心部分,作为一个控制器而存在,Operator会其创建Prometheus、ServiceMonitor、AlertManager和PrometheusRule这4个CRD资源对象,然后一直监控并维持这4个CRD资源对象的状态。

  • Prometheus资源对象是作为Prometheus Service存在的
  • ServiceMonitor资源对象是专门的提供metrics数据接口的exporter的抽象,Prometheus就是通过ServiceMonitor提供的metrics数据接口去pull数据的
  • AlertManager资源对应alertmanager组件
  • PrometheusRule资源对象是被Prometheus实例使用的告警规则文件
CRD简介
全称CustomResourceDefinition,在Kubernetes中一切都可视为资源,在Kubernetes1.7之后增加对CRD自定义资源二次开发能力扩展Kubernetes API,当我们创建一个新的CRD时,Kubernetes API服务将为你制定的每个版本创建一个新的RESTful资源路径,我们可以根据该API路径来创建一些我们自己定义的类型资源。CRD可以是命名空间,也可以是集群范围。由CRD的作用于scpoe字段中所制定的,与现有的内置对象一样,删除命名空间将删除该命名空间下的所有自定义对象。

简单来说CRD是对Kubernetes API的扩展,kubernetes中的每个资源都是一个API对象的集合,例如yaml文件中定义spec那样,都是对Kubernetes中资源对象的定义,所有的自定义资源可以跟Kubernetes中内建的资源一样使用kubectl。

这样,在集群中监控数据,就变成Kubernetes直接去监控资源对象,Service和ServiceMonitor都是Kubernetes的资源对象,一个ServiceMonitor可以通过labelSelector匹配一类Service,Prometheus也可以通过labelSelector匹配多个ServiceMonitor,并且Prometheus和AlertManager都是自动感知监控告警配置的变化,不需要人为进行reload操作。

二、安装Prometheus Operator

Operator是原生支持Prometheus的,可以通过服务发现来监控集群,并且是通用安装。也就是operator提供的yaml文件,基本上在Prometheus是可以直接使用的,需要改动的地方比较少。

这里直接通过Prometheus-Operator的源码进行安装,当然也可以用Helm来进行一键安装。采用源码安装可以去了解更多的实现细节。

在安装之前,先把现在的环境说明一下

服务器IP系统主机名组件
192.168.0.71CentOS7.6k8s-01Kubernetes 1.16.6,Docker 18.09.6,Etcd 3.3.20,Flanneld 0.11.0,kube-apiserver,kube-controller-manager,kube-scheduler,kubelet,kube-proxy,nginx-1.15.3
192.168.0.72CentOS7.6k8s-02同上
192.168.0.73CentOS7.6k8s-03同上
192.168.0.74CentOS7.6nfs-server安装nfs服务用来持久化存储prometheus和grafana数据

如上表格,我已经用一键部署脚本部署好了k8s集群,并安装好了coredns插件dashboard插件

首先将Prometheus-Operator的源码克隆下来。这里有个坑要提前说一下:由于墙的原因,原本是要把Registry的地址换成微软中国的地址的,但是发现貌似从2020年4月份开始就用不了了,网上也是有很多的网友反映,暂时未能找到可以替代的镜像仓库地址。我这里已经把相关的镜像上传到了阿里云镜像仓库中。为了保证所需镜像的版本保持不变,我已经将https://github.com/coreos/kube-prometheus.git fork到了自己的github中,地址为https://github.com/wangchaoforever/kube-prometheus.git。

$ cd /opt/k8s/work
$ git clone https://github.com/wangchaoforever/kube-prometheus.git

##注意:下面的镜像要在所有的k8s集群主机中都拉取和改tag
# 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/wc181/alertmanager:v0.20.0
docker pull registry.cn-hangzhou.aliyuncs.com/wc181/grafana:6.6.0
docker pull registry.cn-hangzhou.aliyuncs.com/wc181/kube-state-metrics:v1.9.5
docker pull registry.cn-hangzhou.aliyuncs.com/wc181/kube-rbac-proxy:v0.4.1
docker pull registry.cn-hangzhou.aliyuncs.com/wc181/node-exporter:v0.18.1
docker pull registry.cn-hangzhou.aliyuncs.com/wc181/k8s-prometheus-adapter-amd64:v0.5.0
docker pull registry.cn-hangzhou.aliyuncs.com/wc181/prometheus:v2.15.2
docker pull registry.cn-hangzhou.aliyuncs.com/wc181/prometheus-operator:v0.38.1
docker pull registry.cn-hangzhou.aliyuncs.com/wc181/configmap-reload:v0.3.0
docker pull registry.cn-hangzhou.aliyuncs.com/wc181/prometheus-config-reloader:v0.38.1
docker pull registry.cn-hangzhou.aliyuncs.com/wc181/pause:3.1

# 修改tag
docker tag registry.cn-hangzhou.aliyuncs.com/wc181/alertmanager:v0.20.0 quay.io/prometheus/alertmanager:v0.20.0
docker tag registry.cn-hangzhou.aliyuncs.com/wc181/grafana:6.6.0 grafana/grafana:6.6.0
docker tag registry.cn-hangzhou.aliyuncs.com/wc181/kube-state-metrics:v1.9.5 quay.io/coreos/kube-state-metrics:v1.9.5
docker tag registry.cn-hangzhou.aliyuncs.com/wc181/kube-rbac-proxy:v0.4.1 quay.io/coreos/kube-rbac-proxy:v0.4.1
docker tag registry.cn-hangzhou.aliyuncs.com/wc181/node-exporter:v0.18.1 quay.io/prometheus/node-exporter:v0.18.1
docker tag registry.cn-hangzhou.aliyuncs.com/wc181/k8s-prometheus-adapter-amd64:v0.5.0 quay.io/coreos/k8s-prometheus-adapter-amd64:v0.5.0
docker tag registry.cn-hangzhou.aliyuncs.com/wc181/prometheus:v2.15.2 quay.io/prometheus/prometheus:v2.15.2
docker tag registry.cn-hangzhou.aliyuncs.com/wc181/prometheus-operator:v0.38.1 quay.io/coreos/prometheus-operator:v0.38.1
docker tag registry.cn-hangzhou.aliyuncs.com/wc181/configmap-reload:v0.3.0 jimmidyson/configmap-reload:v0.3.0
docker tag registry.cn-hangzhou.aliyuncs.com/wc181/prometheus-config-reloader:v0.38.1 quay.io/coreos/prometheus-config-reloader:v0.38.1
docker tag registry.cn-hangzhou.aliyuncs.com/wc181/pause:3.1 k8s.gcr.io/pause:3.1

# 删除多余镜像
docker rmi -f registry.cn-hangzhou.aliyuncs.com/wc181/alertmanager:v0.20.0
docker rmi -f registry.cn-hangzhou.aliyuncs.com/wc181/grafana:6.6.0
docker rmi -f registry.cn-hangzhou.aliyuncs.com/wc181/kube-state-metrics:v1.9.5
docker rmi -f registry.cn-hangzhou.aliyuncs.com/wc181/kube-rbac-proxy:v0.4.1
docker rmi -f registry.cn-hangzhou.aliyuncs.com/wc181/node-exporter:v0.18.1
docker rmi -f registry.cn-hangzhou.aliyuncs.com/wc181/k8s-prometheus-adapter-amd64:v0.5.0
docker rmi -f registry.cn-hangzhou.aliyuncs.com/wc181/prometheus:v2.15.2
docker rmi -f registry.cn-hangzhou.aliyuncs.com/wc181/prometheus-operator:v0.38.1
docker rmi -f registry.cn-hangzhou.aliyuncs.com/wc181/configmap-reload:v0.3.0
docker rmi -f registry.cn-hangzhou.aliyuncs.com/wc181/prometheus-config-reloader:v0.38.1
docker rmi -f registry.cn-hangzhou.aliyuncs.com/wc181/pause:3.1

$ cd kube-prometheus/

# 安装 prometheus-operator
$ kubectl apply -f manifests/setup 

# 安装 promethes metric adapter
$ kubectl apply -f manifests/ 

部署完成之后,会创建一个名为monitoring的namespace,所有资源对象将部署在该命名空间下。此外Operator会自动创建6个CRD资源对象:

[root@k8s-01 work]# kubectl get crd |grep coreos
alertmanagers.monitoring.coreos.com     2020-08-27T08:36:24Z
podmonitors.monitoring.coreos.com       2020-08-27T08:36:24Z
prometheuses.monitoring.coreos.com      2020-08-27T08:36:25Z
prometheusrules.monitoring.coreos.com   2020-08-27T08:36:29Z
servicemonitors.monitoring.coreos.com   2020-08-27T08:36:29Z
thanosrulers.monitoring.coreos.com      2020-08-27T08:36:31Z

可以在monitoring命名空间下查看所有的Pod,其中alertmanager和prometheus是用StatefulSet 控制器管理的,还有一个比较核心的prometheus-operator的Pod,用来控制其他资源对象和监听对象变化的:

[root@k8s-01 kube-prometheus]# kubectl get pods -n monitoring
NAME                                   READY   STATUS    RESTARTS   AGE
alertmanager-main-0                    2/2     Running   0          3m17s
alertmanager-main-1                    2/2     Running   0          3m16s
alertmanager-main-2                    2/2     Running   0          3m15s
grafana-86b55cb79f-htjzl               1/1     Running   0          3m41s
kube-state-metrics-dbb85dfd5-zd2mx     3/3     Running   0          3m41s
node-exporter-4rb6w                    2/2     Running   0          3m40s
node-exporter-fss8l                    2/2     Running   0          3m40s
node-exporter-pmlgw                    2/2     Running   0          3m40s
prometheus-adapter-5cd5798d96-2qpb7    1/1     Running   0          3m40s
prometheus-k8s-0                       3/3     Running   1          2m58s
prometheus-k8s-1                       3/3     Running   1          2m58s
prometheus-operator-5cfbdc9b67-tvzct   2/2     Running   0          3m58s

查看创建的Service:

[root@k8s-01 work]# kubectl get pods -n monitoring
NAME                                   READY   STATUS    RESTARTS   AGE
alertmanager-main-0                    2/2     Running   0          39m
alertmanager-main-1                    2/2     Running   0          39m
alertmanager-main-2                    2/2     Running   0          39m
grafana-86b55cb79f-htjzl               1/1     Running   0          39m
kube-state-metrics-dbb85dfd5-zd2mx     3/3     Running   0          39m
node-exporter-4rb6w                    2/2     Running   0          39m
node-exporter-fss8l                    2/2     Running   0          39m
node-exporter-pmlgw                    2/2     Running   0          39m
prometheus-adapter-5cd5798d96-2qpb7    1/1     Running   0          39m
prometheus-k8s-0                       3/3     Running   1          38m
prometheus-k8s-1                       3/3     Running   1          38m
prometheus-operator-5cfbdc9b67-tvzct   2/2     Running   0          39m

可以看到上面针对grafana和prometheus都创建了一个类型为ClusterIP的Service,当然如果我们想再外网访问这两个服务的话,可以通过创建对应的Ingress对象或者使用NodePort类型的Service,我这里为了方便,直接使用NodePort类型的服务即可。编辑grafana和prometheus-k8s这两个Service,将服务类型更改为NodePort:

[root@k8s-01 work]# kubectl edit svc grafana -n monitoring
...
  type: NodePort
...
[root@k8s-01 work]# kubectl edit svc prometheus-k8s -n monitoring
...
  type: NodePort
...
[root@k8s-01 work]# kubectl get svc -n monitoring
NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
alertmanager-main       ClusterIP   10.254.245.0     <none>        9093/TCP                     49m
alertmanager-operated   ClusterIP   None             <none>        9093/TCP,9094/TCP,9094/UDP   49m
grafana                 NodePort    10.254.111.205   <none>        3000:31019/TCP               49m
kube-state-metrics      ClusterIP   None             <none>        8443/TCP,9443/TCP            49m
node-exporter           ClusterIP   None             <none>        9100/TCP                     49m
prometheus-adapter      ClusterIP   10.254.35.129    <none>        443/TCP                      49m
prometheus-k8s          NodePort    10.254.22.213    <none>        9090:32159/TCP               49m
prometheus-operated     ClusterIP   None             <none>        9090/TCP                     48m
prometheus-operator     ClusterIP   None             <none>        8443/TCP                     49m

修改完成后,就可以去访问prometheus和grafana这两个服务了。这里先查看下prometheus的targets页面:

会发现这时候的monitoring/kubelet/0monitoring/kubelet/1都是红色的,如下图:
在这里插入图片描述
查看kubelet的日志,有报错:TLS handshake error from 192.168.0.71:45994: no serving certificate available for the kubelet

这是因为基于安全性考虑,CSR approving controllers 不会自动 approve kubelet server 证书签名请求,需要手动 approve。

解决方法:

我们手动approve一下CSR即可。

kubectl get csr | grep Pending | awk '{print $1}' | xargs kubectl certificate approve 

然后再去查看prometheus的targets页面,能成功监控上kubelet了。
在这里插入图片描述
除此之外,在prometheus的targets页面中,大部分配置都是正常的,但是还有kube-controller-managerkube-scheduler这两个系统组件没有监控到,这是由于我这里是二进制安装,所以没有获取到相关的信息
在这里插入图片描述
这是由于serverMonitor是根据label去选取svc的,我们可以看下对应的serviceMonitor选取的范围是kube-system

[root@k8s-01 manifests]# grep -2 selector prometheus-serviceMonitorKube*
prometheus-serviceMonitorKubeControllerManager.yaml-    matchNames:
prometheus-serviceMonitorKubeControllerManager.yaml-    - kube-system
prometheus-serviceMonitorKubeControllerManager.yaml:  selector:
prometheus-serviceMonitorKubeControllerManager.yaml-    matchLabels:
prometheus-serviceMonitorKubeControllerManager.yaml-      k8s-app: kube-controller-manager
--
prometheus-serviceMonitorKubelet.yaml-    matchNames:
prometheus-serviceMonitorKubelet.yaml-    - kube-system
prometheus-serviceMonitorKubelet.yaml:  selector:
prometheus-serviceMonitorKubelet.yaml-    matchLabels:
prometheus-serviceMonitorKubelet.yaml-      k8s-app: kubelet
--
prometheus-serviceMonitorKubeScheduler.yaml-    matchNames:
prometheus-serviceMonitorKubeScheduler.yaml-    - kube-system
prometheus-serviceMonitorKubeScheduler.yaml:  selector:
prometheus-serviceMonitorKubeScheduler.yaml-    matchLabels:
prometheus-serviceMonitorKubeScheduler.yaml-      k8s-app: kube-scheduler

但是在kube-system命名空间中没有符合标签的label

[root@k8s-01 manifests]# kubectl get svc -n kube-system --show-labels
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                        AGE   LABELS
kube-dns   ClusterIP   10.254.0.2   <none>        53/UDP,53/TCP,9153/TCP         21h   k8s-app=kube-dns,kubernetes.io/cluster-service=true,kubernetes.io/name=CoreDNS
kubelet    ClusterIP   None         <none>        10250/TCP,10255/TCP,4194/TCP   20h   k8s-app=kubelet

但是却有endpoint(我这里二进制安装的有)

[root@k8s-01 xinjia]# kubectl get ep -n kube-system --show-labels
NAME                      ENDPOINTS                                                              AGE   LABELS
kube-controller-manager   <none>                                                                 22h   k8s-app=kube-controller-manager,service.kubernetes.io/headless=
kube-dns                  172.30.56.2:53,172.30.56.2:53,172.30.56.2:9153                         22h   k8s-app=kube-dns,kubernetes.io/cluster-service=true,kubernetes.io/name=CoreDNS
kube-scheduler            <none>                                                                 22h   k8s-app=kube-scheduler,service.kubernetes.io/headless=
kubelet                   192.168.0.71:10255,192.168.0.72:10255,192.168.0.73:10255 + 6 more...   21h   k8s-app=kubelet

解决方法:

这里创建两个管理组件的svc,将svc的label设置为k8s-app: {kube-controller-manager、kube-scheduler},这样就可以被serviceMonitor选中了。

创建一个svc用来绑定

[root@k8s-01 manifests]# vim controllerandscheduler-svc.yaml
apiVersion: v1
kind: Service
metadata:
  namespace: kube-system
  name: kube-controller-manager
  labels:
    k8s-app: kube-controller-manager
spec:
  selector:
    component: kube-controller-manager
  type: ClusterIP
  clusterIP: None
  ports:
  - name: http-metrics
    port: 10252
    targetPort: 10252
    protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  namespace: kube-system
  name: kube-scheduler
  labels:
    k8s-app: kube-scheduler
spec:
  selector:
    component: kube-scheduler
  type: ClusterIP
  clusterIP: None
  ports:
  - name: http-metrics
    port: 10251
    targetPort: 10251
    protocol: TCP

创建这两个svc

[root@k8s-01 manifests]# kubectl create -f controllerandscheduler-svc.yaml
service/kube-controller-manager created
service/kube-scheduler created

手动编辑svc对应的ep的属性,ep的名称要和svc名称和属性对应上

[root@k8s-01 xinjia]# kubectl edit ep kube-controller-manager -n kube-system
##在末尾加上下面内容
subsets:
- addresses:
  - ip: 192.168.0.71
  - ip: 192.168.0.72
  - ip: 192.168.0.73
  ports:
  - name: http-metrics
    port: 10252
    protocol: TCP

[root@k8s-01 xinjia]# kubectl edit ep kube-scheduler -n kube-system
##在末尾加上下面内容
subsets:
- addresses:
  - ip: 192.168.0.71
  - ip: 192.168.0.72
  - ip: 192.168.0.73
  ports:
  - name: http-metrics
    port: 10251
    protocol: TCP

查看一下svc,已经和ep绑定好了

[root@k8s-01 xinjia]# kubectl get svc -n kube-system
NAME                      TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                        AGE
kube-controller-manager   ClusterIP   None         <none>        10252/TCP                      30m
kube-dns                  ClusterIP   10.254.0.2   <none>        53/UDP,53/TCP,9153/TCP         22h
kube-scheduler            ClusterIP   None         <none>        10251/TCP                      30m
kubelet                   ClusterIP   None         <none>        10250/TCP,10255/TCP,4194/TCP   21h
[root@k8s-01 xinjia]# kubectl describe svc -n kube-system kube-scheduler
Name:              kube-scheduler
Namespace:         kube-system
Labels:            k8s-app=kube-scheduler
Annotations:       <none>
Selector:          component=kube-scheduler
Type:              ClusterIP
IP:                None
Port:              http-metrics  10251/TCP
TargetPort:        10251/TCP
Endpoints:         192.168.0.71:10251,192.168.0.72:10251,192.168.0.73:10251
Session Affinity:  None
Events:            <none>

OK,这个时候再去看一眼prometheus的targets页面:
在这里插入图片描述
可以看到kube-scheduler没问题了,但是kube-controller-manager显示是红色的。查看kube-controller-manager的日志,有报错server returned HTTP status 400 Bad Request,且kube-controller-manager日志报TLS handshake error from 192.168.0.71:43604: remote error: tls: unknown certificate authority

解决方法,参考了https://www.cnblogs.com/yuhaohao/p/10197315.html,具体方法为:

修改kube-controller-manager服务的Service文件:

[root@k8s-01 system]# vim /etc/systemd/system/kube-controller-manager.service
###删除下面两行
--port=0
--secure-port=10252

#重启kube-controller-manager服务
systemctl daemon-reload
systemctl restart kube-controller-manager

注意:这里有个问题,kube-controller-manager重启之后,上面自改的两个endpoint会失效,需要再修改一遍,暂时未找到解决的办法,等找到之后再回来更新。

这时候可以看到所有的targets都正常了:
在这里插入图片描述
上面的监控数据配置完成后,现在可以去查看下grafana中的dashboard,同样是使用上面的NodePort访问即可,首次登陆使用admin/admin登陆即可,进入首页之后,发现已经和Prometheus数据源关联上了,可以看到监控图表了:
在这里插入图片描述

三、自定义监控(Etcd)

除了Kubernetes集群中的一些资源对象、节点以及组件需要监控,有的时候我们可能还需要根据实际的业务需求去添加自定义的监控项,添加一个自定义监控的步骤如下:

  • 创建一个ServiceMonitor对象,用于Prometheus添加监控项
  • 为ServiceMonitor对象关联metrics数据接口的一个Service对象
  • 确保Service对象可以正确获取到metrics数据

获取ETCD证书

对于etcd集群,在搭建的时候就采用了https证书认证的方式,所以这里如果想用Prometheus访问到etcd集群的监控数据,就需要添加证书

可以通过查看etcd的service文件来查看证书路径

[root@k8s-01 system]# cat /etc/systemd/system/etcd.service 

在这里插入图片描述

接着需要创建一个secret,让prometheus pod节点挂载

kubectl create secret generic etcd-ssl --from-file=/etc/kubernetes/cert/ca.pem --from-file=/etc/etcd/cert/etcd.pem --from-file=/etc/etcd/cert/etcd-key.pem -n monitoring

创建完成后检查一下:

[root@k8s-01 system]# kubectl describe secrets -n monitoring etcd-ssl
Name:         etcd-ssl
Namespace:    monitoring
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
ca.pem:        1322 bytes
etcd-key.pem:  1679 bytes
etcd.pem:      1444 bytes

将etcd-ssl secret对象配置到prometheus资源对象中去:

#修改prometheus-prometheus.yaml 
[root@k8s-01 manifests]# vim prometheus-prometheus.yaml
  nodeSelector:
    kubernetes.io/os: linux
  podMonitorNamespaceSelector: {}
  podMonitorSelector: {}
  replicas: 2
  secrets:
  - etcd-ssl #添加secret名称
  
#更新使生效
[root@k8s-01 manifests]# kubectl apply -f prometheus-prometheus.yaml 
prometheus.monitoring.coreos.com/k8s configured

更新完之后,就可以在Prometheus Pod中查看到对象的目录

[root@k8s-01 manifests]# kubectl exec -it -n monitoring prometheus-k8s-0 /bin/sh 
Defaulting container name to prometheus.
Use 'kubectl describe pod/prometheus-k8s-0 -n monitoring' to see all of the containers in this pod.
/prometheus $ ls /etc/prometheus/secrets/etcd-ssl/
ca.pem        etcd-key.pem  etcd.pem

创建ServiceMonitor

目前prometheus已经挂载了etcd的证书文件,接下来需要创建ServiceMonitor

[root@k8s-01 xinjia]# vim etcd-servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: etcd-k8s
  namespace: monitoring
  labels:
    k8s-app: etcd-k8s
spec:
  jobLabel: k8s-app
  endpoints:
  - port: port
    interval: 30s
    scheme: https
    tlsConfig:
      caFile: /etc/prometheus/secrets/etcd-ssl/ca.pem    #证书路径 (在prometheus pod里路径)
      certFile: /etc/prometheus/secrets/etcd-ssl/etcd.pem
      keyFile: /etc/prometheus/secrets/etcd-ssl/etcd-key.pem
      insecureSkipVerify: true
  selector:
    matchLabels:
      k8s-app: etcd
  namespaceSelector:
    matchNames:
    - kube-system

匹配kube-system这个命令空间下面具有k8s-app=etcd这个label标签的Service,job label用于检索job任务名称的标签。由于证书serverName和etcd中签发的证书可能不匹配,所以添加了insecureSkipVerify=true将不再对服务端的证书进行校验。

接下来,直接创建这个ServiceMonitor

[root@k8s-01 xinjia]# kubectl apply -f etcd-servicemonitor.yaml
servicemonitor.monitoring.coreos.com/etcd-k8s created

###查看servicemonitor
[root@k8s-01 xinjia]# kubectl get servicemonitors -n monitoring |grep etcd
etcd-k8s                  41s

创建Service

ServiceMonitor创建完成,但是还没有关联对应的Service对象,所以需要创建一个service对象

[root@k8s-01 xinjia]# vim etcd-prometheus-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: etcd-k8s
  namespace: kube-system
  labels:
    k8s-app: etcd
spec:
  type: ClusterIP
  clusterIP: None
  ports:
  - name: port
    port: 2379
    protocol: TCP

---
apiVersion: v1
kind: Endpoints
metadata:
  name: etcd-k8s
  namespace: kube-system
  labels:
    k8s-app: etcd
subsets:
- addresses:
  - ip: 192.168.0.71     #etcd节点名称
    nodeName: k8s-01     #kubelet名称 (kubectl get node)显示的名称
  - ip: 192.168.0.72
    nodeName: k8s-02
  - ip: 192.168.0.73
    nodeName: k8s-03
  ports:
  - name: port
    port: 2379
    protocol: TCP

直接创建:

[root@k8s-01 xinjia]# kubectl apply -f etcd-prometheus-svc.yaml
service/etcd-k8s created
endpoints/etcd-k8s created

###查看结果
[root@k8s-01 xinjia]# kubectl get svc -n kube-system | grep etcd
etcd-k8s                  ClusterIP   None         <none>        2379/TCP                       69s

不放心的话,还可以查看下service是否绑定到对象的ip上了:

[root@k8s-01 xinjia]# kubectl describe svc etcd-k8s -n kube-system
Name:              etcd-k8s
Namespace:         kube-system
Labels:            k8s-app=etcd
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"k8s-app":"etcd"},"name":"etcd-k8s","namespace":"kube-system"},...
Selector:          <none>
Type:              ClusterIP
IP:                None
Port:              port  2379/TCP
TargetPort:        2379/TCP
Endpoints:         192.168.0.71:2379,192.168.0.72:2379,192.168.0.73:2379
Session Affinity:  None
Events:            <none>

稍等一会,去prometheus targets查看,就会看到etcd的监控信息了
在这里插入图片描述

四、监控K8S集群中的哪些指标和相关的方法

1、监控指标

上面prometheus的targets页面中都是些K8S集群本身的服务组件(如)和一些监控

我个人把上面prometheus的target页面中的这些服务分为三类:

  • 第一类是K8S集群本身的组件:如kube-controller-manager、etcd等。
  • 第二类是所要使用的第三方服务:如prometheus和grafana
  • 第三类是为prometheus收集监控信息而部署的服务(即作为prometheus的客户端):如node-exporter和kube-state-metrics等。

分完 这三类之后,我们再来看看在K8S集群中,我们所要监控的对象。

Pod监控

之前在安装kubelet的时候有讲过,cadvisor 是内嵌在 kubelet 二进制中的,统计所在节点各容器的资源(CPU、内存、磁盘、网卡)使用情况的服务。
kubelet的节点使用cAdvisor提供的metrics接口获取该节点所有Pod和容器相关的性能指标数据。也就是kubelet会暴露两个接口地址:
https://NodeIP:10250/metrics和https://NodeIP:10250/metrics/cadvisor分别返回 kubelet 和 cadvisor 的 metrics。需要注意的是,用浏览器直接访问是不行的,其中还设计到一些授权的问题,具体可以移步到之前安装kubelet的文章。

Node监控

Prometheus Operator以DaemonSet的形式在k8s集群的每个node节点上都部署一个node-exporter来采集各个node的资源利用率信息。

资源对象监控

Prometheus Operator部署了一个kube-state-metrics来采集k8s中各种资源对象的状态信息。

2、使用Grafana可视化展示Prometheus监控数据

没错,其实上面已经展示过了,因为Prometheus Operator部署完成之后的grafana中自带了模板。

但是我这里还是要推荐下几个不错的模板:

  • 集群资源监控:3119(以监控资源的具体指标值为主)
  • 资源状态监控:6417(以监控资源的具体数量为主)
  • Node监控:9276
  • Etcd监控:9733

可以在grafana中根据模板id直接导入这几个模板,导入的方法比较简单,前面的文章都有讲过,这里不再赘述。

现在使用3119这个模板,来展示K8S集群的资源

因为在prometheus中已经有了数据,所以就直接能查看到的集群的资源了。

下图是网络IO图表,一个是接收,一个是发送。
在这里插入图片描述
再下面是集群内存、CPU、文件系统的使用情况。可以看到文件系统并没有显示出来,我们可以看下它的PromQL怎么写的,然后把这个PromQL UI上去调试一下即可。
在这里插入图片描述
来看一下怎么解决:
在这里插入图片描述
在这里插入图片描述
可以看到默认的device是用xvda去匹配的,我们这里改成sda就行。改完之后,数据就出来了。
在这里插入图片描述
其他的图标也有一些需要修改的地方,根据自己集群的实际情况进行修改就行。我这边是把我修改好的模板给导出来保存为json文件了。

现在来看一下资源状态监控的模板6417,直接导入这个模板。
在这里插入图片描述
可以看到,磁盘这里显示不出数据来,这是因为查询语句做了更新,我们添加上_bytes就可以了。
在这里插入图片描述在这里插入图片描述
修改完成后如下图:
在这里插入图片描述

node监控

node监控这里使用9276模板,直接导入模板来看下。分组名称选择node-exporter,IP地址选择主机名即可。
在这里插入图片描述
在这个模板里面,获取网络带宽失败,这是因为网卡名称的原因,有的是eth0,有的是ens32,ens33等。这个根据自己的实际情况去修改就行。
在这里插入图片描述
修改完之后,就有数据了:
在这里插入图片描述

Etcd监控

Etcd的监控这里使用9733模板,直接导入即可。
在这里插入图片描述
这个模板是中文的,而且所有的监控项都能正常的获取到数据。

五、配置报警规则和报警方式

1、配置报警规则

监控项和监控展示都已经搞定了,现在要做的就是设置相应的报警规则,并把报警内容给发送到相关人员的手中。

上面知道了怎么样自定义一个ServiceMonitor对象,怎么样使用grafana模板来展示监控的数据。但是如果需要自定义一个规则呢?如果现在去看Prometheus Dashboard的Alert页面下面就已经有一些报警规则了,还有一些规则已经被触发了:
在这里插入图片描述
这时候,就会产生一些疑问:这些报警信息是哪里来的呢?应该用怎么样的方式通知我们呢?

之前在使用k8s之前,用自定义的方式我可以在Prometheus的配置文件中指定AlertManager实例和报警rules文件,这都好理解。但是我们现在是通过Operator部署的Prometheus,那我们要怎么自定义AlertManager和rules呢?我们可以在Prometheus Dashboard的Configuration页面下查看关于AlertManager的配置:

global:
  scrape_interval: 30s
  scrape_timeout: 10s
  evaluation_interval: 30s
  external_labels:
    prometheus: monitoring/k8s
    prometheus_replica: prometheus-k8s-0
alerting:
  alert_relabel_configs:
  - separator: ;
    regex: prometheus_replica
    replacement: $1
    action: labeldrop
  alertmanagers:
  - kubernetes_sd_configs:
    - role: endpoints
      namespaces:
        names:
        - monitoring
    scheme: http
    path_prefix: /
    timeout: 10s
    api_version: v1
    relabel_configs:
    - source_labels: [__meta_kubernetes_service_name]
      separator: ;
      regex: alertmanager-main
      replacement: $1
      action: keep
    - source_labels: [__meta_kubernetes_endpoint_port_name]
      separator: ;
      regex: web
      replacement: $1
      action: keep
rule_files:
- /etc/prometheus/rules/prometheus-k8s-rulefiles-0/*.yaml

从上面的alertmanager实例的配置我们可以看到是通过角色为endpoints的kubernetes的服务发现机制获取的,匹配的是服务名为alertmanager-main,端口名为web的Service服务。然后我们查看下alertmanager-main这个Service:

[root@k8s-01 ~]# kubectl describe svc alertmanager-main -n monitoring
Name:              alertmanager-main
Namespace:         monitoring
Labels:            alertmanager=main
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"alertmanager":"main"},"name":"alertmanager-main","namespace":"...
Selector:          alertmanager=main,app=alertmanager
Type:              ClusterIP
IP:                10.254.245.0
Port:              web  9093/TCP
TargetPort:        web/TCP
Endpoints:         172.30.16.3:9093,172.30.32.5:9093,172.30.32.8:9093
Session Affinity:  ClientIP
Events:            <none>

可以看到服务名正是alertmanager-main,Port定义的名称也是web,符合上面的规则,所以Prometheus和AlertManager组件就正确关联上了。而对应的报警文件位于/etc/prometheus/rules/prometheus-k8s-rulefiles-0/这个目录下面所有的YAML文件。我们可以进入Prometheus的Pod中验证下该目录下面是否有YAML文件:

[root@k8s-01 ~]# kubectl exec -it prometheus-k8s-0 -n monitoring /bin/sh  
Defaulting container name to prometheus.
Use 'kubectl describe pod/prometheus-k8s-0 -n monitoring' to see all of the containers in this pod.
/prometheus $ ls /etc/prometheus/rules/prometheus-k8s-rulefiles-0/
monitoring-prometheus-k8s-rules.yaml
/prometheus $ cat /etc/prometheus/rules/prometheus-k8s-rulefiles-0/monitoring-prometheus-k8s-rules.yaml
groups:
- name: node-exporter.rules
  rules:
  - expr: |
      count without (cpu) (
        count without (mode) (
          node_cpu_seconds_total{job="node-exporter"}
        )
      )
    record: instance:node_num_cpu:sum
  - expr: |
      1 - avg without (cpu, mode) (
        rate(node_cpu_seconds_total{job="node-exporter", mode="idle"}[1m])
      )
    record: instance:node_cpu_utilisation:rate1m
  - expr: |
      (
        node_load1{job="node-exporter"}
      /
        instance:node_num_cpu:sum{job="node-exporter"}
      )
    record: instance:node_load1_per_cpu:ratio
  - expr: |
      1 - (
        node_memory_MemAvailable_bytes{job="node-exporter"}
      /
        node_memory_MemTotal_bytes{job="node-exporter"}
      )
    record: instance:node_memory_utilisation:ratio
  - expr: |
      rate(node_vmstat_pgmajfault{job="node-exporter"}[1m])
    record: instance:node_vmstat_pgmajfault:rate1m
............

这个YAML文件实际上就是Prometheus Operator项目中的prometheus-rules.yaml文件创建的一个PrometheusRule 包含的:

[root@k8s-01 manifests]# cat prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  labels:
    prometheus: k8s
    role: alert-rules
  name: prometheus-k8s-rules
  namespace: monitoring
spec:
  groups:
  - name: node-exporter.rules
    rules:
    - expr: |
        count without (cpu) (
          count without (mode) (
            node_cpu_seconds_total{job="node-exporter"}
          )
        )
      record: instance:node_num_cpu:sum
    - expr: |
        1 - avg without (cpu, mode) (
          rate(node_cpu_seconds_total{job="node-exporter", mode="idle"}[1m])
        )
      record: instance:node_cpu_utilisation:rate1m
    - expr: |
        (
          node_load1{job="node-exporter"}
        /
          instance:node_num_cpu:sum{job="node-exporter"}
        )
      record: instance:node_load1_per_cpu:ratio
    - expr: |
        1 - (
          node_memory_MemAvailable_bytes{job="node-exporter"}
        /
          node_memory_MemTotal_bytes{job="node-exporter"}
        )
      record: instance:node_memory_utilisation:ratio
    - expr: |
        rate(node_vmstat_pgmajfault{job="node-exporter"}[1m])
      record: instance:node_vmstat_pgmajfault:rate1m
............

这里的PrometheusRule的name为prometheus-k8s-rules,namespace为monitoring,我们可以猜想到我们创建一个PrometheusRule资源对象后,会自动在上面的prometheus-k8s-rulefiles-0目录下面生成一个对应的-.yaml文件,所以如果以后我们需要自定义一个报警选项的话,只需要定义一个Prometheus资源对象即可。治愈为什么Prometheus能够识别这个PrometheusRule资源对象呢?这就需要查看我们创建的prometheus这个资源对象了,里面有非常重要的一个属性ruleSelector,哟呵你过来匹配rule规则的过滤器,要求匹配具有prometheus=k8s和role=alert-rules标签的PrometheusRules资源对象。

ruleSelector:
  matchLabels:
    prometheus: k8s
    role: alert-rules

所以我们要想自定义一个报警规则,只需要创建一个具有prometheus=k8s和role=alert-rules标签的PrometheusRule对象就行了。

这里我个人认为Prometheus Operator项目中自带的报警规则不太适用,于是我就在Awesome Prometheus alerts这个网站中找了自己需要用的报警规则,因为内容比较多,我这里直接将整理报警规则放到了github中(注意label标签一定至少要有prometheus=k8s和role=alert-rules)。直接拿这个文件的内容覆盖掉prometheus-rules.yaml文件中的内容,然后使生效:

[root@k8s-01 manifests]# kubectl apply -f prometheus-rules.yaml                 
prometheusrule.monitoring.coreos.com/prometheus-k8s-rules configured

然后再去Prometheus Dashboard的Alert页面下就可以看到上面我们新建的报警规则了:
在这里插入图片描述

2、配置报警方式

我们知道了如何去添加一个报警规则配置项,但是这些报警信息用怎样的方式去发送呢?之前我们已经知道了可以通过AlertManager的配置文件去配置各种报警接收器,这没啥问题。但是现在我们是通过Prometheus Operator提供的alertmanager资源对象创建的组件,应该怎么样去修改配置呢?

首先我们将alertmanager-main这个Service改为NodePort类型的Service,修改完成后可以在页面上的status路径下查看AlertManager的配置信息:

[root@k8s-01 manifests]# kubectl edit svc alertmanager-main -n monitoring
......
  selector:
    alertmanager: main
    app: alertmanager
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800
  type: NodePort
......

然后用NodePort访问alertmanager-main
在这里插入图片描述
这些配置信息实际上是来自于kube-prometheus/manifests/目录下面的alertmanager-secret.yaml文件

[root@k8s-01 manifests]# cat alertmanager-secret.yaml 
apiVersion: v1
data: {}
kind: Secret
metadata:
  name: alertmanager-main
  namespace: monitoring
stringData:
  alertmanager.yaml: |-
    "global":
      "resolve_timeout": "5m"
    "inhibit_rules":
    - "equal":
      - "namespace"
      - "alertname"
      "source_match":
        "severity": "critical"
      "target_match_re":
        "severity": "warning|info"
    - "equal":
      - "namespace"
      - "alertname"
      "source_match":
        "severity": "warning"
      "target_match_re":
        "severity": "info"
    "receivers":
    - "name": "Default"
    - "name": "Watchdog"
    - "name": "Critical"
    "route":
      "group_by":
      - "namespace"
      "group_interval": "5m"
      "group_wait": "30s"
      "receiver": "Default"
      "repeat_interval": "12h"
      "routes":
      - "match":
          "alertname": "Watchdog"
        "receiver": "Watchdog"
      - "match":
          "severity": "critical"
        "receiver": "Critical"
type: Opaque

所以如果我们想要添加自己的接收器,或者模板消息,就可以更改这个文件。这里我修改报警接收为邮件+微信。创建alertmanager.yaml文件,里面设置好发动的方式、路由和接受者,内容如下:

[root@k8s-01 manifests]# vim alertmanager.yaml
global:
  resolve_timeout: 5m
  smtp_smarthost: 'smtp.163.com:25'
  smtp_from: '15257862054@163.com'
  smtp_auth_username: '15257862054@163.com'
  smtp_auth_password: 'PNRUAELMPDOMTEMP'
  smtp_require_tls: false
templates:
  - '*.tmpl'
route:
  group_by: ['job', 'severity']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 5m
  receiver: default
  routes:
  - receiver: 'wechat'
    group_wait: 10s
    match:
      severity: warning
  - receiver: 'wechat'
    group_wait: 5s
    match:
      severity: critical
receivers:
- name: 'default'
  email_configs:
  - to: '1695040842@qq.com'
    send_resolved: true
- name: 'wechat'
  wechat_configs:
  - corp_id: 'wwa4b5acd214ea2189'
    to_party: '2'
    to_user: "WangChao"
    agent_id: '1000003'
    api_secret: '563Dd_wXc6o2mQ4A2nWcPbuhIh8jLLlehs96hXnX23c'
    send_resolved: true

然后再创建一个微信模板文件wechat.tmpl,内容如下:

[root@k8s-01 manifests]# vim wechat.tmpl
{{ define "wechat.default.message" }}
{{- if gt (len .Alerts.Firing) 0 -}}
{{- range $index, $alert := .Alerts -}}
{{- if eq $index 0 }}
=======异常告警========
告警类型: {{ $alert.Labels.alertname }}
告警级别: {{ $alert.Labels.severity }}
告警详情: {{ $alert.Annotations.message }}{{ $alert.Annotations.description}};{{$alert.Annotations.summary}}
故障时间: {{ ($alert.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}
{{- if gt (len $alert.Labels.instance) 0 }}
实例信息: {{ $alert.Labels.instance }}
{{- end }}
{{- if gt (len $alert.Labels.namespace) 0 }}
命名空间: {{ $alert.Labels.namespace }}
{{- end }}
{{- if gt (len $alert.Labels.node) 0 }}
节点信息: {{ $alert.Labels.node }}
{{- end }}
{{- if gt (len $alert.Labels.pod) 0 }}
实例名称: {{ $alert.Labels.pod }}
{{- end }}
=========END===========
{{- end }}
{{- end }}
{{- end }}
{{- if gt (len .Alerts.Resolved) 0 -}}
{{- range $index, $alert := .Alerts -}}
{{- if eq $index 0 }}
=======异常恢复========
告警类型: {{ $alert.Labels.alertname }}
告警级别: {{ $alert.Labels.severity }}
告警详情: {{ $alert.Annotations.message }}{{ $alert.Annotations.description}};{{$alert.Annotations.summary}}
故障时间: {{ ($alert.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}
恢复时间: {{ ($alert.EndsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}
{{- if gt (len $alert.Labels.instance) 0 }}
实例信息: {{ $alert.Labels.instance }}
{{- end }}
{{- if gt (len $alert.Labels.namespace) 0 }}
命名空间: {{ $alert.Labels.namespace }}
{{- end }}
{{- if gt (len $alert.Labels.node) 0 }}
节点信息: {{ $alert.Labels.node }}
{{- end }}
{{- if gt (len $alert.Labels.pod) 0 }}
实例名称: {{ $alert.Labels.pod }}
{{- end }}
=========END===========
{{- end }}
{{- end }}
{{- end }}
{{- end }}

然后使用这两个文件创建一个Secret对象:

#删除原secret对象
[root@k8s-01 manifests]# kubectl delete secret alertmanager-main -n monitoring
  cret "alertmanager-main" deleted
#将自己的配置文件导入到新的secret中
[root@k8s-01 manifests]# kubectl create secret generic alertmanager-main --from-file=alertmanager.yaml --from-file=wechat.tmpl -n monitoring
secret/alertmanager-main created

我添加了两个接收器,默认的通过邮箱发送,对于告警级别为warning和critical的通过为wechat来发送。执行完成之后,很快就会收到邮件和微信的告警信息,并且等恢复之后,也会有恢复的信息:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

六、数据持久化

1、prometheus设置数据持久化

因为Prometheus Operator默认情况下没有将数据做持久化存储,当Pod被删除或者意外重启之后,可能会造成数据丢失。

首先在192.168.0.74服务器上部署NFS服务

#这里我使用单独的一台nfs服务器进行演示,实际上随便使用一台服务器安装nfs都是可以的(建议是和Kubernetes集群分开)
[root@nfs-server ~]# yum install -y nfs-utils rpcbind

#接下来设置nfs存储目录
[root@nfs-server ~]# mkdir -p /data/k8s-volume       
[root@nfs-server ~]# chmod 755 /data/k8s-volume/

#编辑nfs配置文件
[root@nfs-server ~]# vim /etc/exports
/data/k8s-volume  *(rw,no_root_squash,sync)
#存储目录,*表示允许所有人连接,rw续写权限,sync文件同时写入硬盘和内存,no_root_squash使用者root用户自动修改为普通用户

#启动rpcbind,由于nfs需要向rpcbind进行注册,所以要优先启动rpcbind
[root@nfs-server ~]# systemctl start rpcbind
[root@nfs-server ~]# systemctl enable rpcbind
[root@nfs-server ~]# systemctl status rpcbind
● rpcbind.service - RPC bind service
   Loaded: loaded (/usr/lib/systemd/system/rpcbind.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2020-09-01 14:56:24 CST; 21s ago
 Main PID: 9522 (rpcbind)
   CGroup: /system.slice/rpcbind.service
           └─9522 /sbin/rpcbind -w

Sep 01 14:56:24 nfs-server systemd[1]: Starting RPC bind service...
Sep 01 14:56:24 nfs-server systemd[1]: Started RPC bind service.

#启动的nfs
[root@nfs-server ~]# systemctl restart nfs
[root@nfs-server ~]# systemctl enable nfs 
[root@nfs-server ~]# systemctl status nfs
● nfs-server.service - NFS server and services
   Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; enabled; vendor preset: disabled)
  Drop-In: /run/systemd/generator/nfs-server.service.d
           └─order-with-mounts.conf
   Active: active (exited) since Tue 2020-09-01 14:58:51 CST; 26s ago
 Main PID: 9612 (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/nfs-server.service

Sep 01 14:58:50 nfs-server systemd[1]: Starting NFS server and services...
Sep 01 14:58:51 nfs-server systemd[1]: Started NFS server and services.

#检查rpcbind和nfs是否正常
[root@nfs-server ~]# rpcinfo | grep nfs
    100003    3    tcp       0.0.0.0.8.1            nfs        superuser
    100003    4    tcp       0.0.0.0.8.1            nfs        superuser
    100227    3    tcp       0.0.0.0.8.1            nfs_acl    superuser
    100003    3    udp       0.0.0.0.8.1            nfs        superuser
    100003    4    udp       0.0.0.0.8.1            nfs        superuser
    100227    3    udp       0.0.0.0.8.1            nfs_acl    superuser
    100003    3    tcp6      ::.8.1                 nfs        superuser
    100003    4    tcp6      ::.8.1                 nfs        superuser
    100227    3    tcp6      ::.8.1                 nfs_acl    superuser
    100003    3    udp6      ::.8.1                 nfs        superuser
    100003    4    udp6      ::.8.1                 nfs        superuser
    100227    3    udp6      ::.8.1                 nfs_acl    superuser

#查看nfs目录挂载权限
[root@nfs-server ~]# cat /var/lib/nfs/etab
/data/k8s-volume        *(rw,sync,wdelay,hide,nocrossmnt,secure,no_root_squash,no_all_squash,no_subtree_check,secure_locks,acl,no_pnfs,anonuid=65534,anongid=65534,sec=sys,rw,secure,no_root_squash,no_all_squash)

nfs server端已经挂载完毕,接下来在所有都需要nfs挂载的集群节点执行下面命令

[root@k8s-01 ~]# yum install -y nfs-utils rpcbind
[root@k8s-01 ~]# systemctl start rpcbind
[root@k8s-01 ~]# systemctl enable rpcbind
[root@k8s-01 ~]# systemctl start nfs
[root@k8s-01 ~]# systemctl enable nfs

NFS安装完毕之后,先来看一下prometheus operator数据存储的目录

[root@k8s-01 manifests]# kubectl get pod prometheus-k8s-0 -n monitoring -o yaml
......
    volumeMounts:
    - mountPath: /etc/prometheus/config_out
      name: config-out
      readOnly: true
    - mountPath: /etc/prometheus/certs
      name: tls-assets
      readOnly: true
    - mountPath: /prometheus
      name: prometheus-k8s-db
    - mountPath: /etc/prometheus/rules/prometheus-k8s-rulefiles-0
      name: prometheus-k8s-rulefiles-0
......
  - emptyDir: {}
    name: prometheus-k8s-db
  - name: prometheus-k8s-token-9rhsh

这里/prometheus目录使用的是emptyDir进行挂载,我们重建Pod之后之前的数据就没有了,由于Prometheus使用Statefulset控制器进行 部署的,为了保证数据一致性,这里采用storageclass来做持久化。

因为要使用刚刚创建好的NFS作为后端存储,这里需要一个nfs-client

#现在还需要创建NFS-Client,不然prometheus pod现在是无法Running状态
[root@k8s-01 storageclass]# vim nfs-client.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-client-provisioner
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs
            - name: NFS_SERVER
              value: 192.168.0.74           #nfs server 地址
            - name: NFS_PATH
              value: /data/k8s-volume     #nfs共享目录
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.0.74
            path: /data/k8s-volume

创建nfs-client rbac文件

[root@k8s-01 storageclass]# vim nfs-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io

创建

[root@k8s-01 storageclass]# kubectl create -f nfs-client.yaml
deployment.apps/nfs-client-provisioner created

[root@k8s-01 storageclass]# kubectl create -f nfs-rbac.yaml
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created

[root@k8s-01 manifests]# kubectl get pod | grep nfs-client
nfs-client-provisioner-65bc578f87-xtrhm   1/1     Running     0          6h25m

这里创建一个storageclass对象

[root@k8s-01 ~]# vim prometheus-storageclass.yaml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: prometheus-data-db
provisioner: fuseim.pri/ifs

[root@k8s-01 ~]# kubectl apply -f prometheus-storageclass.yaml 
storageclass.storage.k8s.io/prometheus-data-db created

这里我们声明StorageClass对象,其中的provisioner=fuseim.pri/ifs则是我们集群中使用NFS作为后端存储。

接下来我们在Prometheus中添加如下配置

[root@k8s-01 manifests]# vim kube-prometheus/manifests/prometheus-prometheus.yaml 
......
  storage:
    volumeClaimTemplate:
      spec:
        storageClassName: prometheus-data-db
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 20Gi
......

#只需要在sepc中添加对应的信息,storageClassName为刚刚创建的名称,storage为资源对象大小

Prometheus的完成配置文件如下:

[root@k8s-01 manifests]# cat prometheus-prometheus.yaml     
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  labels:
    prometheus: k8s
  name: k8s
  namespace: monitoring
spec:
  alerting:
    alertmanagers:
    - name: alertmanager-main
      namespace: monitoring
      port: web
  storage:
    volumeClaimTemplate:
      spec:
        storageClassName: prometheus-data-db
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 20Gi
  image: quay.io/prometheus/prometheus:v2.15.2
  nodeSelector:
    kubernetes.io/os: linux
  podMonitorNamespaceSelector: {}
  podMonitorSelector: {}
  replicas: 2
  secrets:
  - etcd-ssl #添加secret名称
  resources:
    requests:
      memory: 400Mi
  ruleSelector:
    matchLabels:
      prometheus: k8s
      role: alert-rules
  securityContext:
    fsGroup: 2000
    runAsNonRoot: true
    runAsUser: 1000
  serviceAccountName: prometheus-k8s
  serviceMonitorNamespaceSelector: {}
  serviceMonitorSelector: {}
  version: v2.15.2

更新并查看prometheus的启动状态

[root@k8s-01 manifests]# kubectl apply -f prometheus-prometheus.yaml 
prometheus.monitoring.coreos.com/k8s configured

#在更新之后,会自动创建两个指定大小的pv卷,因为会为prometheus的备份(prometheus-k8s-0,prometheus-k8s-1)也创建一个pv。pv卷创建之后无法修改,所以最好先考虑好适合的参数配置,比如访问模式和容量大小。

[root@k8s-01 manifests]# kubectl get pod -n monitoring
NAME                                   READY   STATUS    RESTARTS   AGE
alertmanager-main-0                    2/2     Running   4          2d
alertmanager-main-1                    2/2     Running   4          2d
alertmanager-main-2                    2/2     Running   4          2d
grafana-86b55cb79f-jmd7h               1/1     Running   2          2d
kube-state-metrics-dbb85dfd5-6dm9b     3/3     Running   6          2d
node-exporter-52js6                    2/2     Running   4          2d
node-exporter-bcr4h                    2/2     Running   4          2d
node-exporter-mrznt                    2/2     Running   4          2d
prometheus-adapter-5cd5798d96-f2k2n    1/1     Running   3          2d
prometheus-k8s-0                       3/3     Running   1          18s
prometheus-k8s-1                       3/3     Running   1          18s
prometheus-operator-5cfbdc9b67-m2qrj   2/2     Running   4          2d

可以看一下pv和pvc对象资源

[root@k8s-01 manifests]# kubectl get pv -n monitoring | grep prometheus
pvc-1fd80f38-ebfe-4f69-b7f1-ce7dcdfabc2f   20Gi       RWO            Delete           Bound    monitoring/prometheus-k8s-db-prometheus-k8s-1   prometheus-data-db             82s
pvc-68302b85-841b-40e9-a331-5e4b231989d9   20Gi       RWO            Delete           Bound    monitoring/prometheus-k8s-db-prometheus-k8s-0   prometheus-data-db             82s

[root@k8s-01 manifests]# kubectl get pvc -n monitoring
NAME                                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS         AGE
prometheus-k8s-db-prometheus-k8s-0   Bound    pvc-68302b85-841b-40e9-a331-5e4b231989d9   20Gi       RWO            prometheus-data-db   2m9s
prometheus-k8s-db-prometheus-k8s-1   Bound    pvc-1fd80f38-ebfe-4f69-b7f1-ce7dcdfabc2f   20Gi       RWO            prometheus-data-db   2m9s

接下来可以测试下Pod删除之后数据是否丢失

记录删除前的数据图
在这里插入图片描述

删除Pod

[root@k8s-01 setup]# kubectl delete pod prometheus-k8s-0 -n monitoring
pod "prometheus-k8s-0" deleted
[root@k8s-01 setup]# kubectl delete pod prometheus-k8s-1 -n monitoring 
pod "prometheus-k8s-1" deleted

等新的Pod启动之后,再查看结果
在这里插入图片描述

可以看到,数据没丢失。

2、修改prometheus的数据存储时间

prometheus是实现持久化了,但是还有一个问题,就是prometheus operator数据保留的天数,根据官方文档说明,默认prometheus operator数据存储的时间为1d,这个时候无论你prometheus operator如何进行持久化,都没有作用。因为数据只保留了1天,那么你是无法看到更多天的数据的。

官方文档可以配置的说明
在这里插入图片描述

实际上,修改prometheus operator时间是通过retention参数进行修改,,上面也提示了再prometheus.spec下填写。

接下来需要修改prometheus operator的deployment文件,来加上retention这个参数。

[root@k8s-01 manifests]# vim prometheus-prometheus.yaml 
......
spec:
  retention: 7d #添加数据保留时间
  alerting:
    alertmanagers:
    - name: alertmanager-main
      namespace: monitoring
      port: web
......

#更新
[root@k8s-01 manifests]# kubectl apply -f prometheus-prometheus.yaml 
prometheus.monitoring.coreos.com/k8s configured

修改完毕之后,等待2天,然后查看数据是否能正常显示。

3、grafana配置数据持久化

新建grafana-pvc,这里storageclass还是用之前的prometheus-data-db

[root@k8s-01 ~]# vim grafana-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: grafana-pvc
    namespace: monitoring
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 15Gi
  storageClassName: prometheus-data-db

创建pvc并查看pv和pvc对象资源

[root@k8s-01 ~]# kubectl apply -f grafana-pvc.yaml 
persistentvolumeclaim/grafana-pvc created

[root@k8s-01 ~]# kubectl get pv -n monitoring | grep prometheus
pvc-1fd80f38-ebfe-4f69-b7f1-ce7dcdfabc2f   20Gi       RWO            Delete           Bound    monitoring/prometheus-k8s-db-prometheus-k8s-1   prometheus-data-db             143m
pvc-330838e4-e254-49ce-be96-7e791867f175   15Gi       RWO            Delete           Bound    monitoring/grafana-pvc                          prometheus-data-db             3m10s
pvc-68302b85-841b-40e9-a331-5e4b231989d9   20Gi       RWO            Delete           Bound    monitoring/prometheus-k8s-db-prometheus-k8s-0   prometheus-data-db             143m

[root@k8s-01 ~]# kubectl get pvc -n monitoring
NAME                                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS         AGE
grafana-pvc                          Bound    pvc-330838e4-e254-49ce-be96-7e791867f175   15Gi       RWO            prometheus-data-db   3m36s
prometheus-k8s-db-prometheus-k8s-0   Bound    pvc-68302b85-841b-40e9-a331-5e4b231989d9   20Gi       RWO            prometheus-data-db   143m
prometheus-k8s-db-prometheus-k8s-1   Bound    pvc-1fd80f38-ebfe-4f69-b7f1-ce7dcdfabc2f   20Gi       RWO            prometheus-data-db   143m

然后在grafana-deployment.yaml中将emptydir存储方式改为pvc方式:

[root@k8s-01 manifests]# vim grafana-deployment.yaml
......
      volumes:
      #- emptyDir: {}
      #  name: grafana-storage
      - name: grafana-storage
        persistentVolumeClaim:
          claimName: grafana-pvc
      - name: grafana-datasources
        secret:
          secretName: grafana-datasources
      - configMap:
          name: grafana-dashboards
        name: grafana-dashboards
      - configMap:
          name: grafana-dashboard-apiserver
        name: grafana-dashboard-apiserver
      - configMap:
          name: grafana-dashboard-cluster-total
        name: grafana-dashboard-cluster-total
      - configMap:
          name: grafana-dashboard-controller-manager
......

更新grafana

[root@k8s-01 manifests]# kubectl apply -f grafana-deployment.yaml 
deployment.apps/grafana configured

然后去nfs存储上看一眼
在这里插入图片描述

好了,到这里grafana的数据持久化也完成了。

参考文章:
https://blog.51cto.com/14143894/2438026?source=drh
https://www.cnblogs.com/xzkzzz/p/10532002.html
https://www.cnblogs.com/zhangzhi19861216/p/12591345.html
https://blog.csdn.net/qq_24923725/article/details/82910793?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param
https://www.cnblogs.com/yuhaohao/p/10197315.html
https://awesome-prometheus-alerts.grep.to/rules#host-and-hardware
https://www.cnblogs.com/coolops/p/13174233.html
https://i4t.com/4586.html
https://blog.csdn.net/qq_37332827/article/details/105114439

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值