监控学习系列(上):prometheus,grafana,promQL,AlertManager,Thanos背景
文章目录介绍kubernete上部署prometheus开始部署prometheus的webui普通应用与export的监控集群节点、集群自身监控与服务发现设置服务发现容器监控监控api-server监控podkube-state-metrics(deploy副本数,pod状态,pod重启次数)grafana介绍部署grafana部署监控集群的grafana插件PromQLpromQL查询Aler
文章目录
介绍
对于 Kubernetes 这种比较庞大的系统来说,监控报警是不可或缺,我们需要时刻了解系统的各种运行指标,也需要时刻了解我们的 Pod 的各种指标,更需要在出现问题的时候有报警信息通知到我们。
在早期的版本中 Kubernetes 提供了 heapster、influxDB、grafana 的组合来监控系统,在现在的版本中已经移除掉了 heapster,现在更加流行的监控工具是 Prometheus,Prometheus 是 Google 内部监控报警系统的开源版本,是 Google SRE 思想在其内部不断完善的产物,它的存在是为了更快和高效的发现问题,快速的接入速度,简单灵活的配置都很好的解决了这一切,而且是已经毕业的 CNCF 项目。
Prometheus 最初是 SoundCloud 构建的开源系统监控和报警工具,是一个独立的开源项目,于2016年加入了 CNCF 基金会,作为继 Kubernetes 之后的第二个托管项目。Prometheus 相比于其他传统监控工具主要有以下几个特点:
1.具有由 metric 名称和键/值对标识的时间序列数据的多维数据模型
2.有一个灵活的查询语言(promQL)
3.不依赖分布式存储,只和本地磁盘有关
4.通过 HTTP 的服务拉取时间序列数据
5。也支持推送的方式来添加时间序列数据(push)
6.还支持通过服务发现或静态配置发现目标(服务发现配置规则)
7.多种图形和仪表板支持
Prometheus 由多个组件组成,但是其中有些组件是可选的:
1.Prometheus Server:用于抓取指标、存储时间序列数据(被抓取对象只需要提供对应的metrics端口即可被prometheus监控,没有原生的metrics端口,可以用第三方方案来实现metrics端口)
2.exporter:暴露指标让任务来抓(被监控对戏暴露metrics数据等待prometheus server来抓取)
3.pushgateway:push 的方式将指标数据推送到该网关(被监控对象主动推送metrics指标监控数据)
4alertmanager:处理报警的报警组件 adhoc:用于数据查询
大多数 Prometheus 组件都是用 Go 编写的,因此很容易构建和部署为静态的二进制文件。下图是 Prometheus 官方提供的架构及其一些相关的生态系统组件:
流程学习后再回头看着架构图会更清晰
Prometheus 直接接收或者通过中间的 Pushgateway 网关被动获取指标数据,在本地存储所有的获取的指标数据,并对这些数据进行一些规则整理,用来生成一些聚合数据或者报警信息,Grafana 或者其他工具用来可视化这些数据。
http方式获取数据
kubernete上部署prometheus
非集群下安装(不用操作):
直接二进制安装即可:https://prometheus.io/download
Prometheus 是通过一个 YAML 配置文件来进行启动的,如果我们使用二进制的方式来启动的话,可以使用下面的命令:
./prometheus --config.file=prometheus.yml
prometheus.yml 文件的基本配置:
基本三部分,全局配置,报警规则,监控对象
global 模块控制 Prometheus Server 的全局配置:
scrape_interval:表示 prometheus 抓取指标数据的频率,默认是15s,我们可以覆盖这个值
evaluation_interval:用来控制评估规则的频率,prometheus 使用规则产生新的时间序列数据或者产生警报
rule_files:指定了报警规则所在的位置,prometheus 可以根据这个配置加载规则,用于生成新的时间序列数据或者报警信息,当前我们没有配置任何报警规则。
scrape_configs 用于控制 prometheus 监控哪些资源。(按job为单位)
由于 prometheus 通过 HTTP 的方式来暴露的它本身的监控数据,prometheus
也能够监控本身的健康情况。在默认的配置里有一个单独的 job,叫做 prometheus,它采集 prometheus
服务本身的时间序列数据。这个 job 包含了一个单独的、静态配置的目标:监听 localhost 上的 9090 端口。prometheus
默认会通过目标的 /metrics 路径采集 metrics。所以,默认的 job 通过
URL:http://localhost:9090/metrics 采集 metrics。收集到的时间序列包含 prometheus
服务本身的状态和性能。如果我们还有其他的资源需要监控的话,直接配置在 scrape_configs 模块下面就可以了。
被监控的对象的(包括prometheus自身)一般都是通过http方式暴露metrics指标数据
示例应用¶
比如我们在本地启动一些样例来让 Prometheus 采集。Go 客户端库包含一个示例,该示例为具有不同延迟分布的三个服务暴露 RPC 延迟。
首先确保已经安装了 Go 环境并启用 go modules,下载 Prometheus 的 Go 客户端库并运行这三个示例:
$ git clone https://github.com/prometheus/client_golang.git
$ cd client_golang/examples/random
$ export GO111MODULE=on
$ export GOPROXY=https://goproxy.cn
$ go build
然后在3个独立的终端里面运行3个服务:
$ ./random -listen-address=:8080
$ ./random -listen-address=:8081
$ ./random -listen-address=:8082
这个时候我们可以得到3个不同的监控接口:http://localhost:8080/metrics、http://localhost:8081/metrics 和 http://localhost:8082/metrics。
现在我们配置 Prometheus 来采集这些新的目标,让我们将这三个目标分组到一个名为 example-random 的任务。假设前两个端点(即:http://localhost:8080/metrics、http://localhost:8081/metrics )都是生产级目标应用,第三个端点(即:http://localhost:8082/metrics )为金丝雀实例。要在 Prometheus 中对此进行建模,我们可以将多组端点添加到单个任务中,为每组目标添加额外的标签。 在此示例中,我们将 group =“production” 标签添加到第一组目标,同时将 group=“canary”添加到第二组。将以下配置添加到 prometheus.yml 中的 scrape_configs 部分,然后重新启动 Prometheus 实例:
scrape_configs:
- job_name: 'example-random'
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
static_configs:
- targets: ['localhost:8080', 'localhost:8081']
labels:
group: 'production'
- targets: ['localhost:8082']
labels:
group: 'canary'
然后我们可以到浏览器中查看 Prometheus 的配置是否有新增的任务,这就是 Prometheus 添加监控配置最基本的配置方式了,非常简单,只需要提供一个符合 metrics 格式的可访问的接口配置给 Prometheus 就可以了。
开始部署
部署在kubernetes上,也就是以容器的形式部署
3centos的kubernetes集群节点,内核3.10,版本1.20,每节点4cpu(可以调小点)和4g运行(可以调小,内存尽量给高),cri是docker
为了方便管理,我们将监控相关的所有资源对象都安装在 kube-mon 这个 namespace 下面,没有的话可以提前创建。
将 prometheus.yml 文件用 ConfigMap 的形式进行管理(云端应用一般都这么做,方便配置)
prometheus-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: kube-mon
data:
prometheus.yml: |
global:
scrape_interval: 15s #抓取指标数据间隔
scrape_timeout: 15s #抓取超时设置,也就是一次性最多抓取15s
scrape_configs:
- job_name: 'prometheus' #默认自带的自身监控
static_configs:
- targets: ['localhost:9090'] #抓取的对象
你也可以用命令行方式生成cm
apply更新cm一般隔一会prometheus.yml也会更新
想监控心得资源更改cm即可
[root@master ~]# kubectl create ns kube-mon
Error from server (AlreadyExists): namespaces "kube-mon" already exists
[root@master ~]# cat > prometheus-cm.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: kube-mon
data:
prometheus.yml: |
global:
scrape_interval: 15s
scrape_timeout: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
EOF
[root@master ~]# kubectl apply -f prometheus-cm.yaml
configmap/prometheus-config created
创建 prometheus 的 Pod 资源(以pod形式部署prometheus)
使用的是v2.24.1版本
prometheus-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: kube-mon
labels:
app: prometheus
spec:
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
serviceAccountName: prometheus
initContainers: #初始化容器
- name: fix-permissions
image: busybox
command: [chown, -R, "nobody:nobody", /prometheus] #初始化容器完成操作就退出,这里的操作是更改挂载卷的目录的属性,操作带来的变化会留在卷中。这里的这个卷用的是localpv类型,使用的是主机的path目录,会修改这个目录的权限
volumeMounts: #挂载volume
- name: data
mountPath: /prometheus #挂载在初始化容器这个路径下
containers:
- image: prom/prometheus:v2.24.1
name: prometheus
args:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus" # 指定tsdb数据路径
- "--storage.tsdb.retention.time=24h"
- "--web.enable-admin-api" # 控制对admin HTTP API的访问,其中包括删除时间序列等功能
- "--web.enable-lifecycle" # 支持热更新,直接执行localhost:9090/-/reload立即生效
ports:
- containerPort: 9090
name: http
volumeMounts:
- mountPath: "/etc/prometheus" #prometheus思维配置文件一般都是放在这个路径下
name: config-volume
- mountPath: "/prometheus" #prometheus存放数据的路径
name: data
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 100m
memory: 512Mi
volumes:
- name: data
persistentVolumeClaim:
claimName: prometheus-data
- configMap: #cm类型的volume
name: prometheus-config
name: config-volume
为了 prometheus 的性能和数据持久化我们这里是直接将通过一个 LocalPV 来进行数据持久化的(后期改成ceph来做存储),通过 –storage.tsdb.path=/prometheus 指定数据目录,创建如下所示的一个 PVC 资源对象,注意是一个 LocalPV,和 node2(master,node1,node20 节点具有亲和性:
现在node2上:
mkdir -p /data/k8s/prometheus
docker pull prom/prometheus:v2.24.1(手动在对应的节点下载镜像,有时候pod调度后kubelet负责控制cri去下载镜像会很慢,原因可能是用的残酷云不一样的,docker这里配置阿里加速镜像,这里的节点是node2)
prometheus-pvc.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: prometheus-local
labels:
app: prometheus
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 20Gi
storageClassName: local-storage
local:
path: /data/k8s/prometheus #该pv使用的主机路径
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node2
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: prometheus-data
namespace: kube-mon
spec:
selector:
matchLabels:
app: prometheus
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: local-storage
由于 prometheus 可以访问 Kubernetes 的一些资源对象,所以需要配置 rbac 相关认证,这里我们使用了一个名为 prometheus 的 serviceAccount 对象:
prometheus-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: kube-mon
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole #我们要获取的资源信息,在每一个 namespace 下面都有可能存在,所以我们这里使用的是 ClusterRole 的资源对象
metadata:
name: prometheus
rules:
- apiGroups:
- ""
resources:
- nodes
- services
- endpoints
- pods
- nodes/proxy
verbs:
- get
- list
- watch
- apiGroups:
- "extensions"
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- configmaps
- nodes/metrics
verbs:
- get
- nonResourceURLs: #我们这里的权限(角色)规则声明中有一个 nonResourceURLs 的属性,是用来对非资源型 metrics 进行操作的权限声明(非Kubernetes资源和rbd资源类型)(少见,一般是metrics)
- /metrics
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: kube-mon
[root@master ~]# kubectl apply -f prometheus-rbac.yaml
serviceaccount/prometheus created
clusterrole.rbac.authorization.k8s.io/prometheus configured
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRoleBinding
clusterrolebinding.rbac.authorization.k8s.io/prometheus configured
[root@master ~]# kubectl apply -f prometheus-pvc.yaml
persistentvolume/prometheus-local created
persistentvolumeclaim/prometheus-data created
[root@master ~]# kubectl apply -f prometheus-deploy.yaml
deployment.apps/prometheus created
整个部署过程如果出现错误,可以kubectl delete -f
prometheus-rbac.yaml(clusterrole资源),kubectl delete ns
kube-mon,删除对应的pv,删除/data/k8s/prometheus,重新按照上边的步骤操作。
pod创建过程中,如果添加了某些配置,可以等一会,一般会自动定期扫描配置,不要立刻删除重建,想prometheus的deploy删了也是不彻底的,最后会有个error的pod。
创建 Pod 后,很可能看到并没有成功运行,出现了 open /prometheus/queries.active: permission denied 这样的错误信息,这是因为我们的 prometheus 的镜像中是使用的 nobody 这个用户,然后现在我们通过 LocalPV 挂载到宿主机上面的目录的 ownership 却是 root:
$ ls -la /data/k8s
total 36
drwxr-xr-x 6 root root 4096 Dec 12 11:07 .
dr-xr-xr-x. 19 root root 4096 Nov 9 23:19 ..
drwxr-xr-x 2 root root 4096 Dec 12 11:07 prometheus
所以当然会出现操作权限问题了,这个时候我们就可以通过 securityContext 来为 Pod 设置下 volumes 的权限,通过设置 runAsUser=0 指定运行的用户为 root,也可以通过设置一个 initContainer 来修改数据目录权限:
......
initContainers:
- name: fix-permissions
image: busybox
command: [chown, -R, "nobody:nobody", /prometheus]
volumeMounts:
- name: data
mountPath: /prometheus
已经提前加入在上面的prometheus-deploy.yaml文件中了
prometheus的webui
Pod 创建成功后,为了能够在外部访问到 prometheus 的 webui 服务,我们还需要创建一个 Service 对象
prometheus-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: kube-mon
labels:
app: prometheus
spec:
selector:
app: prometheus
type: NodePort #NodePort类型方便测试
ports:
- name: web
port: 9090
targetPort: http
[root@master ~]# kubectl apply -f prometheus-svc.yaml
service/prometheus created
[root@master ~]# kubectl get svc -n kube-mon
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
prometheus NodePort 10.100.31.161 <none> 9090:31973/TCP 18s
[root@master ~]# kubectl get pod -A -l app=prometheus
NAMESPACE NAME READY STATUS RESTARTS AGE
istio-system prometheus-f675ff955-h7wgv 1/2 CrashLoopBackOff 113 9d
kube-mon prometheus-796566c67c-5bw5h 1/1 Running 0 14h
插入一个坑,异常断电等不正常关机行为,导致prometheus的时间戳(时间很重要,metrics就是时间序列数据)与主机的时间对不上,解决思维就是让prometheus存储数据的路径的目录时间与prometheus时间一致
1.Prometheus断电后启动异常 Error on ingesting samples
2.真正解决prometheus Error on ingesting samples that are too old or are too far into the
future的问题
kubectl logs 看到这些问题可以这样解决
http://任意ip:nodeport访问svc
status->target:
我们现在还没有配置任何的报警信息,所以 Alerts 菜单下面现在没有任何数据
注意prometheus的时间和我们的主机时间不一样的,peometheys用的是格林尼治时间
普通应用与export的监控
Prometheus 的数据指标是通过一个公开的 HTTP(S) 数据接口获取到的,我们不需要单独安装监控的 agent,只需要暴露一个 metrics 接口,Prometheus 就会定期去拉取数据(或者被监控端主动提交指标数据);对于一些普通的 HTTP 服务,我们完全可以直接重用这个服务,添加一个 /metrics 接口暴露给 Prometheus;
其实对api-server的操作一般也是会转换成http方式(REST API)
现在很多服务从一开始就内置了一个 /metrics 接口,比如 Kubernetes 的各个组件、istio 服务网格都直接提供了数据指标接口(istio可以选择自带的peometheus搭配grafana)。有一些服务即使没有原生集成该接口,也完全可以使用一些 exporter 来获取到指标数据,比如 mysqld_exporter、node_exporter,这些 exporter 就有点类似于传统监控服务中的 agent,作为服务一直存在,用来收集目标服务的指标数据然后直接暴露给 Prometheus。
对于普通应用只需要能够提供一个满足 prometheus 格式要求的 /metrics 接口就可以让 Prometheus 来接管监控,比如 Kubernetes 集群中非常重要的 CoreDNS 插件,一般默认情况下就开启了 /metrics 接口:
一般kubectl describe和get -o yaml都能判断出某个服务的pod使用的对外暴露metrics的端口
[root@master ~]# kubectl get cm coredns -n kube-system -o yaml
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153 #用来暴露metrics数据的http端口
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
creationTimestamp: "2022-04-13T15:04:47Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:Corefile: {}
manager: kubeadm
operation: Update
time: "2022-04-13T15:04:47Z"
name: coredns
namespace: kube-system
resourceVersion: "233"
uid: 56021a1a-54fc-4517-a6d8-ee0d44987890
过去的kubernetes的kube-dns功能由kube-dns这个pod来实现,现在是coredns。
curl http://10.244.0.2:9153/metrics
测试,看到各种指标数据即表示该端口正常
将这个 /metrics 接口配置到 prometheus.yml 中去(这里是prometheus-cm.yaml)
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: kube-mon
data:
prometheus.yml: |
global:
scrape_interval: 15s
scrape_timeout: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'coredns' #新建一条抓取任务,名叫coredns
static_configs: #静态配置
- targets: ['10.244.0.2:9153', '10.244.0.3:9153'] #列表
kubectl apply -f prometheus-cm.yaml
只是一个很简单的配置,scrape_configs 下面可以支持很多参数,例如:
basic_auth 和 bearer_token:比如我们提供的 /metrics 接口需要 basic 认证的时候,通过传统的用户名/密码或者在请求的 header 中添加对应的 token 都可以支持
kubernetes_sd_configs 或 consul_sd_configs:可以用来自动发现一些应用的监控数据
现在 Prometheus 的配置文件内容已经更改了,隔一会儿被挂载到 Pod 中的 prometheus.yml 文件也会更新(volume是configmap形式,会挂载该文件,config是键值对形式,键是文件名),由于我们之前的 Prometheus 启动参数中添加了 --web.enable-lifecycle 参数,所以现在我们只需要执行一个 reload 命令即可让配置生效:
[root@master ~]# kubectl get pod -n kube-mon -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
prometheus-796566c67c-cmzgx 1/1 Running 0 14h 10.244.1.3 node2 <none> <none>
[root@master ~]# kubectl describe pod prometheus-796566c67c-cmzgx -n kube-mon
---
Containers:
prometheus:
Container ID: docker://76fac2b489328bb4b67ef613b021bb04a4a899165e3a0ddbec66f4c135dc35bb
Image: prom/prometheus:v2.24.1
Image ID: docker-pullable://prom/prometheus@sha256:ea420f6cd98e4e43e264a7a2e6e27e8328f47aa32d937e0c6e8e3b30fdefe6eb
Port: 9090/TCP
Host Port: 0/TCP
---
可以看到prometheus的pod的ip和该pod的提供服务的pod端口targetport是9090
curl -X POST http://10.244.1.3:9090/-/reload(修改配置后运行,让promethues重载服务配置,就像是主机上修改配置文件后systemctl restart)
-X指定协议类型,这里是POST类型
由于 ConfigMap 通过 Volume 的形式挂载到 Pod 中去的热更新需要一定的间隔时间才会生效,所以需要稍微等一小会儿。
读取metrics数据时遇到些问题可以考虑删除prometheus这个pod(deploy,相当于重启)
总结两点:
1.修改配置文件,prometheus的文件热更新后
2.发送POST让prometheus重载服务配置
3.重新载入配置的时间不定,可以要等一会才完成配置
完成了普通应用的监控
使用 exporter 监控:
应用可能没有自带 /metrics 接口供 Prometheus 使用,在这种情况下,我们就需要利用 exporter 服务来为 Prometheus 提供指标数据了。Prometheus 官方为许多应用就提供了对应的 exporter 应用,也有许多第三方的实现,我们可以前往官方网站进行查看:查看自带exporter的应用,当然如果你的应用本身也没有 exporter 实现,那么就要我们自己想办法去实现一个 /metrics 接口了,只要你能提供一个合法的 /metrics 接口,Prometheus 就可以监控你的应用。
1.自带metrics接口
2.自带export应用(类似传统监控的agent)实现metrics
3.第三方方案实现metrics
通过一个 redis-exporter 的服务来监控 redis 服务,对于这类应用,我们一般会以 sidecar 的形式和主应用部署在同一个 Pod 中,比如我们这里来部署一个 redis 应用,并用 redis-exporter 的方式来采集监控数据供 Prometheus 使用
prometheus-redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: kube-mon
spec:
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:4
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 6379
- name: redis-exporter
image: oliver006/redis_exporter:latest #redis的exporter
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 9121 #pod端口一般默认和容器端口一样
---
kind: Service
apiVersion: v1
metadata:
name: redis
namespace: kube-mon
spec:
selector:
app: redis
ports:
- name: redis
port: 6379
targetPort: 6379
- name: prom
port: 9121
targetPort: 9121 #service的port连接到匹配的pod的targetport端口
通过 9121 端口来校验是否能够采集到数据:
curl 10.244.2.2:9121/metrics
这里是通过 Service 去配置的 redis 服务,当然直接配置 Pod IP 也是可以的,因为和 Prometheus 处于同一个 namespace,所以我们直接使用 servicename(servicename.namespace.svc.cluster.local) 即可
集群节点、集群自身监控与服务发现设置
对于 Kubernetes 集群本身的监控也是非常重要的,我们需要时时刻刻了解集群的运行状态。
对于集群的监控一般我们需要考虑以下几个方面:
1.Kubernetes 节点的监控:比如节点的 cpu、load、disk、memory 等指标
2.内部系统组件的状态:比如 kube-scheduler、kube-controller-manager、kubedns/coredns 等组件的详细运行状态
3.编排级的 metrics:比如 Deployment 的状态、资源请求、调度和 API 延迟等数据指标
Kubernetes 集群的监控方案目前主要有以下几种方案:
Heapster:Heapster 是一个集群范围的监控和数据聚合工具,以 Pod 的形式运行在集群中。 heapster 除了 Kubelet/cAdvisor 之外,我们还可以向 Heapster 添加其他指标源数据,比如 kube-state-metrics,需要注意的是 Heapster 已经被废弃了,后续版本中会使用 metrics-server 代替。
cAdvisor:cAdvisor 是 Google 开源的容器资源监控和性能分析工具,它是专门为容器而生,本身也支持 Docker 容器。
kube-state-metrics:kube-state-metrics 通过监听 API Server 生成有关资源对象的状态指标,比如 Deployment、Node、Pod,需要注意的是 kube-state-metrics 只是简单提供一个 metrics 数据,并不会存储这些指标数据,所以我们可以使用 Prometheus 来抓取这些数据然后存储。
metrics-server:metrics-server 也是一个集群范围内的资源数据聚合工具,是 Heapster 的替代品,同样的,metrics-server 也只是显示数据,并不提供数据存储服务。(hpa需要metrics-server)
不过 kube-state-metrics 和 metrics-server 之间还是有很大不同的,二者的主要区别如下:
kube-state-metrics 主要关注的是业务相关的一些元数据,比如 Deployment、Pod、副本状态等
metrics-server 主要关注的是资源度量 API 的实现,比如 CPU、文件描述符、内存、请求延时等指标。
通过 Prometheus 来采集节点的监控指标数据,可以通过 node_exporter 来获取,顾名思义,node_exporter 就是抓取用于采集服务器节点的各种运行指标,目前 node_exporter 支持几乎所有常见的监控点
通过 DaemonSet 控制器来部署该服务,这样每一个节点都会自动运行一个这样的 Pod,如果我们从集群中删除或者添加节点后,也会进行自动扩展
prometheus-node-exporter.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: kube-mon
labels:
app: node-exporter
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
hostPID: true #共享宿主机进程管理,就是容器进程可见于宿主机进程,进程的id管理
hostIPC: true #共享宿主机命名空间,进程间通信
hostNetwork: true #使用宿主机网络
nodeSelector:
kubernetes.io/os: linux
containers:
- name: node-exporter
image: prom/node-exporter:v1.1.1
args:
- --web.listen-address=$(HOSTIP):9100
- --path.procfs=/host/proc
- --path.sysfs=/host/sys
- --path.rootfs=/host/root
- --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker/.+)($|/)
- --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
ports:
- containerPort: 9100
env:
- name: HOSTIP
valueFrom:
fieldRef:
fieldPath: status.hostIP
resources:
requests:
cpu: 150m
memory: 180Mi
limits:
cpu: 150m
memory: 180Mi
securityContext:
runAsNonRoot: true
runAsUser: 65534
volumeMounts:
- name: proc
mountPath: /host/proc
- name: sys
mountPath: /host/sys
- name: root
mountPath: /host/root
mountPropagation: HostToContainer
readOnly: true
tolerations:
- operator: "Exists"
volumes:
- name: proc
hostPath:
path: /proc
- name: dev
hostPath:
path: /dev
- name: sys
hostPath:
path: /sys
- name: root
hostPath:
path: /
由于我们要获取到的数据是主机的监控指标数据(设计进程,网络,而我们的 node-exporter 是运行在容器中的,所以我们在 Pod 中需要配置一些 Pod 的安全策略,这里我们就添加了 hostPID: true、hostIPC: true、hostNetwork: true 3个策略,用来使用主机的 PID namespace、IPC namespace 以及主机网络,这些 namespace 就是用于容器隔离的关键技术,要注意这里的 namespace 和集群中的 namespace 是两个完全不相同的概念。(要想容器即进程获取主机的数据,就得想办法跨过linux的namespace的隔离)
另外我们还将主机的 /dev、/proc、/sys这些目录挂载到容器中,这些因为我们采集的很多节点数据都是通过这些文件夹下面的文件来获取到的,比如我们在使用 top 命令可以查看当前 cpu 使用情况,数据就来源于文件 /proc/stat,使用 free 命令可以查看当前内存使用情况,其数据来源是来自 /proc/meminfo 文件。
另外由于我们集群使用的是 kubeadm 搭建的,所以如果希望 master 节点也一起被监控,则需要添加相应的容忍
使用宿主机网络,端口是9100,测试
curl 192.168.23.178:9100/metrics
加入prometheus监控队列:
kubectl apply -f prometheus-cm.yaml
curl重载prometheus的配置文件
(重载应该是服务的配置文件更改后,再重载,所以最好等一会让prometheus的配置文件热更新先)
当然如果你觉得上面的手动安装方式比较麻烦,我们也可以使用 Helm 的方式来安装:
helm upgrade --install node-exporter --namespace kube-mon stable/prometheus-node-exporter
服务发现
上面的监控室静态设置的,如果有新的节点加入,daemonset类型的node-exporter可以自动增加,但是prometheus的监控条例不会自动增加,要自己手动再去配置?这当然可以,但云与安生领域,本身就强调高自动化,这里就用到的服务发现(服务发现很多知识栈中都重要)
服务发现,那就不用自己直接深究pod的metrics端口,直接使用service即可
prometheus有服务发现的功能
在 Kubernetes 下,Promethues 通过与 Kubernetes API 集成,主要支持5中服务发现模式,分别是:Node、Service、Pod、Endpoints、Ingress。
同样
kubectl apply -f
curl
根据图中的endpoint来curl下:
[root@master ~]# curl 192.168.23.178:10250/metrics
Client sent an HTTP request to an HTTPS server.
回想下,我们配置的服务发现跟静态配置是不是有所不同,prometheus最重要的就是metrics端口设置,而服务发现不像是静态配置那样指定了metrics端口。
prometheus 去发现 Node 模式的服务的时候,访问的端口默认是 10250,而默认是需要认证的 https 协议才有权访问的,但实际上我们并不是希望去访问10250端口的 /metrics 接口,而是 node-exporter 绑定到节点的 9100 端口,所以我们应该将这里的 10250 替换成 9100(node-exporter收集节点的各种数据,prometheus访问node-exporter来获取metrics数据)
Prometheus 提供的 relabel_configs 中的 replace 能力,relabel 可以在
Prometheus 采集数据之前,通过 Target 实例的 Metadata 信息,动态重新写入 Label
的值。除此之外,我们还能根据 Target 实例的 Metadata 信息选择是否采集或者忽略该 Target
实例。比如我们这里就可以去匹配 address(常用)这个 Label 标签,然后替换掉其中的端口,如果你不知道有哪些 Label
标签可以操作的话,可以将鼠标移动到 Targets 的标签区域,其中显示的 Before relabeling 区域都是我们可以操作的标签
重写标签
source_labels:匹配要被relabel操作的已有标签,是个列表,记住用来匹配值的就行
target_label:最终的某个标签的样子,值是某个标签名,用来接收标签的最终值,这里是__address__
regex:正则表达式,用正则表达式的形式匹配source_labels中的值,方便操作
action:操作类型,这里是replace
replacement:正则表达式的形式来表示被action后的值的样子
${1}表示的是regex中的第一个RE正则表达式匹配到的内容,即ip
所以上面就是别拿到source_labels的值,接着对这些值正则表达式获取,然后replace操作,最后用target_label指定的标签来接收值的最终样子
插入些相关的正则表达式:
[]:匹配范围中的某个字符,[A-Za-z0-9]
^:在[]外,也就是多数情况是表示以什么RE字符开头,^$以$结尾符开头,其实一般就是表示空行,^(.*)或^\(.*\)$表示整行内容匹配。^在[]表示取反
+:匹配前一个RE字符一次或无穷次
?:非贪婪匹配,匹配前一个RE字符0次或一次,就是最多匹配一次
*:任意次
.:必定存在的某个字符
\d:就是[0-9]
():首先由群组匹配的意思,(00)表示有oo连续存在,(oo|ii)匹配oo或ii连续存在,其实也可以(o)单个匹配,效果和[]一样。另外还有捕获分组的意思,就是可以通过$1,$2等读取出来
(?:):非捕获分组,就是匹配的内容不保存,不能用${1}等获取,实操讲解
同样kubectl apply -f和curl重载服务配置
监控分类查询
我们采集的指标数据 Label 标签就只有一个节点的 hostname,这对于我们在进行监控分组分类查询的时候带来了很多不方便的地方,要是我们能够将集群中 Node 节点的 Label 标签也能获取到就很好了。这里我们可以通过 labelmap 这个属性来将 Kubernetes 的 Label 标签添加为 Prometheus 的指标数据的标签:
说白了就是为了方便对获取的数据进行更加细粒化的操作,对监控数据进行分组分类,自然使用标签最好,于是将kubernetes中标签全添加为prometheus获取的metrics指标数据的标签
添加了一个 action 为 labelmap,正则表达式是 _meta_kubernetes_node_label(.+)
的配置,这里的意思就是表达式中匹配都的数据也添加到指标数据的 Label 标签中去。
对于 kubernetes_sd_configs 下面可用的元信息标签如下:
__meta_kubernetes_node_name:节点对象的名称
_meta_kubernetes_node_label:节点对象中的每个标签
_meta_kubernetes_node_annotation:来自节点对象的每个注释
_meta_kubernetes_node_address:每个节点地址类型的第一个地址(如果存在)
关于 kubernets_sd_configs 更多信息可以查看官方文档:kubernetes_sd_config
另外由于 kubelet 也自带了一些监控指标数据,就上面我们提到的 10250 端口(上面的之所以访问出错是因为要求https访问:
- job_name: 'kubelet'
kubernetes_sd_configs:
- role: node
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
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
这里需要特别注意的是这里必须使用 https 协议访问,这样就必然需要提供证书,我们这里是通过配置 insecure_skip_verify: true 来跳过了证书校验,但是除此之外,要访问集群的资源,还必须要有对应的权限才可以,也就是对应的 ServiceAccount 的 权限允许才可以,我们这里部署的 prometheus 关联的 ServiceAccount 对象前面我们已经提到过了,这里我们只需要将 Pod 中自动注入的 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt 和 /var/run/secrets/kubernetes.io/serviceaccount/token 文件配置上,就可以获取到对应的权限了。
同样kubectl apply -f和curl让配置生效
可以看到我们上面添加的 kubernetes-kubelet 和 kubernetes-nodes 这两个 job 任务都已经配置成功了,而且二者的 Labels 标签都和集群的 node 节点标签保持一致了
同样也可以去graph下查看"node"相关的指标数据的图形
容器监控
容器,必然是cAdvisor,cAdvisor 已经内置在了 kubelet 组件之中,所以我们不需要单独去安装,cAdvisor 的数据路径为 /api/v1/nodes//proxy/metrics,但是我们不推荐使用这种方式,因为这种方式是通过 APIServer 去代理访问的,对于大规模的集群比如会对 APIServer 造成很大的压力,所以我们可以直接通过访问 kubelet 的 /metrics/cadvisor 这个路径来获取 cAdvisor 的数据, 同样我们这里使用 node 的服务发现模式,因为每一个节点下面都有 kubelet,自然都有 cAdvisor 采集到的数据指标
- job_name: 'kubernetes-cadvisor'
kubernetes_sd_configs:
- role: node
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
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
replacement: $1
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
replacement: /metrics/cadvisor # <nodeip>/metrics -> <nodeip>/metrics/cadvisor
target_label: __metrics_path_
action: replace_
apply和curl
(注意有些标签重写过了,但在target的label下看还是没变,现实的是重写前的,不要判断错误)
https://ip/metrics(端口默认是10250),这是监控kubelet用的,将metrics_path更改到/metrics/cadvisor就是获取cadvisor收集的指标数据
一定要注意这里也用了tls跳过的设置,如果没有这个设置,会报错显示ca中不含kubelet的节点的ip(用的是kubelet中的cadvisor来收集容器的监控数据)
container_cpu_usage_seconds_total 是容器累计使用的 CPU 时间,用它除以 CPU 的总时间,就可以得到容器的 CPU 使用率了:
首先计算容器的 CPU 占用时间,由于节点上的 CPU 有多个,所以需要将容器在每个 CPU 上占用的时间累加起来,Pod 在 1m 内累积使用的 CPU 时间为:(根据 pod 和 namespace 进行分组查询)
sum(rate(container_cpu_usage_seconds_total{image!=“”,pod!=“”}[1m])) by(namespace, pod)
然后计算 CPU 的总时间,这里的 CPU 数量是容器分配到的 CPU 数量,container_spec_cpu_quota 是容器的 CPU 配额,它的值是容器指定的 CPU 个数 * 100000,所以 Pod 在 1s 内 CPU 的总时间为:Pod 的 CPU 核数 * 1s:
sum(container_spec_cpu_quota{image!=“”, pod!=“”}) by(namespace, pod) /100000
由于 container_spec_cpu_quota 是容器的 CPU 配额,所以只有配置了 resource-limit CPU 的 Pod 才可以获得该指标数据。
先看看,promQL后续专门讲,需要深入学习
监控api-server
apiserver 作为 Kubernetes 最核心的组件,当然他的监控也是非常有必要的,对于 apiserver 的监控我们可以直接通过 kubernetes 的 Service 来获取
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 34h
上面这个 Service 就是我们集群的 apiserver 在集群内部的 Service 地址,要自动发现 Service 类型的服务,我们就需要用到 role 为 Endpoints 的 kubernetes_sd_configs
- job_name: 'kubernetes-apiservers'
kubernetes_sd_configs:
- role: endpoints
同样apply和curl
(获取数据异常,但自己配置正确的,不想等,可以考虑重启prometheus)
我们可以看到 kubernetes-apiservers 下面出现了很多实例,这是因为这里我们使用的是 Endpoints 类型的服务发现,所以 Prometheus 把所有的 Endpoints 服务都抓取过来了,同样的,上面我们需要的服务名为 kubernetes 这个 apiserver 的服务也在这个列表之中,那么我们应该怎样来过滤出这个服务来呢?
同样我们需要使用relabel_configs这个配置,只是我们这里不是使用 replace 这个动作了,而是 keep,就是只把符合我们要求的给保留下来
哪些才是符合我们要求的呢?我们可以把鼠标放置在任意一个 target 上,可以查看到Before relabeling里面所有的元数据,比如我们要过滤的服务是 default 这个 namespace 下面,服务名为 kubernetes 的元数据,所以这里我们就可以根据__meta_kubernetes_namespace 和 __meta_kubernetes_service_name 这两个元数据来 relabel,另外由于 kubernetes 这个服务对应的端口是 443,需要使用 https 协议,所以这里我们需要使用 https 的协议,对应的就需要将 ca 证书配置上
(https一般可以跳过验证,443端口(默认的https端口)必须使用https访问,还需要证书)
- job_name: 'kubernetes-apiservers'
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
#insecure_skip_verify: true #这里没有这个配置,亲测过,所以说endpoint类型的服务发现,要用https,没法跳过
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep
regex: default;kubernetes;https #多个匹配,均满足的进行action操作
#https是service对应的pod的port名称
另外如果我们要来监控其他系统组件,比如 kube-controller-manager、kube-scheduler 的话应该怎么做呢?由于 apiserver 服务 namespace 在 default 使用默认的 Service kubernetes,而其余组件服务在 kube-system 这个 namespace 下面,如果我们想要来监控这些组件的话,需要手动创建单独的 Service,其中 kube-sheduler 的指标数据端口为 10251,kube-controller-manager 对应的端口为 10252。端口情况一般会有不同,如果该应用及该pod没有提供原生的metrics端口,可以用exporter来实现,然后可以静态监控也可以像下面那样监控pod。
其实也可以直接通过静态设置来监控这两个组件,但如果集群高可用呢,所以还是用服务发现方式配置较好。
(service不一定要和pod同个命名空间)
监控pod
上面的 apiserver 实际上就是一种特殊的 Endpoints,现在我们同样来配置一个任务用来专门发现普通类型的 Endpoint,其实就是 Service 关联的 Pod 列表:
- job_name: 'kubernetes-endpoints'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
action: replace
target_label: __scheme__
regex: (https?)
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__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]
action: replace
target_label: kubernetes_name
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
同样的applyh和curl
注意我们这里在 relabel_configs 区域做了大量的配置,特别是第一个保留__meta_kubernetes_service_annotation_prometheus_io_scrape 为 true 的才保留下来,这就是说要想自动发现集群中的 Endpoint,就需要我们在 Service 的 annotation 区域添加 prometheus.io/scrape=true 的声明:
我们可以看到 kubernetes-endpoints 这一个任务下面只发现了两个服务,这是因为我们在 relabel_configs 中过滤了 annotation 有 prometheus.io/scrape=true 的 Service,而现在我们系统中只有这样一个 kube-dns 服务符合要求:
现在我们在之前创建的 redis 这个 Service 中添加上 prometheus.io/scrape=true:
由于 redis 服务的 metrics 接口在 9121 这个 redis-exporter 服务上面,所以我们还需要添加一个 prometheus.io/port=9121 这样的 annotations
apply文件,然后同样apply和curl
这样以后我们有了新的服务,服务本身提供了 /metrics 接口,我们就完全不需要用静态的方式去配置了,到这里我们就可以将之前配置的 redis 的静态配置去掉了。
kube-state-metrics(deploy副本数,pod状态,pod重启次数)
上面我们配置了自动发现 Endpoints 的监控,但是这些监控数据都是应用内部的监控,需要应用本身提供一个 /metrics 接口,或者对应的 exporter 来暴露对应的指标数据,但是在 Kubernetes 集群上 Pod、DaemonSet、Deployment、Job、CronJob 等各种资源对象的状态也需要监控,这也反映了使用这些资源部署的应用的状态。比如:
我调度了多少个副本?现在可用的有几个?
多少个 Pod 是 running/stopped/terminated 状态?
Pod重启了多少次?
我有多少 job 在运行中等等
通过查看前面从集群中拉取的指标(这些指标主要来自 apiserver 和 kubelet 中集成的 cAdvisor),并没有具体的各种资源对象的状态指标。对于 Prometheus 来说,当然是需要引入新的 exporter 来暴露这些指标,Kubernetes 提供了一个kube-state-metrics 就是我们需要的。
与 metric-server 的对比
metric-server 是从 APIServer 中获取cpu、内存使用率这种监控指标,并把他们发送给存储后端,如 influxdb 或云厂商,当前的核心作用是为 HPA 等组件提供决策指标支持。
kube-state-metrics 关注于获取 Kubernetes 各种资源的最新状态,如 deployment 或者 daemonset,metric-server仅仅是获取、格式化现有数据,写入特定的存储,实质上是一个监控系统。而 kube-state-metrics 是获取集群最新的指标。
像 Prometheus 这种监控系统,并不会去用 metric-server 中的数据,他都是自己做指标收集、集成的,但 Prometheus 可以监控 metric-server 本身组件的监控状态并适时报警,这里的监控就可以通过 kube-state-metrics 来实现,如 metric-server pod 的运行状态。
部署kube-state-metrics
kube-state-metrics 已经给出了在 Kubernetes 部署的 manifest 定义文件,我们直接将代码 Clone 到集群中(能用 kubectl 工具操作就行),不过需要注意兼容的版本:
git clone https://github.com/kubernetes/kube-state-metrics.git
这里下载可能下载有问题,如果没有配置好git,你有不熟悉git,用个本办法
(执行git config --global --unset http.proxy和git config --global --unset https.proxy,再git clone,可以重复多次git clone)
cd kube-state-metrics/examples/standard
默认的镜像为 gcr (m一般下不了)的,这里我们可以将 deployment.yaml 下面的镜像替换成 cnych/kube-state-metrics:v2.0.0-rc.0,此外我们上面为 Prometheus 配置了 Endpoints 的自动发现,所以我们可以给 kube-state-metrics 的 Service 配置上对应的 annotations 来自动被发现,然后直接创建即可:
apply该文件
kubectl apply -f .
[root@master ~]# git clone https://github.com/kubernetes/kube-state-metrics.git
正克隆到 'kube-state-metrics'...
remote: Enumerating objects: 26257, done.
remote: Counting objects: 100% (404/404), done.
remote: Compressing objects: 100% (157/157), done.
remote: Total 26257 (delta 236), reused 355 (delta 218), pack-reused 25853
接收对象中: 100% (26257/26257), 20.72 MiB | 2.81 MiB/s, done.
处理 delta 中: 100% (16889/16889), done.
[root@master ~]# ls
anaconda-ks.cfg kube-flannel.yml prometheus-deploy.yaml prometheus-redis.yaml 视频 音乐
initial-setup-ks.cfg kube-scheduler-svc.yaml prometheus-node-exporter prometheus-svc.yaml 图片 桌面
key.json kube-state-metrics prometheus-pvc.yaml 公共 文档
kubeadm.yaml prometheus-cm.yaml prometheus-rbac.yaml 模板 下载
[root@master ~]# cd kube-state-metrics/
[root@master kube-state-metrics]# ls
CHANGELOG.md Dockerfile go.sum LICENSE pkg SECURITY_CONTACTS VERSION
cloudbuild.yaml docs internal main.go README.md SECURITY.md
code-of-conduct.md examples jsonnet Makefile RELEASE.md tests
CONTRIBUTING.md go.mod kustomization.yaml OWNERS scripts tools
[root@master kube-state-metrics]# cd examples/
[root@master examples]# ls
autosharding prometheus-alerting-rules standard
[root@master examples]# cd standard/
[root@master standard]# ls
cluster-role-binding.yaml cluster-role.yaml deployment.yaml service-account.yaml service.yaml
[root@master standard]# kubectl apply -f .
clusterrolebinding.rbac.authorization.k8s.io/kube-state-metrics created
clusterrole.rbac.authorization.k8s.io/kube-state-metrics created
deployment.apps/kube-state-metrics created
serviceaccount/kube-state-metrics created
service/kube-state-metrics created
grafana介绍
数据采集到,可以再Prometheus 的 Dashboard 中进行了展示,但是明显可以感觉到 Prometheus 的图表功能相对较弱,所以一般情况下我们会一个第三方的工具来展示这些数据,今天我们要和大家使用到的就是 Grafana。
Grafana 是一个可视化面板,有着非常漂亮的图表和布局展示,功能齐全的度量仪表盘和图形编辑器,支持 Graphite、zabbix、InfluxDB、Prometheus、OpenTSDB、Elasticsearch 等作为数据源,比 Prometheus 自带的图表展示功能强大太多,更加灵活,有丰富的插件,功能更加强大。
一句话,用grafana可视化prometheus采集的数据
部署grafana
将 grafana 安装到 Kubernetes 集群
运行 grafana 容器的命令非常简单:docker run -d --name=grafana -p 3000:3000 grafana/grafana
我们这里要讲grafana运行在集群中,就是讲容器以pod方式运行:
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana
namespace: kube-mon
spec:
selector:
matchLabels:
app: grafana
template:
metadata:
labels:
app: grafana
spec:
volumes:
- name: storage
hostPath:
path: /data/k8s/grafana/
nodeSelector:
kubernetes.io/hostname: node1 #固定在这个节点
securityContext: #声明使用 root 用户运行
runAsUser: 0
containers:
- name: grafana
image: grafana/grafana:7.4.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
name: grafana
env:
- name: GF_SECURITY_ADMIN_USER #grafana管理员的用户和密码
value: admin
- name: GF_SECURITY_ADMIN_PASSWORD
value: admin321
readinessProbe:
failureThreshold: 10
httpGet:
path: /api/health
port: 3000
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 30
livenessProbe:
failureThreshold: 3
httpGet:
path: /api/health
port: 3000
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: 150m
memory: 512Mi
requests:
cpu: 150m
memory: 512Mi
volumeMounts:
- mountPath: /var/lib/grafana #grafana将dashboard和插件等的数据保存在这个目录下,有必要做个持久化
name: storage
---
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: kube-mon
spec:
type: NodePort
ports:
- port: 3000
selector:
app: grafana
apply
[root@master standard]# kubectl get svc -n kube-mon
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
grafana NodePort 10.97.115.4 <none> 3000:31165/TCP 28m
prometheus NodePort 10.96.132.171 <none> 9090:30087/TCP 45h
redis ClusterIP 10.105.131.143 <none> 6379/TCP,9121/TCP 28h
nodeport方式访问grafana
kubectl logs可以查看到grafana的密码和用户
我们这个地方配置的数据源是 Prometheus,我们这里 Prometheus 和 Grafana 都处于 kube-mon 这同一个 namespace 下面,所以我们这里的数据源地址:http://prometheus:9090(因为在同一个 namespace 下面所以直接用 Service 名也可以),然后其他的配置信息就根据实际情况了,比如 Auth 认证,我们这里没有,所以跳过即可,点击最下方的 Save & Test 提示成功证明我们的数据源配置正确:
(9090是prometheus这个service的集群内ip的端口)
prometheus数据源配置完成
部署监控集群的grafana插件
grafana 就有一个专门针对 Kubernetes 集群监控的插件:grafana-kubernetes-app,这里我们介绍一个功能更加强大的插件 DevOpsProdigy KubeGraf,它是 Grafana 官方的 Kubernetes 插件 的升级版本,该插件可以用来可视化和分析 Kubernetes 集群的性能,通过各种图形直观的展示了 Kubernetes 集群的主要服务的指标和特征,还可以用于检查应用程序的生命周期和错误日志。
要安装这个插件,需要到 grafana 的 Pod 里面去执行安装命令:
[root@master standard]# kubectl exec -it grafana-6c464596b8-n8xp6 /bin/bash -n kube-mon
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
bash-5.0# ls
LICENSE README.md bin plugins-bundled scripts
NOTICE.md VERSION conf public
bash-5.0# grafana-6c464596b8-n8xp6
bash: grafana-6c464596b8-n8xp6: command not found
bash-5.0# grafana-cli plugins install devopsprodigy-kubegraf-app
installing devopsprodigy-kubegraf-app @ 1.5.2
from: https://grafana.com/api/plugins/devopsprodigy-kubegraf-app/versions/1.5.2/download
into: /var/lib/grafana/plugins
✔ Installed devopsprodigy-kubegraf-app successfully
installing grafana-piechart-panel @ 1.6.2
from: https://grafana.com/api/plugins/grafana-piechart-panel/versions/1.6.2/download
into: /var/lib/grafana/plugins
✔ Installed grafana-piechart-panel successfully
Installed dependency: grafana-piechart-panel ✔
Restart grafana after installing plugins . <service grafana-server restart>
这里如果你下载失败,老是安装不了,可以自己收下载这两个插件的包,用volume方式挂载进grafana的pod的部署插件的目录下,在重启即可,详细操作和包的或包可以私信我(我一开始装的时候因为版本问题老是命令行装不了,才想到用这个方法挂载插件进去,希望你不要碰到这个坑)
安装了两个东西,其中一个是devopsprodigy-kubegraf的依赖或者说辅助准确点,没有这个饼图的显示会不正常。
安装完成后需要重启 grafana 才会生效,我们这里直接删除 Pod,重建即可。Pod 删除重建完成后插件就安装成功了。然后通过浏览器打开 Grafana 找到该插件,点击 enable 启用插件
点击 Set up your first k8s-cluster 创建一个新的 Kubernetes 集群:
URL 使用 Kubernetes Service 地址即可:https://kubernetes.default:443 Access
访问模式使用:Server(default) 由于插件访问 Kubernetes
集群的各种资源对象信息,所以我们需要配置访问权限,这里我们可以简单使用 kubectl 的 kubeconfig 来进行配置即可。 勾选
Auth 下面的 TLS Client Auth 和 With CA Cert 两个选项 其中 TLS Auth Details
下面的值就对应 kubeconfig 里面的证书信息。
比如我们这里的 kubeconfig 文件格式如下所示:
[root@master standard]# cat ~/.kube/config
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeU1EUXhNekUxTURReU5sb1hEVE15TURReE1ERTFNRFF5Tmxvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTmg1ClNHVHRHV051VlRNQ05BV2pudE5XUC9NTS90RG1LTnNtL08zYjFlK3U3YmJkbG03WjFQQ3RDQmdILzdSSDZGS0QKQ24rOFVzNHp6d0VXRVhWUnF3cmJZaURxaG5tUnN3N2IycWpVVENtUzRsK0grdEdLd0pSenhxMjVyNVM4UFFJNwpIcWRYaWZKUHoyU3JIM0RLc1VqL2lXNHROY01kSktqdXpzcXkxdWlWYU40blovd1pmWUF1Vms0bmY5R1ZEMDAxCkRpYlU5bTByYzhaWVpMQUZZQnhNVTBNV05Vc1kwUnhGamFMK0NtSXl6QXMySnRaUC9ubjNTT25ZRndlaDdhekQKcmlKaEJpTXhlTmNUUzRWbGtzTGsyaTN0S29zM1VzL2pWTVFIUExaeGNEVWVGeUs2bXRpWTczK05mTVNTNHFlVQpVcjhFdXd3VHpMdXo3NFdlU1hFQ0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZLWkRaOHJNZDZtS3JaVmVMRnFXY3lhWEdxRTBNQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFBaVZ4RmJqaGQvL1JaTVpPTnlYcWVDT2t0c09uNHNjZW4wR0M1aWdXZzNsOENleGNNZQpEMHptZEpMYWlvblVqOFFIUUVTV0YzWW9BZmErNzdaVklRVUdZT3hCb0trTTdtOFFSUVlKbXF0ZWdHb0E3VVBxCnBOUEc5SHU0Q3pRTWF0VHl0NTNhTklJM3MyVktta1p2eEpoUTVOZi9DSzZlL0gyK2pkT2YzT05RRE0wUEFjdkQKZVpQRVlySGcrbGozVUpWc2xGbUFrbWtFMnRzV2RIcVhreFhNQ0tOMFFueGJaME45Tk0zSTdnbmF5Nk5JSThncgpGdlRwUUg4OTVia1NQOTdUUVJ1enE2NFBVQTRhNGpKZEhqbW95bWRYdnB6T01TVWNpYUFadElzSEJFUGxhYUVZCkZnTTV4VUZqVFkyUWswQldwY0pWcTd0czdjSXVwcUxvMjNPZQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
server: https://192.168.23.178:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURFekNDQWZ1Z0F3SUJBZ0lJYytoN0g5Ky9BcEl3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TWpBME1UTXhOVEEwTWpaYUZ3MHlNekEwTVRNeE5UQTBNamRhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQW5GK2c2WjB4Y2hnS2NGTlcKNDhFaWF4N3Y0akJQL1kyVmtxRVhDREVjVUpGUUZPMWh0Yk4rbkxkR2swdEtob2M4WWx1S05MbkFnL01uUk9DZQpsZVVnaXE3bmI3dEgwWUZhT3BnRmdKYmRDVFRWMkF4dDZFRjFlT2ZsTDh2SXZuSktHbnVUZGxOamZaV0Q1Ynh6ClpkZzFjVTh4Y2NTV1lNUXg0dVVOQ3hsWlZaaGtXaStvQ05GWS9nRzZpWGhEZTRkMUQySmRkY3J4UTVhZnFicWgKblFjMVF0dWpwQVViZFFhMXZYbEI0VFJtRlJqRjVVZVJobzh1QXV6Y2d4QlpPVkQ1MGlZd1BmZmRPaDZCTzRmQwppT1AzUFlIV0ZlTzdKNGF5SVFjNTA0K0tsbS9sYUIvcmVzc3crZlMvVXlreUJRT1RhU3c3Z3pnYkdlSkYrVlhZCmpQM1FWd0lEQVFBQm8wZ3dSakFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0h3WURWUjBqQkJnd0ZvQVVwa05ueXN4M3FZcXRsVjRzV3BaekpwY2FvVFF3RFFZSktvWklodmNOQVFFTApCUUFEZ2dFQkFMdVZINSsyczFTM3ZyMnV2U3BDa1lNYU9hYXl4am9IdVI5cHVIV2F0cThsQk90dXgyb3hiM1dICm9yQkJuLzNQTmh6ajJrSGZxanIyYzdxV0tvS1ZtMnRsWFViK0Q0SWYyWm5zMENQdjlEZTd3ZEI2UEVxUElNNUUKNWxmVkthU3Bld3UzdmRTZWJ0WlhGS0MxNnVWY0NvNk8wQ1RhaXZ2RGpNalZaNDN0bEtvSGxwWm8vVVl1WS84eQp6eStPbFJraEtRVHpCVW1kOU5yNm9TYUNzdURGUk1kdytvR1YreEFxcFdrZ3Yyb3pxOVU3RlJ1a3hHb21uelBGCjVOREFHMGhSNlFRY2RHR2Q3K3lYaWJ1VkM4NzV6TFJrcTRHNXBnRFFndlVuMW1MbU1WeFhlbXMxcVhrdUxxWnEKK2kwL1BkOGdPU1BQVkxLdll1cHBPWGx1U1NvZWpDdz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBbkYrZzZaMHhjaGdLY0ZOVzQ4RWlheDd2NGpCUC9ZMlZrcUVYQ0RFY1VKRlFGTzFoCnRiTituTGRHazB0S2hvYzhZbHVLTkxuQWcvTW5ST0NlbGVVZ2lxN25iN3RIMFlGYU9wZ0ZnSmJkQ1RUVjJBeHQKNkVGMWVPZmxMOHZJdm5KS0dudVRkbE5qZlpXRDVieHpaZGcxY1U4eGNjU1dZTVF4NHVVTkN4bFpWWmhrV2krbwpDTkZZL2dHNmlYaERlNGQxRDJKZGRjcnhRNWFmcWJxaG5RYzFRdHVqcEFVYmRRYTF2WGxCNFRSbUZSakY1VWVSCmhvOHVBdXpjZ3hCWk9WRDUwaVl3UGZmZE9oNkJPNGZDaU9QM1BZSFdGZU83SjRheUlRYzUwNCtLbG0vbGFCL3IKZXNzdytmUy9VeWt5QlFPVGFTdzdnemdiR2VKRitWWFlqUDNRVndJREFRQUJBb0lCQVFDSGhhRlJsZmhxWjBkMAp2ZVdLRWJJZ29IbEowSkVpci9nM1VnRkNDajM5M1ZKSU1Nc0R6SUlvay9aSVpYSTdVUzBXR1R1WUlud2tYekZqClVrQmVyR3NkaGlQeUE1OWtoUFNMMzRDMVJ3cGRsanJXdVQ3dmtQdWNRMjJ2bEs5dnptRkd2bDY0cCtDTDBHNjEKUmpQRXlqTFc5c2xsVGZqOStFczcxME1Nb1hWNDVmVGl4R3JnbmJpUWtOeGk2SnA0L3VHbUlESUFWRnpEQ0w1KwpBWjVoOS9nL3RNL3NWU3BWRW1jQTF4YTAwVEJpT2dQOUx2RXp1MnB3N0dTVGZtTTlEVjlKa0l0d2FEYVU5U2xNCkJsb29GZ09QalNxM1FrWE9EUHo0WUdCa1RxSDJ4OFZpYlBYdFlMSG1GYkJDdXR0T2x2aUpwQUNPZ0U0d0ZMa0EKMjVBU2RrSWhBb0dCQU1GMmV4azJsYW01a24yajZ3TUw1Tk1rRCtzczBTOE5oaFd6U2s1cC9BV3VyOVpBTGlwSApNckNuVVpza1lFdEJCMjduamgvbkNuU3JEUHIwajJORDV1ano3aWh2SWVsYUlReGVBQStqdUVqcytwZGQ3akJWCnVKcVoxSmhDdlNLQ3hsYk5zMWYxandEM2xuV2lGaFBkUFF2TmVIY2xxRmYyVDhJWEx6anc1WnpmQW9HQkFNN3IKNjZvZkJpM0lmVFMyY0NOU1JPOFQ5YTI3a0tlaFpHM1g1Y0Q4MXFodnMvd0xHeC9CWDJRbGxrNGVFVDU3cE1kRQo0NDZ4QjlqQnhaL1htT0cyL3UvczJabWhNRG8zMDlxWXR6alBxVmZnbnNmSDJOcUlUcHNLeG1INW5CV2tpd0dwCmd6N3lJRlBJclpGbVBiZzl0TSt6NEZ4RDBBT0pQZDF6eitINVFjT0pBb0dCQUxKbUlXbkVyZ3BPOThrb0tRVXMKS0p5VGVxSnhONHplT2JFMUhlWmxVV09rREwzVGZUNjdYUUVOcWZFWWdEdWxBY2ZCZTR2cnIvYVo3V1JWdFF2aQpoOEoxZmo2SERJeDA4bGpITGVNK0pDZ0xiNkc1bW5XejlvR0tMVU1pOStvbEVlVGwxdVhicVQxSkV0UHkzY3ovCmpOV2ZhZURsTnZHMlR5STU0dS90SksrZEFvR0FQc096QlRSSjVJTkc3MEZUZkhOTTg1UURWb0pwVzErS0hnTjUKN25NRERhNXNVc3h2bXM5R0J4T2JwdkJaM0xFV2gxY3ZDeTVKcWdjRTBmcE1wbEtpdTlZRkh5T1VoR1JBdjdMSgpDTk44azJwUDMxdVFQS0ZSN3BkekcyN2lXSEpEcytwZUpDNi9mWXFHazI4RzA2YnIvSUNjVW9CRFRYOUlvNjZvClpPdjlkYmtDZ1lBcHhTQnBPLzk1cVRHeno5Z3lnanNNZ2pwY09XVE1nbWVCZldnM3dBNlNScjNKdGpJYzlNWG0Kclc0dVQxbGYvTGNKdDd5UW9RcGkyWHpMQ2xCWkxJb21mZ0dkSjR4dUdTRjMyYURLQmd2WU1OSlNIUjkvZ1N1SAptSnRnMGJZNEF1TDFwUDBrMU1PM3JzazNCdHY0NEROZzZPYmZ3OGtIQklRZHdVWGl4UGdxekE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
CA Cert 的值就对应 kubeconfig 里面的 进行 base64 解码过后的值;Client Cert 的值对应 进行 base64 解码过后的值;Client Key 的值就对应 进行 base64 解码过后的值
插入些base64使用细节,
对数据进行base64编码,注意去掉换行符
echo -n “” | base64
解码
echo “” | base64 -d
案件的部署和配置完成,是针对kubernetes的监控可视化,用的是prometheus的数据源
有各种的图形化界面,后续插入详细的图形化使用讲解的
导入 Dashboard
为了能够快速对系统进行监控,我们可以直接复用别人的 Grafana Dashboard,在 Grafana 的官方网站上就有很多非常优秀的第三方 Dashboard,我们完全可以直接导入进来即可。比如我们想要对所有的集群节点进行监控,也就是 node-exporter 采集的数据进行展示,这里我们就可以导入 https://grafana.com/grafana/dashboards/8919 这个 Dashboard。(造轮子精神应该有,但面对好的已有的产品适当的借用会更节省时间成本)
在侧边栏点击 “+”,选择 Import,在 Grafana Dashboard 的文本框中输入 8919 即可导入:
自定义图表
导入现成的第三方 Dashboard 或许能解决我们大部分问题,但是毕竟还会有需要定制图表的时候,这个时候就需要了解如何去自定义图表了。
同样在侧边栏点击 “+”,选择 Dashboard,然后选择 Add new panel 创建一个图表:
选择数据源
然后在 Metrics 区域输入我们要查询的监控 PromQL 语句,比如我们这里想要查询集群节点 CPU 的使用率:
(1 - sum(increase(node_cpu_seconds_total{mode=“idle”, instance=~“KaTeX parse error: Expected 'EOF', got '}' at position 6: node"}̲[1m])) by (inst…node”}[1m])) by (instance)) * 100
集群节点的 CPU 使用率实际上就相当于排除空闲 CPU 的使用率,所以我们可以优先计算空闲 CPU 的使用时长,除以总的 CPU 时长就是使用率了,用 1 减掉过后就是 CPU 的使用率了,如果想用百分比来表示的话则乘以 100 即可
这里有一个需要注意的地方是在 PromQL 语句中有一个 instance=~“$node” 的标签,其实意思就是根据 $node 这个参数来进行过滤,也就是我们希望在 Grafana 里面通过参数化来控制每一次计算哪一个节点的 CPU 使用率。
所以这里就涉及到 Grafana 里面的参数使用。点击页面顶部的 Dashboard Settings 按钮进入配置页面:
进入参数配置的页面
这里需要注意的是变量的名称 node 就是上面我们在 PromQL 语句里面使用的 $node 这个参数,这两个地方必须保持一致,然后最重要的就是参数的获取方式了,比如我们可以通过 Prometheus 这个数据源,通过 kubelet_node_name 这个指标来获取,在 Prometheus 里面我们可以查询该指标获取到的值为:
想要获取节点的名称,所以我们可以用正则表达式去匹配 node=xxx 这个标签,将匹配的值作为参数的值即可:
()是捕获分组,/.*.*/是规定的模板
这里就是先将prometheus的指标数据中的某个标签对应的键值对整个值用正则表达式匹配出来,在将捕获分组的值赋值给自定义的变量node,prometheus的指标数据前面设置了,采集时同时将kubernetes中的标签引入进来作为采集的数据的标签
在最下面的 Preview of values 里面会有获取的参数值的预览结果。除此之外,我们还可以使用一个更方便的 label_values 函数来获取,该函数可以用来直接获取某个指标的 label 值:
(更建议用这种方式来货区指标数据中的某个标签的值)
另外由于我们希望能够让用户自由选择一次性可以查询多少个节点的数据,所以我们将 Multi-value 以及 Include All option 都勾选上了,最后记得保存,保存后跳转到 Dashboard 页面就可以看到我们自定义的图表信息(点击apply):
节点选择,图形带下更改,各种可视化配置,都可以自定义的
PromQL
Prometheus 通过指标名称(metrics name)以及对应的一组标签(label)唯一定义一条时间序列。指标名称反映了监控样本的基本标识,而 label 则在这个基本特征上为采集到的数据提供了多种特征维度(指标名称只能由 ASCII 字符、数字、下划线以及冒号组成并必须符合正则表达式)。用户可以基于这些特征维度过滤、聚合、统计从而产生新的计算后的一条时间序列。
PromQL 是 Prometheus 内置的数据查询语言,其提供对时间序列数据丰富的查询,聚合以及逻辑运算能力的支持。并且被广泛应用在 Prometheus 的日常应用当中,包括对数据查询、可视化、告警处理。
很晦涩,下面一一深入解释,先记住,我们采集的数据时时间序列类型,metrics name是时间序列数据的基本特征或者说是基本描述,一般就是用来提取出指定的时间序列数据,label方便我们对这条时间序列数据进行更细粒化的数据处理
时间序列
前面我们通过 node-exporter 暴露的 metrics 服务,Prometheus 可以采集到当前主机所有监控指标的样本数据。
[root@master standard]# curl 10.244.0.2:9153/metrics
# HELP coredns_build_info A metric with a constant '1' value labeled by version, revision, and goversion from which CoreDNS was built.
# TYPE coredns_build_info gauge
coredns_build_info{goversion="go1.14.4",revision="f59c03d",version="1.7.0"} 1
# HELP coredns_cache_entries The number of elements in the cache.
# TYPE coredns_cache_entries gauge
coredns_cache_entries{server="dns://:53",type="denial"} 28
coredns_cache_entries{server="dns://:53",type="success"} 10
# HELP coredns_cache_hits_total The count of cache hits.
# TYPE coredns_cache_hits_total counter
coredns_cache_hits_total{server="dns://:53",type="denial"} 23
coredns_cache_hits_total{server="dns://:53",type="success"} 34
# HELP coredns_cache_misses_total The count of cache misses.
# TYPE coredns_cache_misses_total counter
coredns_cache_misses_total{server="dns://:53"} 306
# HELP coredns_dns_request_duration_seconds Histogram of the time (in seconds) each request took.
其中非 # 开头的每一行表示当前 node-exporter 采集到的一个监控样本:node_cpu_seconds_total 和 node_load1 表明了当前指标的名称、大括号中的标签则反映了当前样本的一些特征和维度、浮点数则是该监控样本的具体值。
时间序列格式:指标数据名称 标签 指标的值
<--------------- metric ---------------------><-timestamp -><-value->
<metric name>{<label name> = <label value>, ...}
Prometheus 会将所有采集到的样本数据以时间序列的方式保存在内存数据库中,并且定时保存到硬盘上。时间序列是按照时间戳和值的序列顺序存放的,我们称之为向量(vector),每条时间序列通过指标名称(metrics name)和一组标签集(labelset)命名。如下所示,可以将时间序列理解为一个以时间为 X 轴的数字矩阵:
在时间序列中的每一个点称为一个样本(sample),样本由以下三部分组成:
指标(metric):metric name 和描述当前样本特征的 labelsets ,反映被监控样本的含义,指标名称只能由 ASCII 字符、数字、下划线以及冒号组成并必须符合正则表达式
时间戳(timestamp):一个精确到毫秒的时间戳
样本值(value): 一个 float64 的浮点型数据表示当前样本的值
每个不同的 metric_name和 label 组合都称为时间序列,在 Prometheus的表达式语言中,表达式或子表达式包括以下四种类型之一:
瞬时向量(Instantvector):一组时间序列,每个时间序列包含单个样本,它们共享相同的时间戳。也就是说,表达式的返回值中只会包含该时间序列中的最新的一个样本值。而相应的这样的表达式称之为瞬时向量表达式。
区间向量(Rangevector):一组时间序列,每个时间序列包含一段时间范围内的样本数据,这些是通过将时间选择器附加到方括号中的瞬时向量(例如[5m]5分钟)而生成的。
标量(Scalar):一个简单的数字浮点值。
字符串(String):一个简单的字符串值。
区间向量的也就是顺势向量加上一个时间选择器,类比就是上图中的带箭头的点和带箭头的线
所有这些指标都是 Prometheus 定期从 metrics 接口那里采集过来的。采集的间隔时间的设置由 prometheus.yaml 配置中的 scrape_interval 指定。最大抓取间隔为30秒,这意味着至少每30秒就会有一个带有新时间戳记录的新数据点,这个值可能会更改,也可能不会更改,但是每隔 scrape_interval 都会产生一个新的数据点。(就是时间戳必然会改变,但同个时间序列即同个metrics标签和label对应的值相不相同都有可能)
指标类型
从存储上来讲所有的监控指标 metric 都是相同的(将时间序列数据存储在内存数据库和磁盘中),但是在不同的场景下这些 metric 又有一些细微的差异。 例如,在 Node Exporter 返回的样本中指标 node_load1 反应的是当前系统的负载状态,随着时间的变化这个指标返回的样本数据是在不断变化的。而指标 node_cpu_seconds_total 所获取到的样本数据却不同,它是一个持续增大的值,因为其反应的是 CPU 的累计使用时间,从理论上讲只要系统不关机,这个值是会一直变大。
为了能够帮助用户理解和区分这些不同监控指标之间的差异,Prometheus 定义了4种不同的指标类型:Counter(计数器)、Gauge(仪表盘)、Histogram(直方图)、Summary(摘要)。
注释中有说明该指标的类型
Counter
Counter (只增不减的计数器) 类型的指标其工作方式和计数器一样,只增不减。常见的监控指标,如 http_requests_total、node_cpu_seconds_total 都是 Counter 类型的监控指标。
(关键字total)
可以在应用程序中记录某些事件发生的次数,通过以时间序列的形式存储这些数据,我们可以轻松的了解该事件产生的速率变化。PromQL 内置的聚合操作和函数可以让用户对这些数据进行进一步的分析,例如,通过 rate() 函数(速率)获取 以当前时间为原点的过去的5分钟)HTTP 请求量的增长率:
rate(http_requests_total[5m])
查询当前系统中,访问量前 10 的 HTTP 请求:
topk(10, http_requests_total)
Gauge
与 Counter 不同,Gauge(可增可减的仪表盘)类型的指标侧重于反应系统的当前状态。因此这类指标的样本数据可增可减。常见指标如:node_memory_MemFree_bytes(主机当前空闲的内存大小)、node_memory_MemAvailable_bytes(可用内存大小)都是 Gauge 类型的监控指标。通过 Gauge 指标,用户可以直接查看系统的当前状态:
(偏重于当前的资源容量情况的反映)
node_memory_MemFree_bytes
对于 Gauge 类型的监控指标,通过 PromQL 内置函数 delta() 可以获取样本在一段时间范围内的变化情况。例如,计算 CPU 温度在两个小时内的差异:
delta(cpu_temp_celsius{host="zeus"}[2h])
还可以直接使用 predict_linear() 对数据的变化趋势进行预测。例如,预测系统磁盘空间在4个小时之后的剩余情况:
(预测过一定时间后数据的情况)
predict_linear(node_filesystem_free_bytes[1h], 4 * 3600)
时间单位是秒,这里是4小时候节点的磁盘空闲的一小时之间的数据情况(往前一小时)
Histogram 和 Summary
除了 Counter 和 Gauge 类型的监控指标以外,Prometheus 还定义了 Histogram 和 Summary 的指标类型。Histogram 和 Summary 主用用于统计和分析样本的分布情况。
一句话,分析分布情况或平均值
在大多数情况下人们都倾向于使用某些量化指标的平均值,例如 CPU 的平均使用率、页面的平均响应时间,这种方式也有很明显的问题,以系统 API
调用的平均响应时间为例:如果大多数 API 请求都维持在 100ms 的响应时间范围内,而个别请求的响应时间需要 5s,那么就会导致某些
WEB 页面的响应时间落到中位数上,而这种现象被称为长尾问题。为了区分是平均的慢还是长尾的慢,最简单的方式就是按照请求延迟的范围进行分组。例如,统计延迟在 0~10ms 之间的请求数有多少而
10~20ms 之间的请求数又有多少。通过这种方式可以快速分析系统慢的原因。Histogram 和 Summary
都是为了能够解决这样的问题存在的,通过 Histogram 和Summary 类型的监控指标,我们可以快速了解监控样本的分布情况。
例如:
# TYPE coredns_forward_request_duration_seconds histogram
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.00025"} 0
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.0005"} 0
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.001"} 0
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.002"} 0
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.004"} 0
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.008"} 18
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.016"} 59
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.032"} 67
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.064"} 68
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.128"} 68
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.256"} 68
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="0.512"} 68
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="1.024"} 68
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="2.048"} 68
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="4.096"} 68
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="8.192"} 68
coredns_forward_request_duration_seconds_bucket{to="192.168.23.2:53",le="+Inf"} 68
coredns_forward_request_duration_seconds_sum{to="192.168.23.2:53"} 0.7131689519999997
promQL查询
就是用promQL的聚合和函数对prometheus采集的样本数据进行查询
查询结构
比如只查询 node_cpu_seconds_total 则会返回所有采集节点的所有类型的 CPU 时长数据,当然如果数据量特别特别大的时候,直接在 Grafana 执行该查询操作的时候,则可能导致浏览器崩溃,因为它同时需要渲染的数据点太多,每个时间序列就是个样本点,total是总和,是基于累积的样本点得出的。(查询时不要轻易查total)
使用标签进行过滤查询:
标签过滤器支持的运算符
= 等于
!= 不等于
=~ 匹配正则表达式
!~ 与正则表达式不匹配
标签过滤器都位于指标名称后面的{}内
node_cpu_seconds_total{instance="ydzs-master"}
过滤数节点为master的cpu的使用总时长
此外我们还可以使用多个标签过滤器,以逗号分隔。多个标签过滤器之间是 AND 的关系,所以使用多个标签进行过滤,返回的指标数据必须和所有标签过滤器匹配。
例如如下查询语句将返回所有以 ydzs-为前缀的节点的并且是 idle 模式下面的节点 CPU 使用时长指标:
node_cpu_seconds_total{instance=~"ydzs-.*", mode="idle"}
范围选择器
我们可以通过将时间范围选择器([])附加到查询语句中,指定为每个返回的区间向量样本值中提取多长的时间范围(同一指标数据或者说样本不同时间戳的情况)。每个时间戳的值都是按时间倒序记录在时间序列中的,该值是从时间范围内的时间戳获取的对应的值。
时间范围通过数字来表示,单位可以使用以下其中之一的时间单位:
s - 秒 m - 分钟 h - 小时 d - 天 w - 周 y - 年
一般用天以下的单位
这是因为我们 Prometheus 中配置的抓取间隔是15秒,所以,我们从图中的 @ 符号后面的时间戳可以看出,它们之间的间隔基本上就是15秒(单位是纳秒)
这是因为现在每一个时间序列中都有多个时间戳多个值,所以没办法渲染,必须是标量或者瞬时向量才可以绘制图形。
不过通常区间向量都会应用一个函数后变成可以绘制的瞬时向量,Prometheus 中对瞬时向量和区间向量有很多操作的函数,不过对于区间向量来说最常用的函数并不多,使用最频繁的有如下几个函数:
rate(): 计算整个时间范围内区间向量中时间序列的每秒平均增长率 ,就是增长数除总数
irate():仅使用时间范围中的最后两个数据点来计算区间向量中时间序列的每秒平均增长率,irate
只能用于绘制快速变化的序列,在长期趋势分析或者告警中更推荐使用 rate 函数
increase():计算所选时间范围内时间序列的增量,它基本上是速率乘以时间范围选择器中的秒数
带时间选择器的区间向量不能直接绘制图形,要用函数转换成向量顺时
我们选择的时间范围持续时间将确定图表的粒度,比如,持续时间 [1m] 会给出非常尖锐的图表,从而很难直观的显示出趋势来,看起来像这样:
有的时候可能想要查看5分钟前或者昨天一天的区间内的样本数据,这个时候我们就需要用到位移操作了,位移操作的关键字是 offset,比如我们可以查询30分钟之前的 master 节点 CPU 的空闲指标数据:
同样位移操作也适用于区间向量,比如我们要查询昨天的前5分钟的 CPU 空闲增长率:
关联查询或者说聚合查询
Prometheus 没有提供类似与 SQL 语句的关联查询的概念,但是我们可以通过在 Prometheus 上使用 运算符 来组合时间序列,可以应用于多个时间序列或标量值的常规计算、比较和逻辑运算。
如果将运算符应用于两个瞬时向量,则它将仅应用于匹配的时间序列,当且仅当时间序列具有完全相同的标签集的时候,才认为是匹配的。当表达式左侧的每个序列和右侧的一个序列完全匹配的时候,在序列上使用这些运算符才可以实现一对一匹配。
比如如下的两个瞬时向量:
node_cpu_seconds_total{instance="ydzs-master", cpu="0", mode="idle"}
和
node_cpu_seconds_total{instance="ydzs-node1", cpu="0", mode="idle"}
如果我们对这两个序列做加法运算来尝试获取 master 和 node1 节点的总的空闲 CPU 时长,则不会返回任何内容了:
两个指标数据进行运算,默认标签得完全匹配才行
这是因为这两个时间序列没有完全匹配标签。我们可以使用 on 关键字指定只希望在 mode 标签上进行匹配,就可以计算出结果来:
on 关键字只能用于一对一的匹配中,如果是多对一或者一对多的匹配情况下,就不行了
在 Prometheus 中还有很多 聚合操作,所以,如果我们真的想要获取节点的 CPU 总时长,我们完全不用这么操作,使用 sum 操作要简单得多:
sum(node_cpu_seconds_total{mode="idle"}) by (instance)
要解决这个问题,我们可以使用 group_left 或group_right 关键字。这两个关键字将匹配分别转换为多对一或一对多匹配。左侧和右侧表示基数较高的一侧。因此,group_left 意味着左侧的多个序列可以与右侧的单个序列匹配。结果是,返回的瞬时向量包含基数较高的一侧的所有标签,即使它们与右侧的任何标签都不匹配。
瞬时向量和标量结合
此外我们还可以将瞬时向量和标量值相结合,这个很简单,就是简单的数学计算,比如:
node_cpu_seconds_total{instance="ydzs-master"} * 10
会为瞬时向量中每个序列的每个值都剩以10。这对于计算比率和百分比得时候非常有用。
除了 * 之外,其他常用的算数运算符当然也支持:+、-、*、/、%、^。 还有其他的比较运算符:==、!=、>、<、>=、<=。
逻辑运算符:and、or、unless,不过逻辑运算符只能用于瞬时向量之间。
以上主要是为了理解promQL常规的语法,和聚合运算以及函数,很少会在prometheus的webui中使用,一般是在grafana下做可视化用。
Alertmanager介绍
Prometheus 包含一个报警模块,就是AlertManager,Alertmanager 主要用于接收 Prometheus 发送的告警信息,它支持丰富的告警通知渠道,而且很容易做到告警信息进行去重,降噪,分组等,是一款前卫的告警通知系统。
通过在 Prometheus 中定义告警规则,Prometheus会周期性的对告警规则进行计算(就是前面的告警规则评估频率),如果满足告警触发条件就会向Alertmanager 发送告警信息。
在 Prometheus 中一条告警规则主要由以下几部分组成:
告警名称:用户需要为告警规则命名,当然对于命名而言,需要能够直接表达出该告警的主要内容
告警规则:告警规则实际上主要由 PromQL进行定义,其实际意义是当表达式(PromQL)查询结果持续多长时间(During)后触发告警
还可以成立告警分组,用group对相同类型的告警进行统一的定义
Alertmanager 作为一个独立的组件,负责接收并处理来自 Prometheus Server 的告警信息。Alertmanager 可以对这些告警信息进行进一步的处理,比如当接收到大量重复告警时能够消除重复的告警信息,同时对告警信息进行分组并且路由到正确的通知方,Prometheus 内置了对邮件、Slack 多种通知方式的支持,同时还支持与 Webhook 的集成,以支持更多定制化的场景。例如,目前 Alertmanager 还不支持钉钉,用户完全可以通过 Webhook 与钉钉机器人进行集成,从而通过钉钉接收告警信息。同时 AlertManager 还提供了静默和告警抑制机制来对告警通知行为进行优化。
部署Alertmanager
也是用.yml为配置文件,可以下载二进制文件下来直接$./alertmanager --config.file=simple.yml运行
这里它与prometheus一样部署在kubernetes中Docker 镜像的方式来安装,使用的镜像是:prom/alertmanager:v0.2
以邮箱方式
alertmanager-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: alert-config
namespace: kube-mon
data:
config.yml: |-
global:
# 当alertmanager持续多长时间未接收到告警后标记告警状态为 resolved
resolve_timeout: 5m #持续5m为收到告警信息告警状态变成resolvd,以解决
# 配置邮件发送信息
smtp_smarthost: 'smtp.163.com:25' #用于发送电子邮件的邮局,465才是加密端口
smtp_from: 'ych_1024@163.com' #自己在邮局注册的邮箱账号
smtp_auth_username: 'ych_1024@163.com' #邮件验证名,也是邮箱账号(用来验证发件账号的用户名和信息)
smtp_auth_password: '<邮箱授权码>' #邮箱账号授权码注意是授权码,区邮箱后台开启查看授权码
smtp_hello: '163.com' #向smtp服务器标识的默认主机名
smtp_require_tls: false #不用tls认证
# 所有报警信息进入后的根路由,用来设置报警的分发策略
route:
# 这里的标签列表是接收到报警信息后的重新分组标签,例如,接收到的报警信息里面有许多具有 cluster=A 和 alertname=LatncyHigh 这样的标签的报警信息将会批量被聚合到一个分组里面,但只有一种标签则一个标签一个分组,或者两个标签值不同的有多种的,又是另外一种分组情况,分组其实就是讲相同类型或相似类型的告警信息放到同一组李变,一般通过告警名来判断
group_by: ['alertname', 'cluster'] #这里最好也用来team这个组中的自定义标签调理会更清晰
# 当一个新的报警分组被创建后,需要等待至少 group_wait 时间来初始化通知,这种方式可以确保您能有足够的时间为同一分组来获取多个警报,然后一起触发这个报警信息。
group_wait: 30s
# 相同的group之间发送告警通知的时间间隔(这里测试下,频率设置较大)
group_interval: 30s
# 如果一个报警信息已经发送成功了,等待 repeat_interval 时间来重新发送他们,不同类型告警发送频率需要具体配置(上面的看看任意理解成只是生成了告警信息,这个是发送给用户)
repeat_interval: 1h
# 默认的receiver:如果一个报警没有被一个route匹配,则发送给默认的接收器
receiver: default
# 上面所有的属性都由所有子路由继承,并且可以在每个子路由上进行覆盖。
routes:
- receiver: email
group_wait: 10s
match:
team: node
receivers:
- name: 'default'
email_configs: #接受者,这里也是邮箱,也可以是同一个邮箱账号,就比如alertmanager用上面的163账号登录邮局发件给自己即同样的163账号接收
#邮件流程:发件账号->邮局->收件人账号
#不论是代码操作授权码还是这里使用密码用户名,只要是发件就像以前那个年代,发件人得有邮局开的账号,用这个账号区邮局让邮局帮忙发件,所以发件得配置邮局,收件不用配置邮局
- to: '517554016@qq.com'
send_resolved: true # 接受告警恢复的通知
- name: 'email'
email_configs:
- to: '517554016@qq.com'
send_resolved: true
apply
详细的邮箱地址密码等改自己的
一个发件一个是收件的功能
smtp发件服务器网址
授权码一般用于代码中操作比如python
分组机制可以将详细的告警信息合并成一个通知,在某些情况下,比如由于系统宕机导致大量的告警被同时触发,在这种情况下分组机制可以将这些被触发的告警合并为一个告警通知,避免一次性接受大量的告警通知,而无法对问题进行快速定位。
一句话就是将类似的报警信息搞到一个告警分组里,就一个告警通知,避免类似问题的告警通知冗余
容器方式部署alertmanager:
alertmanager-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: alertmanager
namespace: kube-mon
labels:
app: alertmanager
spec:
selector:
matchLabels:
app: alertmanager
template:
metadata:
labels:
app: alertmanager
spec:
volumes:
- name: alertcfg
configMap:
name: alert-config
containers:
- name: alertmanager
image: prom/alertmanager:v0.21.0
imagePullPolicy: IfNotPresent
args:
- "--config.file=/etc/alertmanager/config.yml" #配置文件路径
ports:
- containerPort: 9093
name: http
volumeMounts:
- mountPath: "/etc/alertmanager" #将配置文件的cm挂载到这里
name: alertcfg
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 100m
memory: 256Mi
apply
创建一个对应的 Service 对象:
apiVersion: v1
kind: Service
metadata:
name: alertmanager
namespace: kube-mon
labels:
app: alertmanager
spec:
selector:
app: alertmanager
type: NodePort
ports:
- name: web
port: 9093
targetPort: http
apply
AlertManager 的容器启动起来后,我们还需要在 Prometheus 中配置下 AlertManager 的地址,让 Prometheus 能够访问到 AlertManager,在 Prometheus 的 ConfigMap 资源清单中添加如下配置:
alerting:
alertmanagers:
- static_configs:
- targets: ["alertmanager:9093"]
apply reload
prometheus中指定altermanager这个服务的地址就可以用alertmanager来处理(分组降噪等)和通知用户,也就是说以上这些是配置在alertmanager中,而告警信息和告警信息生成的条件是prometheus中定义
报警规则(prometheus中配置)
在alertmanager中配置的规则说是通知规则会更恰当
现在我们只是把 AlertManager 容器运行起来了,也和 Prometheus 进行了关联,但是现在我们并不知道要做什么报警,因为没有任何地方告诉我们要报警,所以我们还需要配置一些报警规则来告诉我们对哪些数据进行报警。
警报规则允许你基于 Prometheus 表达式语言的表达式来定义报警报条件,并在触发警报时发送通知给外部的接收者。
在prometheus的报警模块指定报警规则文件
rule_files:
- /etc/prometheus/rules.yml
rule_files 就是用来指定报警规则的,这里我们同样将 rules.yml 文件用 ConfigMap 的形式(cm中的键就是文件名)挂载到 /etc/prometheus 目录下面即可
修改prometheus-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: kube-mon
data:
prometheus.yml: |
global:
scrape_interval: 15s
scrape_timeout: 15s
evaluation_interval: 30s # 默认情况下每分钟对告警规则进行计算
alerting:
alertmanagers:
- static_configs:
- targets: ["alertmanager:9093"] #alertmanager的svc的地址
rule_files:
- /etc/prometheus/rules.yml
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'coredns'
static_configs:
- targets: ['10.244.0.2:9153', '10.244.0.3:9153']
- job_name: 'redis'
static_configs:
- targets: ['redis:9121']
- job_name: 'redis2'
static_configs:
- targets: ['10.244.2.2:9121']
- job_name: 'clusternode'
static_configs:
- targets: ['192.168.23.178:9100','192.168.23.177:9100','192.168.23.179:9100']
- job_name: 'node'
kubernetes_sd_configs: #sd,service discover,服务发现配置
- role: node #node类型的服务发现
relabel_configs:
- source_labels: [__address__]
regex: '(.*):10250'
replacement: '${1}:9100'
target_label: __address__
action: replace
- action: labelmap #relabel_configs的配置列表中的一个值,都属于重写标签的配置下
regex: __meta_kubernetes_node_label_(.+)
- job_name: 'kubelet'
kubernetes_sd_configs:
- role: node
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
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- job_name: 'kubernetes-cadvisor'
kubernetes_sd_configs:
- role: node
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
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
replacement: $1
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
replacement: /metrics/cadvisor # <nodeip>/metrics -> <nodeip>/metrics/cadvisor
target_label: __metrics_path_
action: replace
- job_name: 'kubernetes-apiservers'
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep
regex: default;kubernetes;https
#- job_name: 'kube-scheduler'
# kubernetes_sd_configs: #sd,service discover,服务发现配置
# - role: endpoints #endpoint类型的服务发现
# scheme: https
# tls_config:
# ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
# relabel_configs:
# - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name]
# action: keep
# regex: kube-system;schedulersvc
# - action: labelmap #relabel_configs的配置列表中的一个值,都属于重写标签的配置下
# regex: __meta_kubernetes_node_label_(.+)
# replacement: $1
- job_name: 'kubernetes-endpoints'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
action: replace
target_label: __scheme__
regex: (https?)
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__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]
action: replace
target_label: kubernetes_name
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
rules.yml: | #定义规则文件的内容
groups: #对告警规则进行分组
- name: test-node-mem #告警分组的名字
rules: #确切的告警规则
- alert: NodeMemoryUsage #告警规则的名字
expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100 > 20 #是用于进行报警规则 PromQL 查询语句
for: 2m #评估等待时间(Pending Duration),用于表示只有当触发条件持续一段时间后才发送告警,在等待期间新产生的告警状态为pending
labels: #自定义标签,允许用户指定额外的标签列表,把它们附加在告警上(可用作告警实例的身份表示,告警实例也自带了些标签,下图所示)
team: node #组的标签,也会是每条这里产生的告警信息的标签
annotations: #指定了另一组标签,它们不被当做告警实例的身份标识,它们经常用于存储一些额外的信息,用于报警信息的展示之类的
summary: "{{$labels.instance}}: High Memory usage detected"
description: "{{$labels.instance}}: Memory usage is above 20% (current value is: {{ $value }}"
apply reload
{{}}变量处理要用这个
$labels可以获取规则中定义的标签或告警信息自带的标签的值
$value当前的样本数据的值
应用的配置文件一般两种方式处理,一种是cm(其实也是借用volume,cm也是一种volume卷类型),一种是直接用volume挂载进去(一般不具备热更新功能),多数情况下挂载文件尤其是配置文件,用cm较好。
prometheus的webui的Alert
for这个参数主要用于降噪,很多类似响应时间这样的指标都是有抖动的,通过指定 Pending
> Duration,我们可以过滤掉这些瞬时抖动,可以让我们能够把注意力放在真正有持续影响的问题上。
> 为了让告警信息具有更好的可读性,Prometheus 支持模板化 label 和 annotations 中的标签的值,通过 $labels.变量 可以访问当前告警实例中指定标签的值,$value 则可以获取当前 PromQL 表达式计算的样本
值。
页面中出现了我们刚刚定义的报警规则信息,而且报警信息中还有状态显示,一个报警信息在生命周期内有下面3种状态:
pending:表示在设置的阈值时间范围内被激活了
firing: 表示超过设置的阈值时间被激活了
inactive: 表示当前报警信息处于非活动状态
同时对于已经 pending 或者 firing 的告警,Prometheus 也会将它们存储到时间序列ALERTS{}中。当然我们也可以通过表达式去查询告警实例:
ALERTS{alertname="<alert name>", alertstate="pending|firing", <additional alert labels>}
样本值为1表示当前告警处于活动状态(pending 或者 firing),当告警从活动状态转换为非活动状态时,样本值则为0。
我们这里的状态现在是 firing 就表示这个报警已经被激活了,我们这里的报警信息有一个 team=node 这样的标签,而最上面我们配置 alertmanager 的时候就有如下的路由配置信息了:
routes:
- receiver: email #告警接收者是email,用email来通知用户
group_wait: 10s #当一个新的告警组(告警规则)创建时,至少等待10s在一起通知
match: #匹配告警信息
team: node #有这个标签的告警组的发送方式准确点说应该是发送给予对焊这个标签的告警信息而不是告警组的判断
即一个奇怪的现象,由于些原因,虚拟机未正确关闭,主机就重启了,然后用ip:端口方式访问prometheus,如果ip是master会访问失败,其他节点可以。大概定位问题是连接master的流量被档住了
我们可以看到收到的邮件内容中包含一个 View In AlertManager 的链接,我们同样可以通过 NodePort 的形式去访问到 AlertManager 的 Dashboard 页面
有个问题alertmanager的配置文件修改后,即使apply了cm,alertmanager依旧不会立刻生效新配置,要重启下pod你也介意百度查下reload的方法
通过 <任一Node节点>:31970 进行访问,我们就可以查看到 AlertManager 的 Dashboard 页面,在这个页面中我们可以进行一些操作,比如过滤、分组等等,里面还有两个新的概念:Inhibition(抑制) 和 Silences(静默)。
Inhibition:如果某些其他警报已经触发了,则对于某些警报,Inhibition 是一个抑制通知的概念。例如:一个警报已经触发,它正在通知整个集群是不可达的时,Alertmanager 则可以配置成关心这个集群的其他警报无效。这可以防止与实际问题无关的数百或数千个触发警报的通知,Inhibition 需要通过上面的配置文件进行配置。(在一个大问题面前,其底下的小问题都不管先)
Silences:静默是一个非常简单的方法,可以在给定时间内简单地忽略所有警报。Silences 基于 matchers配置,类似路由树。来到的警告将会被检查,判断它们是否和活跃的 Silences 相等或者正则表达式匹配。如果匹配成功,则不会将这些警报发送给接收者。(给定时间内,对到来的警告进行判断,是否符合静默,符合则不通知用户)
由于全局配置中我们配置的 repeat_interval: 1h,所以正常来说,上面的测试报警如果一直满足报警条件(内存使用率大于20%)的话,那么每1小时我们就可以收到一条报警邮件。
**一条告警产生后,还要经过 Alertmanager 的分组、抑制处理、静默处理、去重处理和降噪处理最后再发送给接收者。**这个过程中可能会因为各种原因会导致告警产生了却最终没有进行通知,可以通过下图了解整个告警的生命周期:
流程整理:
prometheus持续监控,的配置文件定义了告警分组和promQL,比如予以是promQL的样本值超过多少就产生告警信息,告警信息中一般会有多条指标数据,就跟promQL查询出来的一样,这些指标数据同意为一条告警信息,对这条规则会定期评估运算,告警信息产生会经过一个for指定降噪功能的pending时间判断,如果指定降噪时间内监控,一致产生告警信息(就是符合promQL),那么告警信息会从杠杠的pending状态变为firing状态,此时firing的告警信息不同于其他告警信息它是要被进一步处理的,比如prometheus的配置文件定义了告警信息名称,alertmanager的配置文件可以在这里用groupby对拥有同样名称的告警信息划分到一个分组内
,注意期间的监控产生的处于firing的告警信息都会被进一步处理,告警分组初次创建要等待指定时间后,由route和routes判断发送给那个reciver,同样类型的告警分组发送成功后需要在指定时间间隔后才能进一步发送,监控期间promQ的样本值不满足告警信息产生条件会由firing变成inative状态。
webhook接收器
上面我们配置的是 AlertManager 自带的邮件报警模板,我们也说了 AlertManager 支持很多中报警接收器,比如 slack、微信之类的,其中最为灵活的方式当然是使用 webhook 了,我们可以定义一个 webhook 来接收报警信息,然后在 webhook 里面去进行处理,需要发送怎样的报警信息我们自定义就可以。
实现了一个简单的 webhook 程序,代码仓库地址:github.com/cnych/alertmanager-dingtalk-hook
将webhook部署到集群中
dingtalk-hook.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dingtalk-hook
namespace: kube-mon
spec:
selector:
matchLabels:
app: dingtalk-hook
template:
metadata:
labels:
app: dingtalk-hook
spec:
containers:
- name: dingtalk-hook
image: cnych/alertmanager-dingtalk-hook:v0.3.2 #docker仓库
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5000
name: http
env:
- name: PROME_URL
value: k8s.qikqiak.com:30087 #prometheus服务的地址
- name: LOG_LEVEL #日志级别,设置成 debug 可以看到 AlertManager WebHook 发送的数据,方便调试使用,不需调试可以不设置该环境变量
value: debug
- name: ROBOT_TOKEN #钉钉机器人的token
valueFrom:
secretKeyRef:
name: dingtalk-secret
key: token
- name: ROBOT_SECRET #为钉钉机器人的安全设置密钥,机器人安全设置页面,加签一栏下面显示的 SEC 开头的字符串
valueFrom:
secretKeyRef:
name: dingtalk-secret
key: secret
resources:
requests:
cpu: 50m
memory: 100Mi
limits:
cpu: 50m
memory: 100Mi
---
apiVersion: v1
kind: Service
metadata:
name: dingtalk-hook
namespace: kube-mon
spec:
selector:
app: dingtalk-hook
ports:
- name: hook
port: 5000
targetPort: http
下面这张图也勾选上加签
webhook中包含了访问用的accesstoken
我这里只用了关键字和加密签名,满足这两个就可以用机器人推送消息
(这里改一下,关键字的化,需要你发送给机器人的消息中带有关键字,为了方便这里取消关键字,保存了设置可以点击机器人头像更改配置)
上面我们声明的 ROBOT_TOKEN 和 ROBOT_SECRET 环境变量,由于这是一个相对于私密的信息,所以我们这里从一个 Secret 对象中去获取,通过如下命令创建一个名为 dingtalk-secret 的 Secret 对象,然后部署上面的资源对象即可:
(钉钉机器人token和安全密码)
kubectl create secret generic dingtalk-secret --from-literal=token=<钉钉群聊的机器人TOKEN> --from-literal=secret=<钉钉群聊机器人的SECRET> -n kube-mon
一般都是http就够了
team=node标签(filesystem也行,应该是promQL语句自带的标签)
可以暂时注释了email这个receiver和routes(通过标签匹配,如果有多个receiver符合,应该都会发送,你可以测试下)
改下时间间隔,方便测试
用logs查看dingtalk的日志可以看详情
解释下这些字段
alertmanager的配置文件cm更改后,总觉得当前版本的这个alertmanager没有及时reload配置,所以我这边采用的是删除alertmanager这个deploy来重启alertmanager
自定义模板
告警通知使用的是默认模版,因为它已经编译到二进制包了,所以我们不需要额外配置。如果我们想自定义模版,这又该如何配置呢?
步骤一: 下载官方默认模版
wget https://raw.githubusercontent.com/prometheus/alertmanager/master/template/default.tmpl
步骤二: 根据自己的需求修改模版,主要是下面这一段
define "email.default.html"
.... // 修改内容
end
步骤三: 修改 alertmanger.yml,添加 templates 配置参数
templates:
- './template/*.tmpl' # 自定义模版路径
最后保存重新加载配置即可。
除了在现有的模板上进行修改,也可以自己百度找些模板,最后放到指定路径下即可
记录规则
通过 PromQL 可以实时对 Prometheus 中采集到的样本数据进行查询,聚合以及其它各种运算操作。而在某些 PromQL 较为复杂且计算量较大时,直接使用 PromQL 可能会导致 Prometheus 响应超时的情况。这时需要一种能够类似于后台批处理的机制在后台完成这些复杂运算的计算,对于使用者而言只需要查询这些运算结果即可。Prometheus 通过Recoding Rule 规则支持这种后台计算的方式,可以实现对复杂查询的性能优化,提高查询效率。
(就是promQL查询量计算量很大时,如果在前台运算会抢占prometheus的大量资源,导致prometheus响应超时,这里通过有recoding rule的后台计算方式来处理,提前定期对promQL进行运算)
在 Prometheus 配置文件中,我们可以通过 rule_files 定义 recoding rule 规则文件的访问路径。
rule_files:
[ - <filepath_glob> ... ]
每一个规则文件通过以下格式进行定义:
groups:
[ - <rule_group> ]
一个简单的规则文件可能是这个样子的:
groups:
- name: example
rules:
- record: job:http_inprogress_requests:sum
expr: sum(http_inprogress_requests) by (job)
rule_group 的具体配置项如下所示:
分组的名称,在一个文件中必须是唯一的
name: <string>
评估分组中规则的频率
[ interval: <duration> | default = global.evaluation_interval ]
rules:
[ - <rule> ... ]
。
与告警规则一致,一个 group 下可以包含多条规则 rule。
输出的时间序列名称,必须是一个有效的 metric 名称
record: <string>
要计算的 PromQL 表达式,每个评估周期都是在当前时间进行评估的,结果记录为一组新的时间序列,metrics 名称由 record 设置
expr: <string>
添加或者覆盖的标签
labels:
[ <labelname>: <labelvalue> ]
根据规则中的定义,Prometheus 会在后台完成 expr 中定义的 PromQL 表达式计算,并且将计算结果保存到新的时间序列 record 中,同时还可以通过 labels 标签为这些样本添加额外的标签。
这些规则文件的计算频率与告警规则计算频率一致,都通过 global.evaluation_interval 进行定义:
global:
[ evaluation_interval: <duration> | default = 1m ]
Thanos
前面我们已经学习了 Prometheus 的使用,了解了基本的 PromQL 语句以及结合 Grafana 来进行监控图表展示,通过 Alertmanager 来进行报警,这些工具结合起来已经可以帮助我们搭建一套比较完整的监控报警系统了,但是也仅仅局限于测试环境,对于生产环境来说则还有许多需要改进的地方,其中一个非常重要的就是 Prometheus 的高可用。
单台的 Prometheus 存在单点故障的风险,随着监控规模的扩大,Prometheus 产生的数据量也会非常大,性能和存储都会面临问题。毋庸置疑,我们需要一套高可用的 Prometheus 集群。
最简单的高可用架构
其实数据采集时,哪怕使用push gateway机制,对于prometheus来说也是pull
确保 Prometheus 服务的可用性,我们只需要部署多个 Prometheus 实例,然后采集相同的 metrics 数据即可
这个方式来满足服务的可用性应该是平时我们使用得最多的一种方式,当一个实例挂掉后从 LB 里面自动剔除掉,而且还有负载均衡的作用,可以降低一个
Prometheus 的压力,但这种模式缺点也是非常明显的,就是不满足数据一致性以及持久化问题,因为 Prometheus 是 Pull
的方式,即使多个实例抓取的是相同的监控指标,也不能保证抓取过来的值就是一致的,更何况在实际的使用过程中还会遇到一些网络延迟问题,所以会造成数据不一致的问题,不过对于监控报警这个场景来说,一般也不会要求数据强一致性,所以这种方式从业务上来说是可以接受的,因为这种数据不一致性影响基本上没什么影响
这种场景适合监控规模不大,只需要保存短周期监控数据的场景。
详细的操作就是加多一个pormetheus的pod和配置好配置文件即可,就是把前边章节的内容在走一遍
数据持久化
上面的高可用架构有个问题其实也是所有对数具有存储要求的架构存在的问题,就是数据,其中一点就是数据的存储问题,担心数据的丢失,上面的架构如果数据丢失了就很难恢复,于是要使用数据持久化
就是加上个远程存储,你数据存储在本地,再怎么备份都不如连接各专门的存储几圈来得稳妥,比如ceph存储几圈,ceph会有单独的一篇本专栏的文章来将,这里主要将Thanos
通过锁获取 Leader
其实上面的基本 HA 加上远程存储的方式基本上可以满足 Prometheus 的高可用了,这种方式的多个 Prometheus 实例都会去定时拉取监控指标数据,然后将热数据存储在本地,然后冷数据同步到远程存储中去,对于大型集群来说频繁的去拉取指标数据势必会对网络造成更大的压力。所以我们也通过服务注册的方式来实现 Prometheus 的高可用性,集群启动的时候每个节点都尝试去获取锁,获取成功的节点成为 Leader 执行任务,若主节点宕机,从节点获取锁成为 Leader 并接管服务。主要的功能修改决策,是leader
不过这种方案需要我们通过去写代码进行改造,如果在 Kubernetes 中我们完全可以使用自带的 Lease 对象来获取分布式锁,这不是很困难,只是以后要更新版本稍微麻烦点。
上面的几种方案基本上都可以满足基本的 Prometheus 高可用,但是对于大型集群来说,一个 Prometheus 实例的压力始终非常大。
对于流量的管理自然想到了istio,确实可以将prometheus部署到isito中,可以动手试试
联邦集群
自然想到了联邦制,中央是集权了,但底下民众很多事,不可能什么事都中央处理,毕竟一个单位处理能力有限,可以经由县省等
当单个 Promthues 实例无法处理大量的采集任务时,这个时候我们就可以使用基于 Prometheus 联邦集群的方式来将监控任务划分到不同的 Prometheus 实例中去。
最后就是prometheus采集promentheus
具体的采集任务如何去进行分区也没有固定的标准,需要结合实际的业务进行考虑,除了上面的方式之外,还有一种情况就是单个的采集数据量就非常非常大,比如我们要采集上万个节点的监控指标数据,这种情况即使我们已经进行了分区,但是对于单个 Prometheus 来说压力也是非常大的,这个时候我们就需要按照任务的不同实例进行划分,我们通过 Prometheus 的 relabel 功能,通过 hash 取模的方式可以确保当前 Prometheus 只采集当前任务的一部分实例的监控指标。
省略其他配置......
relabel_configs:
- source_labels: [__address__]
modulus: 4 # 将节点分片成 4 个组
target_label: __tmp_hash
action: hashmod
- source_labels: [__tmp_hash]
regex: ^1$ # 只抓第2个组中节点的数据(序号0为第1个组)
action: keep
到这里我们基本上就完成了 Prometheus 高可用的改造。对于小规模集群和大规模集群可以采用不同的方案,但是其中有一个非常重要的部分就是远程存储,我们需要保证数据的持久化就必须使用远程存储。所以下面我们将重点介绍下远程存储的时候,这里我们主要讲解目前比较流行的方案:Thanos,它完全兼容 Prometheus API,提供统一查询聚合分布式部署 Prometheus 数据的能力,同时也支持数据长期存储到各种对象存储(比如 S3、阿里云 OSS 等)以及降低采样率来加速大时间范围的数据查询。
(Thanos是一款着重于数据持久化的搭建高可用prometheus的方案)
以上反映出了原生的prometheus的一些弱点,高可用,数据存储,扩缩容等,那有没有方式可以对上面的问题提供良好的解决方案,最好还同意实现,有,就是thanos方案,thonas是一个基于prometheus实现的监控方案,解决原生的prometheus的痛点,主要表现在远程外部存储,长期存储,高可用,动态扩展
thanos的内容在监控学习系列(中)
更多推荐
所有评论(0)