一、背景介绍
前面我们一起配置了如何在 kube-prometheus 下面新增一个监控项 Kubernetes 集群监控 ETCD 组件。如果我们在 Kubernetes 集群中有了很多的 Service 和 Pod,那么我们都得一个一个的去建立一个对应的 ServiceMonitor 对象来进行监控吗?这样岂不是又变得很繁琐起来了?
答案「否定的」,我们可以通过 prometheus 的自动发现功能轻松解决这个问题。
二、什么是服务发现?
我们在每个节点上面都运行了 node-exporter,如果我们通过一个 Service 来将数据收集到一起用静态配置的方式配置到 Prometheus 去中,就只会显示一条数据,我们得自己在指标数据中去过滤每个节点的数据,当然我们也可以手动的把所有节点用静态的方式配置到 Prometheus 中去,但是以后要新增或者去掉节点的时候就还得手动去配置,那么有没有一种方式可以让 Prometheus 去自动发现我们节点的 node-exporter 程序,并且按节点进行分组呢?这就是 Prometheus 里面非常重要的「服务发现」功能。
Prometheus支持多种服务发现机制:文件、DNS、Consul、Kubernetes、OpenStack、EC2等。基于服务发现的过程并不复杂,通过第三方提供的接口,Prometheus查询到需要监控的Target列表,然后轮训这些Target获取监控数据,下面主要介绍Kubernetes服务发现机制。
目前,在Kubernetes下,Prometheus 通过与 Kubernetes API 集成主要支持5种服务发现模式:Node、Service、Pod、Endpoints、Ingress。不同的服务发现模式适用于不同的场景,例如:node适用于与主机相关的监控资源,如节点中运行的Kubernetes 组件状态、节点上运行的容器状态等;service 和 ingress 适用于通过黑盒监控的场景,如对服务的可用性以及服务质量的监控;endpoints 和 pod 均可用于获取 Pod 实例的监控数据,如监控用户或者管理员部署的支持 Prometheus 的应用。
三、规则解析
为解决服务发现的问题,kube-prometheus 为我们提供了一个额外的抓取配置来解决这个问题,我们可以通过添加额外的配置来进行服务发现进行自动监控。我们可以在 kube-prometheus 当中去自动发现并监控具有 prometheus.io/scrape=true
这个 annotations 的 Service。
配置项规则参考链接:https://prometheus.io/docs/prometheus/latest/configuration/configuration/
其中通过 kubernetes_sd_configs
支持监控其各种资源。kubernetes SD 配置允许从 kubernetes REST API 接受搜集指标,且总是和集群保持同步状态,任何一种 role 类型都能够配置来发现我们想要的对象。
规则配置使用 yaml 格式,下面是文件中一级配置项。自动发现 k8s Metrics 接口是通过 scrape_configs 来实现的:
代码语言:javascript复制#全局配置
global:
#规则配置主要是配置报警规则
rule_files:
#抓取配置,主要配置抓取客户端相关
scrape_configs:
#报警配置
alerting:
#用于远程存储写配置
remote_write:
#用于远程读配置
remote_read:
举例说明:
代码语言:javascript复制# Kubernetes的API SERVER会暴露API服务,Promethues集成了对Kubernetes的自动发现,它有5种模式:Node、Service
# 、Pod、Endpoints、ingress,下面是Prometheus官方给出的对Kubernetes服务发现的实例。这里你会看到大量的relabel_configs,
# 其实你就是把所有的relabel_configs去掉一样可以对kubernetes做服务发现。relabel_configs仅仅是对采集过来的指标做二次处理,比如
# 要什么不要什么以及替换什么等等。而以__meta_开头的这些元数据标签都是实例中包含的,而relabel则是动态的修改、覆盖、添加删除这些标签
# 或者这些标签对应的值。而且以__开头的标签通常是系统内部使用的,因此这些标签不会被写入样本数据中,如果我们要收集这些东西那么则要进行
# relabel操作。当然reabel操作也不仅限于操作__开头的标签。
#
# action的行为:
# replace:默认行为,不配置action的话就采用这种行为,它会根据regex来去匹配source_labels标签上的值,并将并将匹配到的值写入target_label中
# labelmap:它会根据regex去匹配标签名称,并将匹配到的内容作为新标签的名称,其值作为新标签的值
# keep:仅收集匹配到regex的源标签,而会丢弃没有匹配到的所有标签,用于选择
# drop:丢弃匹配到regex的源标签,而会收集没有匹配到的所有标签,用于排除
# labeldrop:使用regex匹配标签,符合regex规则的标签将从target实例中移除,其实也就是不收集不保存
# labelkeep:使用regex匹配标签,仅收集符合regex规则的标签,不符合的不收集
global:
# 间隔时间
scrape_interval: 30s
# 超时时间
scrape_timeout: 10s
# 另一个独立的规则周期,对告警规则做定期计算
evaluation_interval: 30s
# 外部系统标签
external_labels:
prometheus: monitoring/k8s
prometheus_replica: prometheus-k8s-1
# 抓取服务端点,整个这个任务都是用来发现node-exporter和kube-state-metrics-service的,这里用的是endpoints角色,这是通过这两者的service来发现
# 的后端endpoints。另外需要说明的是如果满足采集条件,那么在service、POD中定义的labels也会被采集进去
scrape_configs:
# 定义job名称,是一个拉取单元
- job_name: "kubernetes-endpoints"
# 发现endpoints,它是从列出的服务端点发现目标,这个endpoints来自于Kubernetes中的service,每一个service都有对应的endpoints,这里是一个列表
# 可以是一个IP:PORT也可以是多个,这些IP:PORT就是service通过标签选择器选择的POD的IP和端口。所以endpoints角色就是用来发现server对应的pod的IP的
# kubernetes会有一个默认的service,通过找到这个service的endpoints就找到了api server的IP:PORT,那endpoints有很多,我怎么知道哪个是api server呢
# 这个就靠source_labels指定的标签名称了。
kubernetes_sd_configs:
# 角色为 endpoints
- role: endpoints
# 下面的含义是源标签__meta_kubernetes_namespace等如果其值为default;kubernetes;https标签顺序和值要对应。换句话说就是
# 当__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name三者对应的
# 值为default、kubernetes、https则进行保留,而且该endpoints对应的地址为api server的地址。
#
# __meta_kubernetes_namespace 端点对象的命名空间,在不同对象上这个标签的含义不同,在角色是endpoints中这个是端点对象的名称空间
# __meta_kubernetes_service_name 端点对象的服务名称
# __meta_kubernetes_endpoint_port_name 端点的端口名称
#
# kubernetes默认在default名称空间有一个叫做kubernetes的service,所以这个service的有3个设置对应的就是下面三个标签
# __meta_kubernetes_namespace 值为default
# __meta_kubernetes_service_name 值为kubernetes
# __meta_kubernetes_endpoint_port_name 值为https
relabel_configs:
# 重新打标仅抓取到的具有 "prometheus.io/scrape: true" 的annotation的端点,意思是说如果某个service具有prometheus.io/scrape = true annotation声明则抓取
# annotation本身也是键值结构,所以这里的源标签设置为键,而regex设置值,当值匹配到regex设定的内容时则执行keep动作也就是保留,其余则丢弃.
# node-exporter这个POD的service里面就有一个叫做prometheus.io/scrape = true的annotations所以就找到了node-exporter这个POD
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
# 动作 删除 regex 与串联不匹配的目标 source_labels
action: keep
# 通过正式表达式匹配 true
regex: true
# 重新设置scheme
# 匹配源标签__meta_kubernetes_service_annotation_prometheus_io_scheme也就是prometheus.io/scheme annotation
# 如果源标签的值匹配到regex则把值替换为__scheme__对应的值
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
action: replace
target_label: __scheme__
regex: (https?)
# 匹配来自 pod annotationname prometheus.io/path 字段
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
# 获取POD的 annotation 中定义的"prometheus.io/path: XXX"定义的值,这个值就是你的程序暴露符合prometheus规范的metrics的地址
# 如果你的metrics的地址不是 /metrics 的话,通过这个标签说,那么这里就会把这个值赋值给 __metrics_path__这个变量,因为prometheus
# 是通过这个变量获取路径然后进行拼接出来一个完整的URL,并通过这个URL来获取metrics值的,因为prometheus默认使用的就是 http(s)://X.X.X.X/metrics
# 这样一个路径来获取的。
action: replace
# 匹配目标指标路径
target_label: __metrics_path__
# 匹配全路径
regex: (. )
# 匹配出 Pod ip地址和 Port
- 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
# service 对象的名称
- source_labels: [__meta_kubernetes_service_name]
action: replace
target_label: kubernetes_name
# pod对象的名称
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
四、如何实现?
1、创建发现规则
我们定义的 Prometheus 的配置如下:「prometheus-additional.yaml:」
代码语言:javascript复制- 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
endpoints role 从每个服务监听的 endpoints 发现,每个 endpoint 都会发现一个port,如果 endpoint 是一个pod,所有包含的容器不被绑定到一个 endpoint port,也会被 targets 被发现。如果你对上面这个配置还不是很熟悉的话,建议去查看下前面关于 Kubernetes 常用资源对象监控的介绍,要想自动发现集群中的 Service,就需要我们在 Service 的 annotation 区域添加 prometheus.io/scrape=true
的声明
2、创建Secret 对象
将上面文件直接保存为 prometheus-additional.yaml
,然后通过这个文件创建一个对应的 Secret 对象:
$ kubectl create secret generic additional-configs --from-file=prometheus-additional.yaml -n monitoring
secret "additional-configs" created
3、创建资源对象
然后我们需要在声明 prometheus 的资源对象文件中通过 additionalScrapeConfigs 属性添加上这个额外的配置:
「prometheus-prometheus.yaml」:
代码语言:javascript复制apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
labels:
prometheus: k8s
name: k8s
namespace: monitoring
spec:
alerting:
alertmanagers:
- name: alertmanager-main
namespace: monitoring
port: web
image: quay.io/prometheus/prometheus:v2.20.0
nodeSelector:
kubernetes.io/os: linux
podMonitorNamespaceSelector: {}
podMonitorSelector: {}
replicas: 2
resources:
requests:
memory: 400Mi
ruleSelector:
matchLabels:
prometheus: k8s
role: alert-rules
securityContext:
fsGroup: 2000
runAsNonRoot: true
runAsUser: 1000
serviceAccountName: prometheus-k8s
serviceMonitorNamespaceSelector: {}
serviceMonitorSelector: {}
version: v2.20.0
storage: #----添加持久化配置,指定StorageClass
volumeClaimTemplate:
spec:
storageClassName: nfs-storage-new #---指定name
resources:
requests:
storage: 10Gi
additionalScrapeConfigs:
name: additional-configs
key: prometheus-additional.yaml
关于 additionalScrapeConfigs 属性的具体介绍,我们可以使用 kubectl explain
命令来了解详细信息:
$ kubectl explain prometheus.spec.additionalScrapeConfigs
KIND: Prometheus
VERSION: monitoring.coreos.com/v1
RESOURCE: additionalScrapeConfigs <Object>
DESCRIPTION:
AdditionalScrapeConfigs allows specifying a key of a Secret containing
additional Prometheus scrape configurations. Scrape configurations
specified are appended to the configurations generated by the Prometheus
Operator. Job configurations specified must have the form as specified in
the official Prometheus documentation:
https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config.
As scrape configs are appended, the user is responsible to make sure it is
valid. Note that using this feature may expose the possibility to break
upgrades of Prometheus. It is advised to review Prometheus release notes to
ensure that no incompatible scrape configs are going to break Prometheus
after the upgrade.
FIELDS:
key <string> -required-
The key of the secret to select from. Must be a valid secret key.
name <string>
Name of the referent. More info:
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
optional <boolean>
Specify whether the Secret or its key must be defined
添加完成后,直接更新 prometheus 这个 CRD 资源对象即可:
代码语言:javascript复制kubectl apply -f prometheus-prometheus.yaml
4、查看配置
过一段时间,刷新 promethues 上的 config,将会查看配置已经生效:
5、创建 RBAC 权限
我们切换到 targets 页面下面却并没有发现对应的监控任务,查看 Prometheus 的 Pod 日志,发现很多错误日志出现,都是 xxx is forbidden,这说明是 RBAC 权限的问题。
通过 prometheus 资源对象的配置可以知道 Prometheus 绑定了一个名为 prometheus-k8s 的 ServiceAccount 对象,而这个对象绑定的是一个名为 prometheus-k8s 的
ClusterRole:
创建 prometheus-clusterRole.yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus-k8s
rules:
- apiGroups:
- ""
resources:
- nodes/metrics
verbs:
- get
- nonResourceURLs:
- /metrics
verbs:
- get
上面的权限规则中我们可以看到明显没有对 Service 或者 Pod 的 list 权限,所以报错了,要解决这个问题,我们只需要添加上需要的权限即可:
代码语言:javascript复制apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus-k8s
rules:
- apiGroups:
- ""
resources:
- nodes
- services
- endpoints
- pods
- nodes/proxy
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- configmaps
- nodes/metrics
verbs:
- get
- nonResourceURLs:
- /metrics
verbs:
- get
更新上面的 ClusterRole 这个资源对象,然后重建下 Prometheus 的所有 Pod,正常就可以看到 targets 页面下面有 kubernetes-endpoints 这个监控任务了:
这里抓取目标是因为 Service 中都有 prometheus.io/scrape=true
这个 annotation。至此,一个自动发现 pod 的配置就完成了,其他资源(service、endpoint、ingress、node同样也可以通过自动发现的方式实现。
五、常用中间件如何落地 ?
1、Redis
1.1、原理
Prometheus 的数据指标是通过一个公开的 HTTP(S) 数据接口获取到的,我们不需要单独安装监控的 agent,只需要暴露一个 metrics 接口,Prometheus 就会定期去拉取数据;对于一些普通的 HTTP 服务,我们完全可以直接重用这个服务,添加一个 /metrics
接口暴露给 Prometheus;而且获取到的指标数据格式是非常易懂的,不需要太高的学习成本。同时现在很多服务从一开始就内置了一个/metrics 接口,比如 Kubernetes 的各个组件、istio 服务网格都直接提供了数据指标接口。有一些服务即使没有原生集成该接口,也完全可以使用一些 exporter 来获取到指标数据,比如今天介绍的 redis_exporter,而 exporter 就有点类似于传统监控服务中的 agent,作为服务一直存在,用来收集目标服务的指标数据然后直接暴露给 Prometheus。
1.2、redis_exporter
redis 没有自带 /metrics 接口供 Prometheus 使用,在这种情况下,我们就需要利用 exporter 服务来为 Prometheus 提供指标数据了。Prometheus 官方为许多应用就提供了对应的 exporter 应用,也有许多第三方的实现,我们可以前往官方网站进行查看:https://prometheus.io/docs/instrumenting/exporters/
这里我们选择官方的 redis_exporter:https://github.com/oliver006/redis_exporter
1.3、构建 sidecar
这里通过 redis_exporter 的服务来监控 redis 服务,我们以 sidecar 的形式和主应用部署在同一个 Pod 中,比如我们这里来部署一个 redis,并用 redis_exporter 的方式来采集监控数据供 Prometheus 使用,如下资源清单文件:promethues-redis-deploy.yaml这里通过 redis_exporter 的服务来监控 redis 服务,我们以 sidecar 的形式和主应用部署在同一个 Pod 中,比如我们这里来部署一个 redis,并用 redis_exporter 的方式来采集监控数据供 Prometheus 使用。
如下资源清单文件:promethues-redis-deploy.yaml:
代码语言:javascript复制## Service
apiVersion: v1
kind: Service
metadata:
name: cloud-redis
labels:
app: redis
spec:
selector:
app: redis
ports:
- name: redis
port: 6379
targetPort: 6379
- name: prom
port: 9121
targetPort: 9121
---
## Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloud-redis
labels:
app: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9121"
labels:
app: redis
spec:
initContainers:
- name: system-init
image: busybox:1.32
imagePullPolicy: IfNotPresent
command:
- "sh"
- "-c"
- "echo 2000 > /proc/sys/net/core/somaxconn && echo never > /sys/kernel/mm/transparent_hugepage/enabled"
securityContext:
privileged: true
runAsUser: 0
volumeMounts:
- name: sys
mountPath: /sys
containers:
- name: redis-exporter
image: oliver006/redis_exporter:latest
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 9121
- name: redis
image: redis:5.0.8
command:
- "sh"
- "-c"
- "redis-server /usr/local/etc/redis/redis.conf"
ports:
- containerPort: 6379
resources:
limits:
cpu: 1000m
memory: 1024Mi
requests:
cpu: 1000m
memory: 1024Mi
volumeMounts:
- name: data
mountPath: /data
- name: config
mountPath: /usr/local/etc/redis/redis.conf
subPath: redis.conf
volumes:
- name: data
persistentVolumeClaim:
claimName: redis
- name: config
configMap:
name: redis-config
- name: sys
hostPath:
path: /sys
1.4、测试
创建完成后,我们可以看到 redis 的 Pod 里面包含有两个容器:
代码语言:javascript复制$ kubectl get pods -n mall
NAME READY STATUS RESTARTS AGE
cloud-redis-79b69db657-vjh8r 2/2 Running 0 5d4h
$ kubectl get svc -n mall
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cloud-redis ClusterIP 10.96.243.202 <none> 6379/TCP,9121/TCP 5d4h
我们可以通过 9121 端口来校验是否能够采集到数据:
代码语言:javascript复制$ curl 10.96.243.202: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"} 4.6144e-05
go_gc_duration_seconds{quantile="0.25"} 9.4462e-05
go_gc_duration_seconds{quantile="0.5"} 0.000122258
go_gc_duration_seconds{quantile="0.75"} 0.000168729
go_gc_duration_seconds{quantile="1"} 0.009149671
go_gc_duration_seconds_sum 1.531426455
go_gc_duration_seconds_count 6342
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 10
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.15"} 1
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
# TYPE go_memstats_alloc_bytes gauge
go_memstats_alloc_bytes 4.304504e 06
# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
# TYPE go_memstats_alloc_bytes_total counter
go_memstats_alloc_bytes_total 1.6514546264e 10
# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
# TYPE go_memstats_buck_hash_sys_bytes gauge
go_memstats_buck_hash_sys_bytes 1.581302e 06
# HELP go_memstats_frees_total Total number of frees.
# TYPE go_memstats_frees_total counter
go_memstats_frees_total 1.45861236e 08
......
1.5、Prometheus 规则配置
创建的发现规则如下:
代码语言:javascript复制- job_name: "kubernetes-pod"
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (. )
- source_labels:
[__address__, __meta_kubernetes_pod_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_pod_name]
action: replace
target_label: kubernetes_pod_name
重新更新 secret 配置:
代码语言:javascript复制kubectl apply -f secret generic additional-configs --from-file=prometheus-additional.yaml -n monitoring
过一会儿我们再去看 Prometheus 的 targets 中查看采集的目标数据:
可以看到配置的 自动发现这个 job 已经生效了。切换到 Graph 下面可以看到很多关于 redis 的指标数据,我们选择任意一个指标,比如 redis_allocator_allocated_bytes
,然后点击执行就可以看到对应的数据图表了:
1.6、Dashboard
选择导入ID为 「2751」 的模版。效果如下:
2、MySQL
1.1、mysqld_exporter
MySQL 没有自带 /metrics
接口供 Prometheus 使用,在这种情况下,我们也需要利用 exporter 服务来为 Prometheus 提供指标数据了。Prometheus 官方为许多应用就提供了对应的 exporter 应用,也有许多第三方的实现,我们可以前往官方网站进行查看:https://prometheus.io/docs/instrumenting/exporters/。
这里我们选择官方的 mysqld_exporter:https://github.com/prometheus/mysqld_exporter
mysqld_exporter 其支持的版本:
- MySQL >= 5.6.
- MariaDB >= 10.1
1.2、构建 sidecar
这里通过 mysqld_exporter 的服务来监控 MySQL 服务,我们以 sidecar 的形式和主应用部署在同一个 Pod 中,比如我们这里来部署一个 MySQL,并用 mysqld_exporter 的方式来采集监控数据供 Prometheus 使用。
如下资源清单文件:「promethues-mysql-deploy.yaml」
代码语言:javascript复制## Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: db-mall-mysql
labels:
app: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9104"
labels:
app: mysql
spec:
containers:
- name: mysql
image: centos/mysql-57-centos7:latest
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD ## 配置Root用户默认密码
value: "123456"
resources:
limits:
cpu: 2000m
memory: 512Mi
requests:
cpu: 2000m
memory: 512Mi
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: config
mountPath: /etc/mysql/conf.d/my.cnf
subPath: my.cnf
- name: localtime
readOnly: true
mountPath: /etc/localtime
- name: mysql-exporter
image: prom/mysqld-exporter:latest
env:
- name: DATA_SOURCE_NAME
value: "root:123456@(db-mall-mysql:3306)/"
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 9104
volumes:
- name: data
persistentVolumeClaim:
claimName: mysql
- name: config
configMap:
name: mysql-config
- name: localtime
hostPath:
type: File
path: /etc/localtime
---
## Service
apiVersion: v1
kind: Service
metadata:
name: db-mall-mysql
labels:
app: mysql
spec:
type: NodePort
ports:
- name: mysql
port: 3306
targetPort: 3306
nodePort: 30336
- name: prom
port: 9104
targetPort: 9104
selector:
app: mysql
1.3、测试
创建完成后,我们可以看到 MySQL 的 Pod 里面包含有两个容器:
代码语言:javascript复制$ kubectl get pods -n mall
NAME READY STATUS RESTARTS AGE
db-mall-mysql-6b69bdddd6-7crqg 2/2 Running 0 4d20h
$ kubectl get svc -n mall
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
db-mall-mysql NodePort 10.96.106.73 <none> 3306:30336/TCP,9104:30602/TCP 4d20h
我们可以通过 9104 端口来校验是否能够采集到数据:
代码语言:javascript复制$ curl 10.96.106.73:9104/metrics
TYPE mysql_global_variables_ft_query_expansion_limit gauge
mysql_global_variables_ft_query_expansion_limit 20
# HELP mysql_global_variables_general_log Generic gauge metric from SHOW GLOBAL VARIABLES.
# TYPE mysql_global_variables_general_log gauge
mysql_global_variables_general_log 0
# HELP mysql_global_variables_group_concat_max_len Generic gauge metric from SHOW GLOBAL VARIABLES.
# TYPE mysql_global_variables_group_concat_max_len gauge
mysql_global_variables_group_concat_max_len 1024
# HELP mysql_global_variables_gtid_executed_compression_period Generic gauge metric from SHOW GLOBAL VARIABLES.
# TYPE mysql_global_variables_gtid_executed_compression_period gauge
mysql_global_variables_gtid_executed_compression_period 1000
# HELP mysql_global_variables_gtid_mode Generic gauge metric from SHOW GLOBAL VARIABLES.
# TYPE mysql_global_variables_gtid_mode gauge
......
过一会儿我们再去看 Prometheus 的 targets 中查看采集的目标数据:
可以看到配置的 自动发现这个 job 已经生效了。切换到 Graph 下面可以看到很多关于 MySQL 的指标数据,我们选择任意一个指标,比如 mysql_exporter_collector_duration_seconds
,然后点击执行就可以看到对应的数据图表了:
1.4、配置 Dashboard
选择导入ID为 「7362」 的模版。效果如下:
选择导入ID为 「6239」 的模版。效果如下:
3、Mongo
1.1、mongo_exporter
mongodb 没有自带 /metrics
接口供 Prometheus 使用,在这种情况下,我们就需要利用 exporter 服务来为 Prometheus 提供指标数据了,我们可以前往官方网站进行查看:https://prometheus.io/docs/instrumenting/exporters/。
这里我们选择 mongodb_exporter:https://hub.docker.com/r/noenv/mongo-exporter
1.2、构建 sidecar
这里通过 mongodb_exporter 的服务来监控 mongodb 服务,我们以 sidecar 的形式和主应用部署在同一个 Pod 中,比如我们这里来部署一个 mongodb ,并用 mongodb _exporter 的方式来采集监控数据供 Prometheus 使用。
如下资源清单文件:「promethues-mongo-deploy.yaml」
代码语言:javascript复制## Service
apiVersion: v1
kind: Service
metadata:
name: db-mongo
labels:
app: mongo
spec:
ports:
- name: mongo
port: 27017
targetPort: 27017
- name: prom
port: 9104
targetPort: 9104
selector:
app: mongo
---
## Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: db-mongo
labels:
app: mongo
spec:
replicas: 1
selector:
matchLabels:
app: mongo
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9104"
labels:
app: mongo
spec:
containers:
- name: mongo
image: mongo:4.2.5
command:
- sh
- -c
- "exec mongod -f /etc/mongod.conf"
ports:
- containerPort: 27017
resources:
limits:
cpu: 1000m
memory: 512Mi
requests:
cpu: 1000m
memory: 512Mi
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
tcpSocket:
port: 27017
readinessProbe:
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
tcpSocket:
port: 27017
volumeMounts:
- name: data
mountPath: /data/middleware-data/mongodb/
- name: config
mountPath: /etc/mongod.conf
subPath: mongodb.conf
- name: localtime
readOnly: true
mountPath: /etc/localtime
- name: mongo-exporter
image: noenv/mongo-exporter:latest
args:
[
"--web.listen-address=:9104",
"--mongodb.uri",
"mongodb://db-mongo:27017",
]
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 9104
volumes:
- name: data
persistentVolumeClaim:
claimName: mongo
- name: config
configMap:
name: mongo-config
- name: localtime
hostPath:
type: File
path: /etc/localtime
可以看到上面我们在 mongodb 这个 Pod 中包含了两个容器,一个就是 mongodb 本身的主应用,另外一个容器就是 mongodb _exporter。
1.3、测试
创建完成后,我们可以看到 mongodb 的 Pod 里面包含有两个容器:
代码语言:javascript复制$ kubectl get pods -n mall
NAME READY STATUS RESTARTS AGE
db-mongo-5596947577-7bspt 2/2 Running 4 4d5h
$ kubectl get svc -n mall
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
db-mongo ClusterIP 10.96.239.99 <none> 27017/TCP,9104/TCP 4d5h
我们可以通过 9104 端口来校验是否能够采集到数据:
代码语言:javascript复制$ curl 10.96.239.99:9104/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"} 7.2981e-05
go_gc_duration_seconds{quantile="0.25"} 0.000132539
go_gc_duration_seconds{quantile="0.5"} 0.000185705
go_gc_duration_seconds{quantile="0.75"} 0.000327893
go_gc_duration_seconds{quantile="1"} 0.002464757
go_gc_duration_seconds_sum 1.023320185
go_gc_duration_seconds_count 3033
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 12
......
过一会儿我们再去看 Prometheus 的 targets 中查看采集的目标数据:
可以看到配置的 自动发现这个 job 已经生效了。切换到 Graph 下面可以看到很多关于 mongodb 的指标数据,我们选择任意一个指标,比如 mongodb_instance_local_time
,然后点击执行就可以看到对应的数据图表了:
1.4、配置 Dashboard
选择导入ID为 「2583」 的模版。效果如下:
选择导入ID为 「7353」 的模版。效果如下:
4、RabbitMQ
1.1、rabbitmq_exporter
rabbitmq 没有自带 /metrics
接口供 Prometheus 使用,在这种情况下,我们就需要利用 exporter 服务来为 Prometheus 提供指标数据了。Prometheus 官方为许多应用就提供了对应的 exporter 应用,也有许多第三方的实现,我们可以前往官方网站进行查看:https://prometheus.io/docs/instrumenting/exporters/。
这里我们选择官方的 rabbitmq_exporter:https://github.com/kbudde/rabbitmq_exporter
1.2、构建 sidecar
这里通过 rabbitmq_exporter 的服务来监控 rabbitmq 服务,我们以 sidecar 的形式和主应用部署在同一个 Pod 中,比如我们这里来部署一个 rabbitmq,并用 rabbitmq_exporter 的方式来采集监控数据供 Prometheus 使用。
如下资源清单文件:「promethues-rabbitmq-deploy.yaml」
代码语言:javascript复制## Service
apiVersion: v1
kind: Service
metadata:
name: cloud-rabbitmq
labels:
app: rabbitmq
spec:
selector:
app: rabbitmq
ports:
- name: rabbitmq
port: 5672
targetPort: 5672
- name: rabbitmq-management
port: 15672
targetPort: 15672
- name: prom
port: 9419
targetPort: 9419
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: cloud-rabbitmq
spec:
rules:
- host: cloud-rabbitmq.mall.demo.7d.com
http:
paths:
- backend:
serviceName: cloud-rabbitmq
servicePort: 15672
---
## Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloud-rabbitmq
labels:
app: rabbitmq
spec:
replicas: 1
selector:
matchLabels:
app: rabbitmq
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9419"
labels:
app: rabbitmq
spec:
containers:
- name: rabbitmq
image: rabbitmq:3.7.15-management
ports:
- containerPort: 5672
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 500m
memory: 512Mi
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
tcpSocket:
port: 5672
readinessProbe:
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
tcpSocket:
port: 5672
volumeMounts:
- name: data
mountPath: /var/lib/rabbitmq/
- name: localtime
readOnly: true
mountPath: /etc/localtime
- name: rabbitmq-exporter
image: kbudde/rabbitmq-exporter:latest
env:
- name: RABBIT_URL
value: "http://cloud-rabbitmq:15672"
- name: RABBIT_USER
value: "guest"
- name: RABBIT_PASSWORD
value: "guest"
- name: PUBLISH_PORT
value: "9419"
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 9419
volumes:
- name: data
persistentVolumeClaim:
claimName: rabbitmq
- name: localtime
hostPath:
type: File
path: /etc/localtime
可以看到上面我们在 rabbitmq这个 Pod 中包含了两个容器,一个就是 edis 本身的主应用,另外一个容器就是 r_exporter。
1.3、测试
创建完成后,我们可以看到 rabbitmq 的 Pod 里面包含有两个容器:
代码语言:javascript复制$ kubectl get pods -n mall
NAME READY STATUS RESTARTS AGE
cloud-rabbitmq-559c99cffd-22m67 2/2 Running 2 42h
$ kubectl get svc -n mall
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cloud-rabbitmq ClusterIP 10.96.67.143 <none> 5672/TCP,15672/TCP,9419/TCP 3d7h
我们可以通过 9121 端口来校验是否能够采集到数据:
代码语言:javascript复制$ curl 10.96.67.143:9419/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"} 1.938e-05
go_gc_duration_seconds{quantile="0.25"} 3.0385e-05
go_gc_duration_seconds{quantile="0.5"} 5.2105e-05
go_gc_duration_seconds{quantile="0.75"} 8.8354e-05
go_gc_duration_seconds{quantile="1"} 0.00257348
go_gc_duration_seconds_sum 0.139414322
go_gc_duration_seconds_count 1358
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 14
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.14.3"} 1
......
过一会儿我们再去看 Prometheus 的 targets 中查看采集的目标数据:
可以看到配置的 自动发现这个 job 已经生效了。切换到 Graph 下面可以看到很多关于 rabbitmq 的指标数据,我们选择任意一个指标,比如 rabbitmq_fd_used
,然后点击执行就可以看到对应的数据图表了:
1.4、配置 Dashboard
选择导入ID为 「4279」 的模版。效果如下:
六、小结
Kubernetes 与 Prometheus 有着十分相似的历程,均是源自Google内部多年的运维经验,并且相继从CNCF基金会正式毕业。它们分别代表了云原生模式下容器编排以及监控的事实标准。
本文首先介绍了Kubernetes 下 Prometheus 自动发现的基本原理。然后介绍了几个常见中间件服务发现和监控对象是如何配置。最后通过Grafana可视化展示监控界面。
本文源码:
- https://github.com/zuozewei/blog-example/tree/master/Kubernetes/k8s-kube-promethues-auto-discover