Prometheus+Grafana+Kubernetes
# Prometheus+Grafana+Kubernetes
我们知道监控是保证系统运行必不可少的功能,特别是对于 Kubernetes 这种比较庞大的系统来说,监控报警更是不可或缺,我们需要时刻了解系统的各种运行指标,也需要时刻了解我们的 Pod 的各种指标,更需要在出现问题的时候有报警信息通知到我们。
在早期的版本中 Kubernetes 提供了 heapster、influxDB、grafana 的组合来监控系统,在现在的版本中已经移除掉了 heapster,现在更加流行的监控工具是 Prometheus (opens new window),Prometheus 是 Google 内部监控报警系统的开源版本。
在Prometheus监控linux主机包括docker的时候,安装的方式以及配置文件都不同,这里kubernetes介意直接使用Pod的方式。
# 实验架构图
# Prometheus
将通过以下的方式去在Kubernetes中部署Prometheus。
Prometheus的配置文件将会以configMap的方式存储。
将会通过逐步增加逐步修改Prometheus的配置文件,查看产生的变化,实现监控多个组件的metrics。
# 1.创建命名空间
创建kube-mon的命名空间,以下的所有操作将在kube-mon该命名空间执行。
[root@k8s-master ~]# kubectl create namespace kube-mon
# 2.创建配置文件
为了能够方便的管理配置文件,我们这里将 prometheus.yml
文件用 ConfigMap 的形式进行管理:(prometheus-cm.yaml)。
[root@k8s-master ~]# vim 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']
我们这里暂时只配置了对 prometheus 本身的监控,直接创建该资源对象:
$ kubectl apply -f prometheus-cm.yaml
configmap "prometheus-config" created
# 3.创建Deployment
配置文件创建完成了,以后如果我们有新的资源需要被监控,我们只需要将上面的ConfigMap
对象更新即可。现在我们来创建prometheus
的Pod
资源:(prometheus-deploy.yaml)
注意:securityContext: 配置runAsUser: 0,如果不配置这里就会出现
open /prometheus/queries.active: permission denied
这样的错误信息,这是因为我们的 prometheus 的镜像中是使用的 nobody 这个用户,然后现在我们通过 hostPath 挂载到宿主机上面的目录的ownership
却是root
。
[root@k8s-master ~]# vim 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
nodeSelector:
monitor: prometheus
containers:
- image: prom/prometheus:v2.14.0
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立即生效
- "--web.console.libraries=/usr/share/prometheus/console_libraries"
- "--web.console.templates=/usr/share/prometheus/consoles"
ports:
- containerPort: 9090
name: http
volumeMounts:
- mountPath: "/etc/prometheus"
name: config-volume
- mountPath: "/prometheus"
name: data
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 100m
memory: 512Mi
securityContext:
runAsUser: 0
volumes:
- name: data
hostPath:
path: /data/prometheus/
- configMap:
name: prometheus-config
name: config-volume
为了 prometheus 的性能和数据持久化我们这里是直接将通过 hostPath 的方式来进行数据持久化的,通过 --storage.tsdb.path=/prometheus
指定数据目录,然后将该目录声明挂载到 /data/prometheus
这个主机目录下面,为了防止 Pod 漂移,所以我们使用 nodeSelector
将 Pod 固定到了一个具有 monitor=prometheus
标签的节点上,如果没有这个标签则需要为你的目标节点打上这个标签:
[root@k8s-master ~]# kubectl label node ydzs-node3 monitor=prometheus
node/ydzs-node3 labeled
# 4.配置RBAC授权
由于 prometheus 可以访问 Kubernetes 的一些资源对象,所以需要配置 rbac 相关认证,这里我们使用了一个名为 prometheus 的 serviceAccount 对象:(prometheus-rbac.yaml)
[root@k8s-master ~]# vim prometheus-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: kube-mon
---
apiVersion: rbac.authorization.k8s.io/v1
kind: 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:
- /metrics
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: kube-mon
注意: 在每一个 namespace 下面都有可能存在,所以我们这里使用的是
ClusterRole
的资源对象,值得一提的是我们这里的权限规则声明中有一个nonResourceURLs
的属性,是用来对非资源型 metrics 进行操作的权限声明。
创建上面的资源对象即可:
[root@k8s-master ~]# kubectl apply -f prometheus-rbac.yaml
serviceaccount "prometheus" created
clusterrole.rbac.authorization.k8s.io "prometheus" created
clusterrolebinding.rbac.authorization.k8s.io "prometheus" created
# 5.配置Service
Pod 创建成功后,为了能够在外部访问到 prometheus 的 webui 服务,我们还需要创建一个 Service 对象:(prometheus-svc.yaml)
[root@k8s-master ~]# vim prometheus-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: kube-mon
labels:
app: prometheus
spec:
selector:
app: prometheus
type: NodePort
ports:
- name: web
port: 9090
targetPort: http
创建资源然后访问测试
[root@k8s-master ~]# kubectl apply -f prometheus-svc.yaml
service "prometheus" created
[root@k8s-master ~]# kubectl get svc -n kube-mon
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
prometheus NodePort 10.106.21.192 <none> 9090:32086/TCP 8h
现在我们就可以通过 http://任意节点IP:32086
访问 prometheus 的 webui 服务了:
# 应用监控
# 1.查看普通应用
对于普通应用只需要能够提供一个满足 prometheus 格式要求的 /metrics
接口就可以让 Prometheus 来接管监控,比如 Kubernetes 集群中非常重要的 CoreDNS 插件,一般默认情况下就开启了 /metrics
接口:
[root@k8s-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
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
creationTimestamp: "2021-12-10T06:24:44Z"
name: coredns
namespace: kube-system
resourceVersion: "219"
uid: ccd3375a-80c3-4394-9d8b-9e352561a26f
上面 ConfigMap 中 prometheus :9153
就是开启 prometheus 的插件:
[root@k8s-master ~]# kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-7f6cbbb7b8-24f7z 1/1 Running 0 9h 10.244.235.192 k8s-master <none> <none>
coredns-7f6cbbb7b8-klzrp 1/1 Running 0 9h 10.244.235.194 k8s-master <none> <none>
尝试手动访问下 /metrics
接口,如果能够手动访问到那证明接口是没有任何问题的:
[root@k8s-master ~]# curl http://10.244.235.192: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.16.4",revision="053c4d5",version="1.8.4"} 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"} 30
coredns_cache_entries{server="dns://:53",type="success"} 8
# 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"} 14
coredns_cache_hits_total{server="dns://:53",type="success"} 10
# HELP coredns_cache_misses_total The count of cache misses.
可以看到可以正常访问到,从这里可以看到 CoreDNS 的监控数据接口是正常的了,然后我们就可以将这个 /metrics
接口配置到 prometheus.yml
中去了,直接加到默认的 prometheus 这个 job
下面:(prometheus-cm.yaml)
[root@k8s-master ~]# cat 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'
static_configs:
- targets: ['10.244.235.192:9153','10.244.235.194:9153']
scrape_configs
下面可以支持很多参数,例如:
basic_auth
和bearer_token
:比如我们提供的/metrics
接口需要 basic 认证的时候,通过传统的用户名/密码或者在请求的 header 中添加对应的 token 都可以支持kubernetes_sd_configs
或consul_sd_configs
:可以用来自动发现一些应用的监控数据
现在我们重新更新这个 ConfigMap 资源对象:
[root@k8s-master ~]# kubectl apply -f prometheus-cm.yaml
configmap/prometheus-config configured
现在 Prometheus 的配置文件内容已经更改了,隔一会儿被挂载到 Pod 中的 prometheus.yml 文件也会更新,由于我们之前的 Prometheus 启动参数中添加了 --web.enable-lifecycle
参数,所以现在我们只需要执行一个 reload
命令即可让配置生效:
# 2.热更新
由于 ConfigMap 通过 Volume 的形式挂载到 Pod 中去的热更新需要一定的间隔时间才会生效,所以需要稍微等一小会儿。
[root@k8s-master ~]# kubectl get pods -n kube-mon -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
prometheus-6bf48c498f-rw8cn 1/1 Running 0 9h 10.244.36.66 k8s-node1 <none> <none>
[root@k8s-master ~]# curl -X POST "10.244.36.66:9090/-/reload" #这是热更新的命令
这个时候我们再去看 Prometheus 的 Dashboard 中查看采集的目标数据:
# 使用exporter监控
有一些应用可能没有自带 /metrics
接口供 Prometheus 使用,在这种情况下,我们就需要利用 exporter
服务来为 Prometheus 提供指标数据了。
exporters (opens new window),当然如果你的应用本身也没有 exporter 实现,那么就要我们自己想办法去实现一个 /metrics
接口了,只要你能提供一个合法的 /metrics
接口,Prometheus 就可以监控你的应用。
# 1.创建redis
比如我们这里通过一个 redis-exporter (opens new window) 的服务来监控 redis 服务,对于这类应用,我们一般会以 sidecar
的形式和主应用部署在同一个 Pod 中,比如我们这里来部署一个 redis 应用,并用 redis-exporter 的方式来采集监控数据供 Prometheus 使用,如下资源清单文件:(prome-redis.yaml)
[root@k8s-master ~]# vim prome-redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: kube-mon
spec:
selector:
matchLabels:
app: redis
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9121"
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
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 9121
---
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
可以看到上面我们在 redis 这个 Pod 中包含了两个容器,一个就是 redis 本身的主应用,另外一个容器就是 redis_exporter。现在直接创建上面的应用:
[root@k8s-master ~]# kubectl apply -f prome-redis.yaml
deployment.apps/redis created
service/redis created
创建完成后,我们可以看到 redis 的 Pod 里面包含有两个容器:
[root@k8s-master ~]# kubectl get pods -n kube-mon
NAME READY STATUS RESTARTS AGE
prometheus-6bf48c498f-rw8cn 1/1 Running 0 9h
redis-6468bf6c84-slqgq 2/2 Running 0 9h
[root@k8s-master ~]# kubectl get svc -n kube-mon
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
prometheus NodePort 10.106.21.192 <none> 9090:32086/TCP 9h
redis ClusterIP 10.102.180.119 <none> 6379/TCP,9121/TCP 9h
# 2.校验采集数据
我们可以通过 9121
端口来校验是否能够采集到数据:
[root@k8s-master ~]# curl 10.102.180.119:9121/metrics
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 2.0303e-05
go_gc_duration_seconds{quantile="0.25"} 4.8104e-05
go_gc_duration_seconds{quantile="0.5"} 0.000110823
go_gc_duration_seconds{quantile="0.75"} 0.000298164
go_gc_duration_seconds{quantile="1"} 0.001429556
go_gc_duration_seconds_sum 0.150331728
go_gc_duration_seconds_count 694
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
现在我们只需要更新 Prometheus 的配置文件:
- job_name: 'redis'
static_configs:
- targets: ['redis:9121']
这里是通过 Service 去配置的 redis 服务,当然直接配置 Pod IP 也是可以的,因为和 Prometheus 处于同一个 namespace,所以我们直接使用 servicename 即可。配置文件更新后,重新加载:
[root@k8s-master ~]# kubectl apply -f prometheus-cm.yaml
configmap/prometheus-config configured
//隔一会儿执行reload操作
[root@k8s-master ~]# curl -X POST "http://10.244.36.66:9090/-/reload"
# 3.热更新后访问
这个时候我们再去看 Prometheus 的 Dashboard 中查看采集的目标数据:
# 集群节点
对于集群的监控一般我们需要考虑以下几个方面:
- Kubernetes 节点的监控:比如节点的 cpu、load、disk、memory 等指标
- 内部系统组件的状态:比如 kube-scheduler、kube-controller-manager、kubedns/coredns 等组件的详细运行状态
- 编排级的 metrics:比如 Deployment 的状态、资源请求、调度和 API 延迟等数据指标
Kubernetes 集群的监控方案目前主要有以下几种方案:
- Heapster:Heapster 是一个集群范围的监控和数据聚合工具,以 Pod 的形式运行在集群中。 heapster 除了 Kubelet/cAdvisor 之外,我们还可以向 Heapster 添加其他指标源数据,比如 kube-state-metrics,需要注意的是 Heapster 已经被废弃了,后续版本中会使用 metrics-server 代替。
- cAdvisor:cAdvisor (opens new window) 是 Google 开源的容器资源监控和性能分析工具,它是专门为容器而生,本身也支持 Docker 容器。
- kube-state-metrics:kube-state-metrics (opens new window) 通过监听 API Server 生成有关资源对象的状态指标,比如 Deployment、Node、Pod,需要注意的是 kube-state-metrics 只是简单提供一个 metrics 数据,并不会存储这些指标数据,所以我们可以使用 Prometheus 来抓取这些数据然后存储。
- metrics-server:metrics-server 也是一个集群范围内的资源数据聚合工具,是 Heapster 的替代品,同样的,metrics-server 也只是显示数据,并不提供数据存储服务。
不过 kube-state-metrics 和 metrics-server 之间还是有很大不同的,二者的主要区别如下:
- kube-state-metrics 主要关注的是业务相关的一些元数据,比如 Deployment、Pod、副本状态等
- metrics-server 主要关注的是资源度量 API (opens new window) 的实现,比如 CPU、文件描述符、内存、请求延时等指标。
# 1.监控集群节点
以通过 DaemonSet 控制器来部署该服务,这样每一个节点都会自动运行一个这样的 Pod,如果我们从集群中删除或者添加节点后,也会进行自动扩展。
在部署 node-exporter
的时候有一些细节需要注意,如下资源清单文件:(prome-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
hostIPC: true
hostNetwork: true
nodeSelector:
kubernetes.io/os: linux
containers:
- name: node-exporter
image: prom/node-exporter:v0.18.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 是两个完全不相同的概念。另外我们还将主机的
/dev
、/proc
、/sys
这些目录挂载到容器中,这些因为我们采集的很多节点数据都是通过这些文件夹下面的文件来获取到的,比如我们在使用top
命令可以查看当前 cpu 使用情况,数据就来源于文件/proc/stat
,使用free
命令可以查看当前内存使用情况,其数据来源是来自/proc/meminfo
文件。
另外由于我们集群使用的是 kubeadm
搭建的,所以如果希望 master 节点也一起被监控,则需要添加相应的容忍,然后直接创建上面的资源对象:
[root@k8s-master ~]# kubectl apply -f prome-node-exporter.yaml
daemonset.apps/node-exporter created
[root@k8s-master ~]# kubectl get pods -n kube-mon -l app=node-exporter -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node-exporter-6rd77 1/1 Running 0 9h 192.168.1.107 k8s-node2 <none> <none>
node-exporter-mtlm6 1/1 Running 0 9h 192.168.1.105 k8s-node1 <none> <none>
node-exporter-v8cwr 1/1 Running 0 9h 192.168.1.103 k8s-master <none> <none>
# 2.服务发现
由于我们这里每个节点上面都运行了 node-exporter
程序,如果我们通过一个 Service 来将数据收集到一起用静态配置的方式配置到 Prometheus 去中,就只会显示一条数据,我们得自己在指标数据中去过滤每个节点的数据,当然我们也可以手动的把所有节点用静态的方式配置到 Prometheus 中去,但是以后要新增或者去掉节点的时候就还得手动去配置,那么有没有一种方式可以让 Prometheus 去自动发现我们节点的 node-exporter
程序,并且按节点进行分组呢?这就是 Prometheus 里面非常重要的服务发现功能了。
在 Kubernetes 下,Promethues 通过与 Kubernetes API 集成,主要支持5中服务发现模式,分别是:Node
、Service
、Pod
、Endpoints
、Ingress
。
这里我们可以通过 labelmap
这个属性来将 Kubernetes 的 Label 标签添加为 Prometheus 的指标数据的标签:
- job_name: 'kubernetes-nodes'
kubernetes_sd_configs:
- role: node
relabel_configs:
- source_labels: [__address__]
regex: '(.*):10250'
replacement: '${1}:9100'
target_label: __address__
action: replace
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
prometheus 去发现 Node 模式的服务的时候,访问的端口默认是10250,而默认是需要认证的 https 协议才有权访问的,但实际上我们并不是希望让去访问10250端口的
/metrics
接口,而是node-exporter
绑定到节点的 9100 端口,所以我们应该将这里的10250
替换成9100
添加了一个 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
:每个节点地址类型的第一个地址(如果存在)
# 3.监控kubelet组件
另外由于 kubelet 也自带了一些监控指标数据,就上面我们提到的 10250 端口,所以我们这里也把 kubelet 的监控任务也一并配置上:
- job_name: 'kubernetes-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_(.+)
然后执行prometheus-cm.yaml然后再热更新。
[root@k8s-master ~]# kubectl apply -f prometheus-cm.yaml
configmap/prometheus-config configured
//隔一会儿执行reload操作
[root@k8s-master ~]# curl -X POST "http://10.244.36.66:9090/-/reload"
# 4.容器监控
说到容器监控我们自然会想到 cAdvisor
,我们前面也说过cAdvisor已经内置在了 kubelet 组件之中,所以我们不需要单独去安装,cAdvisor
的数据路径为 /api/v1/nodes/<node>/proxy/metrics
,同样我们这里使用 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
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- target_label: __address__
replacement: kubernetes.default.svc:443
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
target_label: __metrics_path__
replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
然后执行prometheus-cm.yaml然后再热更新。
[root@k8s-master ~]# kubectl apply -f prometheus-cm.yaml
configmap/prometheus-config configured
//隔一会儿执行reload操作
[root@k8s-master ~]# curl -X POST "http://10.244.36.66:9090/-/reload"
上面的配置和我们之前配置 node-exporter
的时候几乎是一样的,区别是我们这里使用了 https 的协议,另外需要注意的是配置了 ca.cart 和 token 这两个文件,这两个文件是 Pod 启动后自动注入进来的,通过这两个文件我们可以在 Pod 中访问 apiserver,比如我们这里的 __address__
不再是 nodeip 了,而是 kubernetes 在集群中的服务地址,然后加上__metrics_path__
的访问路径 /api/v1/nodes/${1}/proxy/metrics/cadvisor
,因为我们现在是通过 kubernetes 的 apiserver 地址去进行访问的,现在同样更新下配置,然后查看 Targets 路径:
# 5.监控apiserver
这里我们使用的是 Endpoints 类型的服务发现,所以 Prometheus 把所有的 Endpoints 服务都抓取过来了,同样的,上面我们需要的服务名为 kubernetes
这个 apiserver 的服务也在这个列表之中,那么我们应该怎样来过滤出这个服务来呢?
这里我们就可以根据对应的 __meta_kubernetes_namespace
和 __meta_kubernetes_service_name
这两个元数据来 relabel,另外由于 kubernetes 这个服务对应的端口是 443,需要使用 https 协议,所以这里我们需要使用 https 的协议,对应的就需要将 ca 证书配置上,如下所示:
- 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
现在重新更新配置文件、重新加载 Prometheus,切换到 Prometheus 的 Targets 路径下查看:
# 6.监控 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+)
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
注意我们这里在 relabel_configs
区域做了大量的配置,特别是第一个保留__meta_kubernetes_service_annotation_prometheus_io_scrape
为 true 的才保留下来,这就是说要想自动发现集群中的 Endpoint,就需要我们在 Service 的 annotation
区域添加 prometheus.io/scrape=true
的声明,现在我们先将上面的配置更新,查看下效果:
现在我们在之前创建的 redis 这个 Service 中添加上 prometheus.io/scrape=true
这个 annotation:(prome-redis.yaml)
kind: Service
apiVersion: v1
metadata:
name: redis
namespace: kube-mon
annotations: #添加这三行
prometheus.io/scrape: "true"
prometheus.io/port: "9121"
spec:
selector:
app: redis
ports:
- name: redis
port: 6379
targetPort: 6379
- name: prom
port: 9121
targetPort: 9121
由于 redis 服务的 metrics 接口在 9121 这个 redis-exporter 服务上面,所以我们还需要添加一个 prometheus.io/port=9121
这样的 annotations,然后更新这个 Service:
[root@k8s-master ~]# kubectl apply -f prome-redis.yaml
deployment.apps "redis" unchanged
service "redis" changed
这样以后我们有了新的服务,服务本身提供了 /metrics
接口,我们就完全不需要用静态的方式去配置了,到这里我们就可以将之前配置的 redis 的静态配置去掉了。
# 7.完整的Prometheus配置文件
[root@k8s-master ~]# cat 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'
static_configs:
- targets: ['10.244.235.192:9153','10.244.235.194:9153']
- job_name: 'redis'
static_configs:
- targets: ['10.102.180.119:9121']
- job_name: 'kubernetes-nodes'
kubernetes_sd_configs:
- role: node
relabel_configs:
- source_labels: [__address__]
regex: '(.*):10250'
replacement: '${1}:9100'
target_label: __address__
action: replace
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- job_name: 'kubernetes-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_(.+)
- target_label: __address__
replacement: kubernetes.default.svc:443
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
target_label: __metrics_path__
replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
- 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: '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+)
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
# Grafana
Grafana 是一个可视化面板,有着非常漂亮的图表和布局展示,功能齐全的度量仪表盘和图形编辑器,支持 Graphite、zabbix、InfluxDB、Prometheus、OpenTSDB、Elasticsearch 等作为数据源,比 Prometheus 自带的图表展示功能强大太多,更加灵活,有丰富的插件,功能更加强大。
# 1.安装
使用deployment部署granfana
- 添加了健康检查、资源声明,另外两个比较重要的环境变量
GF_SECURITY_ADMIN_USER
和GF_SECURITY_ADMIN_PASSWORD
,用来配置 grafana 的管理员用户和密码的。 - 由于 grafana 将 dashboard、插件这些数据保存在
/var/lib/grafana
这个目录下面的,所以我们这里如果需要做数据持久化的话,就需要针对这个目录进行 volume 挂载声明。 - 和 Prometheus 一样,我们将 grafana 固定在一个具有
monitor: prometheus
标签的节点,由于上面我们刚刚提到的 Changelog 中 grafana 的 userid 和 groupid 有所变化,所以我们这里增加一个securityContext
的声明来进行声明使用 root 用户运行。最后,我们需要对外暴露 grafana 这个服务,所以我们需要一个对应的 Service 对象,当然用 NodePort 或者再建立一个 ingress 对象都是可行的。
[root@k8s-master ~]# vim grafana.yaml
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/grafana/
nodeSelector:
monitor: prometheus
securityContext:
runAsUser: 0
containers:
- name: grafana
image: grafana/grafana:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
name: grafana
env:
- name: GF_SECURITY_ADMIN_USER
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
name: storage
---
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: kube-mon
spec:
type: NodePort
ports:
- port: 3000
selector:
app: grafana
现在我们直接创建上面的这些资源对象:
[root@k8s-master ~]# kubectl apply -f grafana.yaml
deployment.apps "grafana" created
service "grafana" created
创建完成后,我们可以查看 grafana 对应的 Pod 是否正常:
[root@k8s-master ~]# kubectl get deployment -n kube-mon
NAME READY UP-TO-DATE AVAILABLE AGE
grafana 1/1 1 1 8h
prometheus 1/1 1 1 9h
redis 1/1 1 1 9h
看到上面的日志信息就证明我们的 grafana 的 Pod 已经正常启动起来了。这个时候我们可以查看 Service 对象:
[root@k8s-master ~]# kubectl get svc -n kube-mon
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
grafana NodePort 10.98.74.87 <none> 3000:31457/TCP 8h
prometheus NodePort 10.106.21.192 <none> 9090:32086/TCP 9h
redis ClusterIP 10.102.180.119 <none> 6379/TCP,9121/TCP 9h
现在我们就可以在浏览器中使用 http://<任意节点IP:31457>
,admin/adnmin321来访问 grafana 这个服务了:
# 2.配置Grafana
这个地方配置的数据源是 Prometheus
,我们这里 Prometheus 和 Grafana 都处于 kube-mon 这同一个 namespace 下面,所以我们这里的数据源地址:http://prometheus:9090
(因为在同一个 namespace 下面所以直接用 Service 名也可以),然后其他的配置信息就根据实际情况了,比如 Auth 认证,我们这里没有,所以跳过即可,点击最下方的 Save & Test
提示成功证明我们的数据源配置正确:
# 3.导入Dashboard
为了能够快速对系统进行监控,我们可以直接复用别人的 Grafana Dashboard,在 Grafana 的官方网站上就有很多非常优秀的第三方 Dashboard,我们完全可以直接导入进来即可。比如我们想要对所有的集群节点进行监控,也就是 node-exporter 采集的数据进行展示,这里我们就可以导入 https://grafana.com/grafana/dashboards/8919 这个 Dashboard。
在侧边栏点击 "+",选择 Import
,在 Grafana Dashboard 的文本框中输入 8919 即可导入:
进入导入 Dashboard 的页面,可以编辑名称,选择 Prometheus 的数据源。
保存后即可进入导入的 Dashboard 页面。由于该 Dashboard 更新比较及时,所以基本上导入进来就可以直接使用了,我们也可以对页面进行一些调整,如果有的图表没有出现对应的图形,则可以编辑根据查询语句去 DEBUG。