13、Prometheus
13、Prometheus
Prometheus特性
- 一个多维的数据模型,具有由指标名称和键/值对标识的时间序列数据;
- 使用 PromOL 查询和聚合数据,可以非常灵活的对数据进行检索;
- 不依赖额外的数据存储,Prometheus 本身就是一个时序数据库,提供本地存储和分布式存储,并且每个Prometheus都是自治的;
- 应用程序暴露Metrics接口,Prometheus 通过基于 HTTP 的 Pull 模型采集数据,同时可以使用 PushGateway 进行Push数据;
- Prometheus 同时支持动态服务发现和静态配置发现目标机器;
- 支持多种图形和仪表盘,和Grafana堪称“绝配”。
1. Prometheus架构
- Prometheus Server:Prometheus 生态最重要的组件,主要用于抓取和存储时间序列数据,同时提供数据的查询和告警策略的配置管理;
- Alertmanager:Prometheus 生态用于告警的组件,Prometheus Server 会将告警发送给 Alertmanager,Alertmanager 根据路由配置,将告警信息发送给指定的人或组。Alertmanager 支持邮件、Webhook、微信、钉钉、短信等媒介进行告警通知;
- Grafana:用于展示数据,便于数据的查询和观测;
- Push Gateway:Prometheus 本身是通过 Pull 的方式拉取数据,但是有些监控数据可能是短期的,如果没有采集数据可能会出现丢失。Push Gateway 可以用来解决此类问题,它可以用来接收数据,也就是客户端可以通过 Push 的方式将数据推送到 Push Gateway,之后 Prometheus 可以通过 Pull 拉取该数据;
- Exporter:主要用来采集监控数据,比如主机的监控数据可以通过 node_exporter采集,MySQL 的监控数据可以通过 mysql_exporter 采集,之后 Exporter 暴露一个接口,比如/metrics,Prometheus 可以通过该接口采集到数据;
- PromQL:PromQL 其实不算 Prometheus 的组件,它是用来查询数据的一种语法,比如查询数据库的数据,可以通过 SQL 语句,查询 Loki 的数据,可以通过 LogQL,查询 Prometheus 数据的叫做 PromQL;
- Service Discovery:用来发现监控目标的自动发现,常用的有基于 Kubernetes、Consul、Eureka、文件的自动发现等。
2. 部署Prometheus
2.1 下载项目、拉取镜像
本次部署使用 Kube-Prometheus Stack 进行高可用安装
Kube-Prometheus 项目地址:https://github.com/prometheus-operator/kube-prometheus/
首先需要通过该项目地址,找到和自己 Kubernetes 版本对应的Kube Prometheus Stack的版本:
我的k8s版本是1.23.7,所以我进行clone release-0.11版本。
git clone -b release-0.11 https://github.com/prometheus-operator/kube-prometheus.git
#如果报错↓↓↓
Git默认的POST缓冲区大小可能不足以处理大的文件传输。你可以通过以下命令临时增加这个值:
git config --global http.postBuffer 524288000
这个命令将POST缓冲区大小设置为500MB。之后再尝试进行git clone。
进入到/root/kube-prometheus/manifests,grep出所需要用到的镜像,提前拉下来推送到本地Harbor仓库中。
[root@k8s-master01 manifests]# pwd
/root/kube-prometheus/manifests
[root@k8s-master01 manifests]# grep -n "image:" -r . --exclude-dir=setup/
./alertmanager-alertmanager.yaml:13: image: quay.io/prometheus/alertmanager:v0.24.0
./blackboxExporter-deployment.yaml:33: image: quay.io/prometheus/blackbox-exporter:v0.21.0
./blackboxExporter-deployment.yaml:60: image: jimmidyson/configmap-reload:v0.5.0
./blackboxExporter-deployment.yaml:88: image: quay.io/brancz/kube-rbac-proxy:v0.12.0
./grafana-deployment.yaml:33: image: grafana/grafana:8.5.5
./kubeStateMetrics-deployment.yaml:35: image: k8s.gcr.io/kube-state-metrics/kube-state-metrics:v2.5.0
./kubeStateMetrics-deployment.yaml:56: image: quay.io/brancz/kube-rbac-proxy:v0.12.0
./kubeStateMetrics-deployment.yaml:82: image: quay.io/brancz/kube-rbac-proxy:v0.12.0
./nodeExporter-daemonset.yaml:38: image: quay.io/prometheus/node-exporter:v1.3.1
./nodeExporter-daemonset.yaml:74: image: quay.io/brancz/kube-rbac-proxy:v0.12.0
./prometheus-prometheus.yaml:21: image: quay.io/prometheus/prometheus:v2.36.1
./prometheusAdapter-deployment.yaml:40: image: k8s.gcr.io/prometheus-adapter/prometheus-adapter:v0.9.1
./prometheusOperator-deployment.yaml:33: image: quay.io/prometheus-operator/prometheus-operator:v0.57.0
./prometheusOperator-deployment.yaml:56: image: quay.io/brancz/kube-rbac-proxy:v0.12.0
release-0.11版本项目文件中的镜像地址,以上镜像地址信息过滤重复后的结果如下(并更换了k8s.io的镜像源为docker hub的)。
docker pull quay.io/prometheus/alertmanager:v0.24.0
docker pull quay.io/prometheus/blackbox-exporter:v0.21.0
docker pull jimmidyson/configmap-reload:v0.5.0
docker pull quay.io/brancz/kube-rbac-proxy:v0.12.0
docker pull grafana/grafana:8.5.5
docker pull landv1001/kube-state-metrics:v2.5.0
docker pull quay.io/prometheus/node-exporter:v1.3.1
docker pull quay.io/prometheus/prometheus:v2.36.1
docker pull v5cn/prometheus-adapter:v0.9.1
docker pull quay.io/prometheus-operator/prometheus-operator:v0.57.0
#推送至镜像仓库
docker tag quay.io/prometheus/alertmanager:v0.24.0 10.0.0.138:5000/prometheus/alertmanager:v0.24.0
docker push 10.0.0.138:5000/prometheus/alertmanager:v0.24.0
docker tag quay.io/prometheus/blackbox-exporter:v0.21.0 10.0.0.138:5000/prometheus/blackbox-exporter:v0.21.0
docker push 10.0.0.138:5000/prometheus/blackbox-exporter:v0.21.0
docker tag jimmidyson/configmap-reload:v0.5.0 10.0.0.138:5000/prometheus/configmap-reload:v0.5.0
docker push 10.0.0.138:5000/prometheus/configmap-reload:v0.5.0
docker tag quay.io/brancz/kube-rbac-proxy:v0.12.0 10.0.0.138:5000/prometheus/kube-rbac-proxy:v0.12.0
docker push 10.0.0.138:5000/prometheus/kube-rbac-proxy:v0.12.0
docker tag grafana/grafana:8.5.5 10.0.0.138:5000/prometheus/grafana:8.5.5
docker push 10.0.0.138:5000/prometheus/grafana:8.5.5
docker tag landv1001/kube-state-metrics:v2.5.0 10.0.0.138:5000/prometheus/kube-state-metrics:v2.5.0
docker push 10.0.0.138:5000/prometheus/kube-state-metrics:v2.5.0
docker tag quay.io/prometheus/node-exporter:v1.3.1 10.0.0.138:5000/prometheus/node-exporter:v1.3.1
docker push 10.0.0.138:5000/prometheus/node-exporter:v1.3.1
docker tag quay.io/prometheus/prometheus:v2.36.1 10.0.0.138:5000/prometheus/prometheus:v2.36.1
docker push 10.0.0.138:5000/prometheus/prometheus:v2.36.1
docker tag v5cn/prometheus-adapter:v0.9.1 10.0.0.138:5000/prometheus/prometheus-adapter:v0.9.1
docker push 10.0.0.138:5000/prometheus/prometheus-adapter:v0.9.1
docker tag quay.io/prometheus-operator/prometheus-operator:v0.57.0 10.0.0.138:5000/prometheus/prometheus-operator:v0.57.0
docker push 10.0.0.138:5000/prometheus/prometheus-operator:v0.57.0
推送到本地仓库后记得修改各yaml种的镜像地址为本地仓库镜像地址
[root@k8s-master01 manifests]# cat 1.sh
#!/bin/bash
# Replace images in blackboxExporter-deployment.yaml
sed -i 's#image: quay.io/prometheus/alertmanager:v0.24.0#image: 10.0.0.138:5000/prometheus/alertmanager:v0.24.0#g' blackboxExporter-deployment.yaml
sed -i 's#image: quay.io/prometheus/blackbox-exporter:v0.21.0#image: 10.0.0.138:5000/prometheus/blackbox-exporter:v0.21.0#g' blackboxExporter-deployment.yaml
sed -i 's#image: jimmidyson/configmap-reload:v0.5.0#image: 10.0.0.138:5000/prometheus/configmap-reload:v0.5.0#g' blackboxExporter-deployment.yaml
sed -i 's#image: quay.io/brancz/kube-rbac-proxy:v0.12.0#image: 10.0.0.138:5000/prometheus/kube-rbac-proxy:v0.12.0#g' blackboxExporter-deployment.yaml
# Replace images in grafana-deployment.yaml
sed -i 's#image: grafana/grafana:8.5.5#image: 10.0.0.138:5000/prometheus/grafana:8.5.5#g' grafana-deployment.yaml
# Replace images in kubeStateMetrics-deployment.yaml
sed -i 's#image: k8s.gcr.io/kube-state-metrics/kube-state-metrics:v2.5.0#image: 10.0.0.138:5000/prometheus/kube-state-metrics:v2.5.0#g' kubeStateMetrics-deployment.yaml
sed -i 's#image: quay.io/brancz/kube-rbac-proxy:v0.12.0#image: 10.0.0.138:5000/prometheus/kube-rbac-proxy:v0.12.0#g' kubeStateMetrics-deployment.yaml
# Replace images in nodeExporter-daemonset.yaml
sed -i 's#image: quay.io/prometheus/node-exporter:v1.3.1#image: 10.0.0.138:5000/prometheus/node-exporter:v1.3.1#g' nodeExporter-daemonset.yaml
sed -i 's#image: quay.io/brancz/kube-rbac-proxy:v0.12.0#image: 10.0.0.138:5000/prometheus/kube-rbac-proxy:v0.12.0#g' nodeExporter-daemonset.yaml
# Replace images in prometheus-prometheus.yaml
sed -i 's#image: quay.io/prometheus/prometheus:v2.36.1#image: 10.0.0.138:5000/prometheus/prometheus:v2.36.1#g' prometheus-prometheus.yaml
# Replace images in prometheusAdapter-deployment.yaml
sed -i 's#image: k8s.gcr.io/prometheus-adapter/prometheus-adapter:v0.9.1#image: 10.0.0.138:5000/prometheus/prometheus-adapter:v0.9.1#g' prometheusAdapter-deployment.yaml
# Replace images in prometheusOperator-deployment.yaml
sed -i 's#image: quay.io/brancz/kube-rbac-proxy:v0.12.0#image: 10.0.0.138:5000/prometheus/kube-rbac-proxy:v0.12.0#g' prometheusOperator-deployment.yaml
2.2 部署Kube Prometheus Stack
安装Prometheus Operator
[root@k8s-master01 manifests]# pwd
/root/kube-prometheus/manifests
[root@k8s-master01 manifests]# kubectl create -f setup/
Tips:注意新版安装setup后,可能不会有 Pod,不影响继续安装即可
安装 Prometheus Stack
[root@k8s-master01 manifests]# pwd
/root/kube-prometheus/manifests
[root@k8s-master01 manifests]# kubectl create -f .
2.3 访问Grafana、Prometheus
查看 Prometheus 容器状态
新版添加了 networkpolicy,可能会导致 Grafana 无法访问,执行一下操作即可。
kubectl delete networkpolicy --all -n monitoring
将 Grafana 的 Service 改成 NodePort 类型,并查看暴露出的端口进行访问,通过 30535 端口即可访问到 Grafana 的 Web UI,默认账号密码admin/admin。
选择监控仪表盘
选择node节点查看
将 Prometheus 的 Service 改成 NodePort 类型,并查看暴露出的端口进行访问,通过 31974 端口即可访问到 Prometheus 的 Web UI。
kubectl edit svc prometheus-k8s -n monitoring
3. 云原生与非云原生监控流程
3.1 监控数据来源
Prometheus 通常采用 Pull 的形式来拉取数据,也就意味着被监控应用只要有一个能获取到监控数据的接口,就可以采集到监控数据。
云原生应用监控
基于云原生理念开发的程序自己会暴露 Metrics 接口,就像 Kubernetes 本身的组件、Etcd等,都有一个/metrics接口,Prometheus 只需要请求这个接口即可获取到相关数据。
非云原生应用监控
Kubernetes 节点(也就是服务器本身)肯定不是云原生应用,或者说它不算一个应用,而是一个系统,那么系统本身肯定没有类似的Metrics 接口。
那么就需要有一个 Exporter 的 Pod 采集宿主机信息,然后暴露给 Prometheus,此时可以使用专用于主机信息采集的 node-exporter 应用来采集数据,并暴露 Metrics 接口给 Prometheus。
安装Prometheus Stack 时,默认用 DaemonSet 安装了 node-exporter,它会在每个 Kubernetes 节点部署一个 node-exporter。
此时可以在每个宿主机看到node-exporter监听的9100端口,访问9100即可访问到主机的监控信息。
目前比较常用的Exporter工具
3.2 ServiceMonitor 自定义资源
3.2.1 了解 ServiceMonitor
Prometheus 有一个配置文件,用于配置需要监控哪些数据,或者配置一些告警策略。这个配置文件的维护非常麻烦,特别是监控项非常多的情况下,很容易出现配置错误,而在 Kubernetes上 部署 Prometheus,可以不用去维护这个配置文件,而是通过一个叫ServiceMonitor 的资源来自动发现监控目标并动态生成配置。
Kubelet 和 NodeExporter 的监控,都会有一个 ServiceMonitor
下图可以看到它的 kind 配置为 ServiceMonitor,并非标准的 Kubernetes 提供的资源类型,Prometheus Stack 也是一种 CRD 和 Operator 的实现。我们定义了一个类型为 ServiceMonitor 的自定义资源,Prometheus 的 Operator 会解析该资源类型的配置,然后动态生成 Prometheus 的配置,这就是 ServiceMonitor 的作用,它可以让我们用资源定义的方式去配置 Prometheus 需要监控谁,不用再处理成百上千行的 Prometheus 配置,也不需要每次更改 Prometheus 配置后重启 Prometheus 实例。
通过上图定义的 selector 来匹配符合该 Label 的 Service,从而获取 endpoint 中的信息。
3.2.2 自动发现机制
ServiceMonitor 使用以下机制来实现自动发现和配置:
- 标签选择器(Label Selectors): 在 ServiceMonitor 的规范中,您可以定义一个或多个标签选择器。这些选择器用于匹配具有特定标签的 Service 和 Endpoint 资源。当与这些选择器匹配的资源发生变化时,Prometheus Operator 将自动更新相关的监控配置。
- 目标生成(Target Generation): Prometheus Operator 根据 ServiceMonitor 和匹配的 Service、Endpoint 资源生成监控目标的配置。这包括目标的URL、标签和其他指标收集配置的详细信息。
- 动态配置更新: Prometheus Operator 会持续监控 ServiceMonitor 及其关联的资源。当 Service 或 Endpoint 的标签、地址、端口等信息发生变化时,Prometheus Operator 会自动检测到这些更改并更新相关的监控配置。
- 自动注销: 如果 Service 或 Endpoint 资源被删除,Prometheus Operator 会自动停止相关的监控目标和指标收集任务。这确保了无效的监控目标不会继续占用资源或导致错误。
3.2.3 监控流程图
对于不同的应用,可以采用不同的管控流程,具体说明如下:
- 云原生应用:找到本身提供的 Metrics 接口,然后配置一个 Service 指向该应用的Pod,最后创建一个 ServiceMonitor 指定该应用的 Service 即可完成监控数据的采集。
- 非云原生应用:需要找到合适该应用的 Exporter 的 Pod,之后 Exporter 链接至该应用并采集相关数据,然后配置一个 Service 指向该 Exporter 的 Pod,最后创建一个 ServiceMonitor 指定该 Exporter 的 Service 即可完成监控数据的采集。
3.2.4 ServiceMonitor配置解析
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor #定义了资源的类型为ServiceMonitor
metadata:
labels: #为ServiceMonitor定义了标签
k8s-app: elasticsearch-exporter
release: es-exporter
name: es-elasticsearch-exporter #定义了ServiceMonitor的名称
namespace: monitoring #指定了资源所属的命名空间
spec:
endpoints: #定义了监控的终端信息
- honorLabels: true #则通过保留已抓取数据的标签值,并忽略冲突的服务器端标签来解决标签冲突。
interval: 10s #指定了获取指标的间隔时间为10秒
path: /metrics #指定了用于获取指标的路径
port: https #指定了指标收集的端口,可以名称或者端口号
scheme: https #指定了使用HTTPS协议进行指标收集
tlsConfig: #定义了TLS连接的配置信息
insecureSkipVerify: true #允许在连接时跳过证书验证
jobLabel: app.kubernetes.io/name #指定了用于标识工作的标签
namespaceSelector: #监控目标Service所在的命名空间
matchNames: #匹配具有下列标签的Service资源用于监控
- monitoring
selector: #指定了要监控的Service资源的标签选择器
matchLabels: #匹配具有下列标签的Service资源用于监控
k8s-app: elasticsearch-service
4. 云原生应用监控etcd实战
4.1 测试Metrics
测试访问 Etcd Metrics 接口
curl -s --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key https://10.0.0.104:2379/metrics -k | tail -2
证书的位置查询如下
#kubeadm安装方式
grep -E "key-file|cert-file" /etc/kubernetes/manifests/etcd.yaml
- --cert-file=/etc/kubernetes/pki/etcd/server.crt
- --key-file=/etc/kubernetes/pki/etcd/server.key
#二进制安装方式,找不到就通过systemctl status etcd查看指定的配置文件
grep -E "key-file|cert-file" /etc/etcd/etcd.config.yml
4.2 创建Service
首先需要配置 Etcd 的 Service 和 Endpoint,需要注意将下列IP改成自己的 Etcd 主机 IP,另外需要注意 port 的名称为 https-metrics,需要和后面的 ServiceMonitor 保持一致。
# vim etcd-svc.yaml
apiVersion: v1
kind: Endpoints
metadata:
labels:
app: etcd-prom
name: etcd-prom
namespace: kube-system
subsets:
- addresses:
- ip: 10.0.0.104
- ip: 10.0.0.105
- ip: 10.0.0.106
ports:
- name: https-metrics
port: 2379 # etcd 端口
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
labels:
app: etcd-prom
name: etcd-prom
namespace: kube-system
spec:
ports:
- name: https-metrics
port: 2379
protocol: TCP
targetPort: 2379
type: ClusterIP
创建该资源并查看 Service 的 ClusterIP
[root@k8s-master01 ~]# kubectl create -f etcd-svc.yaml
endpoints/etcd-prom created
[root@k8s-master01 ~]# kubectl get svc -n kube-system etcd-prom 或 get -f etcd-svc.yaml
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
etcd-prom ClusterIP 192.168.154.148 <none> 2379/TCP 14m
通过 ClusterIP 访问测试
[root@k8s-master01 kube-prometheus]# curl -s --cert /etc/kubernetes/pki/etcd/server.crt --key
4.3 创建Secret
创建 Etcd 证书(Prometheus需要etcd的证书)的 Secret(证书路径根据实际环境进行更改)
kubectl create secret generic etcd-ssl --from-file=/etc/kubernetes/pki/etcd/ca.crt --from-file=/etc/kubernetes/pki/etcd/server.crt --from-file=/etc/kubernetes/pki/etcd/server.key -n monitoring
4.4 挂载 Secret 到 Prometheus 容器
将证书挂载至Prometheus容器(由于Prometheus是Operator部署的,所以只需要修改Prometheus资源即可)
kubectl edit -n monitoring prometheus
保存退出后,Prometheus 的 Pod 会自动重启,重启完成后,查看证书是否挂载(任意一个 Prometheus 的 Pod 均可)
[root@k8s-master01 kube-prometheus]# kubectl exec -n monitoring prometheus-k8s-0 -c prometheus -- ls /etc/prometheus/secrets/etcd-ssl/
ca.crt
server.crt
server.key
4.5 创建监控 Etcd 的 ServiceMonitor
监控Etcd的ServiceMonitor配置如下:
#cat servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: etcd
namespace: monitoring
labels:
app: etcd
spec:
endpoints:
- interval: 30s
port: https-metrics # 这个 port 对应 Service.spec.ports.name
scheme: https
tlsConfig:
caFile: /etc/prometheus/secrets/etcd-ssl/ca.crt #证书路径
certFile: /etc/prometheus/secrets/etcd-ssl/server.crt
keyFile: /etc/prometheus/secrets/etcd-ssl/server.key
insecureSkipVerify: true # 关闭证书校验
selector:
matchLabels:
app: etcd-prom #跟Service的labels保持一致
namespaceSelector:
matchNames:
- kube-system
创建ServiceMonitor
kubectl create -f servicemonitor.yaml
4.6 查看Prometheus并Grafana展示
访问 10.0.0.236:31648 进入 Prometheus 查看监控目标,状态已经up!
尝试查询数据
配置Grafana模板 也可以使用官方链接下载再导入
Tips:数据流 etcd(云原生应用)--> Service --> ServiceMonitor --> Prometheus
5. 非云原生应用监控MySQL实战
5.1 部署MySQL
首先部署 MySQL 至 Kubernetes 集群中
kubectl create deploy mysql --image=10.0.0.138:5000/library/mysql:5.7.23 -n kube-public
设置密码
kubectl -n kube-public set env deploy/mysql MYSQL_ROOT_PASSWORD=mysql
查看 Pod 是否正常
5.2 创建Service
创建 Service 暴露 MySQL
kubectl -n kube-public expose deploy mysql --port 3306
查询该 Service 的 Labels,后续可以给 ServiceMonitor 进行匹配
kubectl get svc -n kube-public --show-labels
检查 Service 是否可用,没问题!
5.3 配置MySQL权限
语句如下:
CREATE USER 'exporter'@'%' IDENTIFIED BY 'exporter' WITH MAX_USER_CONNECTIONS 3;
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'%';
5.4 配置MySQL Exporter采集MySQL监控数据
yaml 配置如下,创建MySQL Exporter的 Deployment 和 Service,这两者都需要在 Prometheus 所在的 Namespace 中。
注意 DATA_SOURCE_NAME 的配置,需要将 exporter:exporter@(mysql.kube-public:3306)/改成自己的实际配置。
格式如下:
USERNAME:PASSWORD@MYSQL_HOST_ADDRESS:MYSQL_PORT
USERNAME:PASSWORD@Service名称.NameSpace命名空间:MYSQL_PORT
# vim mysql-exporter.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-exporter
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
k8s-app: mysql-exporter
template:
metadata:
labels:
k8s-app: mysql-exporter
spec:
containers:
- name: mysql-exporter
image: registry.cn-beijing.aliyuncs.com/dotbalo/mysqld-exporter
env:
- name: DATA_SOURCE_NAME
value: "exporter:exporter@(mysql.kube-public:3306)/"
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9104
---
apiVersion: v1
kind: Service
metadata:
name: mysql-exporter
namespace: monitoring
labels:
k8s-app: mysql-exporter
spec:
type: ClusterIP
selector:
k8s-app: mysql-exporter #选择deployment标签为mysql-exporter
ports:
- name: api
port: 9104
protocol: TCP
mysql-exporter镜像拉取地址:registry.cn-beijing.aliyuncs.com/dotbalo/mysqld-exporter
创建MySQL Exporter的Deployment和Service
kubectl create -f mysql-exporter.yaml
通过该 Service 地址,检查是否能正常获取 Metrics 数据,没问题!
[root@k8s-master01 ~]# curl -s 192.168.88.216:9104/metrics | tail -1
promhttp_metric_handler_requests_total{code="503"} 0
5.5 创建监控MySQL的ServiceMonitor
监控MySQL的ServiceMonitor配置如下:
#cat mysql-sm.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: mysql-exporter
namespace: monitoring
labels:
k8s-app: mysql-exporter
namespace: monitoring
spec:
endpoints:
- port: api #使用名称连接,以后这里改了也不用去修改ServiceMonitor的配置
interval: 30s
scheme: http
selector:
matchLabels:
k8s-app: mysql-exporter #跟mysql-exporter的Service的labels保持一致
namespaceSelector:
matchNames:
- monitoring
创建ServiceMonitor
kubectl create -f mysql-sm.yaml
尝试查询数据
配置Grafana模板
Tips:数据流 MySQL(非云原生应用)--> 应用Service --> Exporter --> Export Service(做引流) --> ServiceMonitor --> Prometheus
6. Service Monitor监控问题排查
6.1 发现问题
安装Prometheus后,kube-controller-manager和kube-scheduler可能处于无法监控的状态,也就是找不到目标主机。在Alerts页面也能看到Controller Manager和Scheduler的告警。
6.2 排查问题流程
通过ServiceMonitor监控应用时,如果监控没有找到目标主机的排查步骤,排查步骤大致如下:
- 确认ServiceMonitor是否成功创建。
- 确认Prometheus是否生成了相关配置。
- 确认存在ServiceMonitor匹配的Service。
- 确认通过Service能够访问程序的Metrics接口。
- 确认Service的端口和Scheme、ServiceMonitor一致。
6.3 排查kube-controller-manager案例
Prometheus是有相关监控配置的,说明ServiceMonitor已经创建成功,可以通过以下命令查看:
kubectl get servicemonitor -n monitoring
Prometheus中也有监控配置信息
查看 kube-controller-manager 的 ServiceMonitor 配置,看到该 ServiceMonitor 匹配的是 kube-system 命名空间下具有 app.kubernetes.io/name=kubecontroller-manager 的标签。
kubectl get servicemonitor -n monitoring kube-controller-manager -oyaml
接下来通过该标签查看是否有该Service,可以看到并没有此标签的Service,所以导致找不到需要监控的目标。
kubectl get svc -n kube-system -l app.kubernetes.io/name=kube-controller-manager
手动创建该Service和Endpoint,配置中IP填写自己的Controller Manager节点IP
#vim kube-controller-manager-svc.yaml
apiVersion: v1
kind: Endpoints
metadata:
labels:
app.kubernetes.io/name: kube-controller-manager
name: kube-controller-manager-prom
namespace: kube-system
subsets:
- addresses:
- ip: 10.0.0.104
- ip: 10.0.0.105
- ip: 10.0.0.106
ports:
- name: https-metrics
port: 10257
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: kube-controller-manager
name: kube-controller-manager-prom
namespace: kube-system
spec:
ports:
- name: https-metrics
port: 10257
protocol: TCP
targetPort: 10257
sessionAffinity: None
type: ClusterIP
创建Service
kubectl create -f controller-manager-service.yaml
此时该Service可能是不通的,因为在集群搭建时,Controller Manager和Scheduler监听的可能是127.0.0.1,导致无法被外部访问,此时需要更改它的监听地址为0.0.0.0。
#二进制部署方式
sed -i "s#address=127.0.0.1#address=0.0.0.0#g" /usr/lib/systemd/system/kube-controller-manager.service
systemctl daemon-reload
systemctl restart kube-controller-manager
#kubeadm部署方式
#kubeadm安装方式配置文件在/etc/kubernetes/manifests目录
#三个Master节点都需要进行
sed -i "s#address=127.0.0.1#address=0.0.0.0#g" /etc/kubernetes/manifests/kube-controller-manager.yaml
Tips:如果集群原本监听的就是0.0.0.0,则无须更改!
由于 manifests 目录下是以静态 Pod 运行在集群中的,所以只要修改静态 Pod 目录下对应的 yaml 文件即可。等待一会后,对应服务会自动重启,所以不需要我们手动重启。(最后一列能看到是刚重新启动的)
#如果需要重启
#kubeadm部署方式
systemctl restart kubelet
#二进制部署方法
systemctl daemon-reload
systemctl restart kube-controller-manager
查看Prometheus,监控正常,告警消除。
Grafana显示正常
7. 黑盒监控与静态配置
7.1 白盒与黑盒监控概念
白盒监控:MySQL或者Etcd的监控都是监控应用本身,也就是程序内部的一些指标,这类监控关注的是原因,一般为出现问题的根本。
黑盒监控:关注的是现象,也就是正在发生的告警,比如某个网站突然慢了,或者打不开了。此类告警是站在用户的角度看到的东西,比较关注现象,表示正在发生的问题。
白盒监控可以通过Exporter采集数据,黑盒监控也可以通过Exporter采集数据,新版本的Prometheus Stack已经默认安装了Blackbox Exporter,可以用其采集某个域名、接口或者TCP连接的状态、是否可用等。
7.2 黑盒监控使用
新版 Prometheus Stack 已经默认安装了 Blackbox Exporter,可以通过以下命令查看:
kubectl get po -n monitoring -l app.kubernetes.io/name=blackbox-exporter
如果集群中没有配置Blackbox Exporter,可以参考https://github.com/prometheus/blackbox_exporter进行安装。
同时也会创建一个 Service,可以通过该 Service 访问 Blackbox Exporter 。
[root@k8s-master01 kube-prometheus]# kubectl get svc -n monitoring -l app.kubernetes.io/name=blackbox-exporter
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
blackbox-exporter ClusterIP 192.168.229.51 <none> 9115/TCP,19115/TCP 4h12m
检测下 www.baidu.com(使用任何一个公网域名或者公司内的域名探测即可)网站的状态,可以通过如下命令进行检查
[root@k8s-master01 kube-prometheus]# curl -s "http://192.168.229.51:19115/probe?target=www.baidu.com&module=http_2xx" | tail -1
probe_success 1
Tips:probe 是接口地址,target 是检测的目标,module 是使用哪个模块进行探测。
7.3 Prometheus加载静态配置
7.3.1 为什么使用静态配置
之前配置监控目标时,用的都是ServiceMonitor,但是ServiceMonitor可能会有一些限制。比如,如果没有安装Prometheus Operator,可能就无法使用 ServiceMonitor,另外并不是所有的监控都能使用 ServiceMonitor 进行配置,或者使用 ServiceMonitor 配置显得过于烦琐。
黑盒监控使用 ServiceMonitor 就显得过于复杂,使用传统的配置方式,直接将 target 传递给 Blackbox Exporter 即可。虽然本篇使用的是 Operator 安装 Prometheus,但是它也是支持静态配置的,可以通过以下步骤开启 Prometheus 的静态配置。
7.3.2 创建Secret
首先创建一个空文件,然后通过该文件创建一个Secret,这个Secret即可作为Prometheus的静态配置:
[root@k8s-master01 blackbox]# touch prometheus-blackbox.yaml
[root@k8s-master01 blackbox]# kubectl create secret generic blackbox-configs --from-file=prometheus-blackbox.yaml -n monitoring
secret/blackbox-configs created
[root@k8s-master01 blackbox]# kubectl describe secrets -n monitoring blackbox-configs
Name: blackbox-configs
Namespace: monitoring
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
prometheus-blackbox.yaml: 0 bytes
7.3.3 Prometheus加载Secret
编辑下 Prometheus 配置,添加下面配置后保存退出,无需重启 Prometheus 的 Pod 即可生效。
kubectl edit prometheus -n monitoring k8s
#内容如下:
additionalScrapeConfigs: #是Prometheus的一个配置选项,用于指定额外的抓取配置。
name: blackbox-configs #引用secret
key: prometheus-blackbox.yaml #指定secret中的自定义的抓取配置文件
optional: true #即使额外配置文件不存在,Prometheus 也可以成功启动。false则是必须得加载成功才能启动Prometheus。
7.3.4 黑盒监控配置热更新
prometheus-blackbox.yaml文件内编辑一些静态配置,此处用黑盒监控的配置进行演示:
- job_name: 'blackbox'
metrics_path: /probe
params: #使用哪个模块进行探测
module: [http_2xx] # Look for a HTTP 200 response.
static_configs:
- targets: #探测的目标,根据实际情况进行更改
- http://www.cnblogs.com # Target to probe with http.
- https://www.baidu.com # Target to probe with https.
- https://www.qq.com # Target to probe with https.
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-exporter:19115 # Blackbox Exporter的地址
之后通过热更新该Secret将黑盒监控配置加载到Prometheus
kubectl create secret generic blackbox-configs --from-file=prometheus-blackbox.yaml --dry-run=client -oyaml | kubectl replace -f - -n monitoring
Tips:“ - ” 表示从标准输入中读取配置。
7.3.5 查看Prometheus、Grafana
执行下列热更新完成后,稍等一分钟即可在Prometheus Web UI看到该配置,监控状态为UP。
导入Grafana模板
8.Alertmanager告警实战
8.1 Alertmanager配置文件解析
Alertmanager 的配置示例
# global块配置下的配置选项在本配置文件内的所有配置项下可见
global:
# 在Alertmanager内管理的每一条告警均有两种状态: "resolved"或者"firing"。在altermanager首次发送告警通知后, 该告警会一直处于firing状态,设置resolve_timeout可以指定处于firing状态的告警间隔多长时间会被设置为resolved状态, 在设置为resolved状态的告警后,altermanager不会再发送firing的告警通知。
resolve_timeout: 1h
# 邮件告警配置
smtp_smarthost: 'smtp.exmail.qq.com:25'
smtp_from: 'zzb@xxx.com'
smtp_auth_username: 'zzb@xxx.com'
smtp_auth_password: 'DKxxx'
wechat_api_url: 'https://qyapi.weixin.qq.com/cgi-bin/'
wechat_api_secret: 'JJ'
wechat_api_corp_id: 'ww'
# 告警通知模板
templates:
- '/etc/alertmanager/config/*.tmpl'
# route:
#1. 根路由,该模块用于该根路由下的节点及子路由routes的定义. 子树节点如果不对相关配置进行配置,则默认会从父路由树继承该配置选项。
#2. 每一条告警都要进入route,即要求配置选项group_by的值能够匹配到每一条告警的至少一个labelkey(即通过POST请求向altermanager服务接口所发送告警的labels项所携带的<labelname>)
#3. 告警进入到route后,将会根据子路由routes节点中的配置项match_re或者match来确定能进入该子路由节点的告警(由在match_re或者match下配置的labelkey: labelvalue是否为告警labels的子集决定,是的话则会进入该子路由节点,否则不能接收进入该子路由节点)。
route:
# 例如所有labelkey:labelvalue含cluster=A及altertname=LatencyHigh labelkey的告警都会被归入单一组中
group_by: ['job', 'altername', 'cluster', 'service','severity']
# 若一组新的告警产生,则会等group_wait后再发送通知,该功能主要用于当告警在很短时间内接连产生时,在group_wait内合并为单一的告警后再发送
group_wait: 30s
# 再次告警时间间隔
group_interval: 5m
# 如果一条告警通知已成功发送,且在间隔repeat_interval后,该告警仍然未被设置为resolved,则会再次发送该告警通知
repeat_interval: 12h
# 默认告警通知接收者,凡未被匹配进入各子路由节点的告警均被发送到此接收者
receiver: 'wechat'
# 上述route的配置会被传递给子路由节点,子路由节点进行重新配置才会被覆盖
# 子路由树
routes:
# 该配置选项使用正则表达式来匹配告警的labels,以确定能否进入该子路由树
# match_re和match均用于匹配labelkey为service,labelvalue分别为指定值的告警,被匹配到的告警会将通知发送到对应的receiver
- match_re:
service: ^(foo1|foo2|baz)$
receiver: 'team-ops-mails'
# 在带有service标签的告警同时有severity标签时,他可以有自己的子路由,同时具有severity != critical的告警则被发送给接收者team-ops-mails,对severity == critical的告警则被发送到对应的接收者即team-ops-pager
routes:
- match:
severity: critical
receiver: 'team-ops-pager'
# 比如关于数据库服务的告警,如果子路由没有匹配到相应的owner标签,则都默认由team-DB接收
- match:
service: database
receiver: 'team-DB'
# 抑制规则,当出现critical告警时,忽略warning
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
# 收件人配置
receivers:
- name: 'team-ops-mails'
email_configs:
- to: 'zzb@xxx.com'
- name: 'wechat'
wechat_configs:
- send_resolved: true
corp_id: 'ww'
api_secret: 'JJ'
to_tag: '1'
agent_id: '1000002'
api_url: 'https://qyapi.weixin.qq.com/cgi-bin/'
message: '{{ template "wechat.default.message" . }}'
- name: 'team-ops-pager'
email_configs:
- to: 'team-ops-pager@example.org'
pagerduty_configs:
- service_key: <team-X-key>
- name: 'team-DB'
email_configs:
- to: 'team-DB@example.org'
#
#- name: 'team-Y-pager'
# pagerduty_configs:
# - service_key: <team-Y-key>
#
#- name: 'team-X-pager'
# pagerduty_configs:
# - service_key: <team-X-key>
8.2 Alertmanager告警路由规则
route配置
route:
receiver: Default
group_by:
- namespace
- job
- alertname
routes:
- receiver: Watchdog
match:
alertname: Watchdog
- receiver: Critical
match:
severity: critical
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver:告警的通知目标,需要和 receivers 配置中 name 进行匹配。需要注意的是route.routes 下也可以有 receiver 配置,优先级高于 route.receiver 配置的默认接收人,当告警没有匹配到子路由时,会使用 route.receiver 进行通知,比如上述配置中的Default。
group_by:分组配置,值类型为列表。比如配置成['job', 'severity'],代表告警信息包含job 和 severity 标签的会进行分组,且标签的 key 和 value 都相同才会被分到一组。
continue:决定匹配到第一个路由后,是否继续后续匹配。默认为 false,即匹配到第一个子节点后停止继续匹配
match:一对一匹配规则,比如 match 配置的为 job: mysql,那么具有 job=mysql 的告警会进入该路由。
match_re:和 match 类似,只不过是 match_re 是正则匹配。
group_wait:告警通知等待,值类型为字符串。若一组新的告警产生,则会等 group_wait后再发送通知,该功能主要用于当告警在很短时间内接连产生时,在 group_wait 内合并为单一的告警后再发送,防止告警过多,默认值 30s。
group_interval:同一组告警通知后,如果有新的告警添加到该组中,再次发送告警通知的时间,默认值为 5m。
repeat_interval:如果一条告警通知已成功发送,且在间隔 repeat_interval 后,该告警仍然未被设置为 resolved,则会再次发送该告警通知,默认值 4h。
8.3 Alertmanager邮件通知
修改/root/kube-prometheus/manifests/alertmanager-secret.yaml
[root@k8s-master01 manifests]# cat alertmanager-secret.yaml
apiVersion: v1
kind: Secret
metadata:
labels:
app.kubernetes.io/component: alert-router
app.kubernetes.io/instance: main
app.kubernetes.io/name: alertmanager
app.kubernetes.io/part-of: kube-prometheus
app.kubernetes.io/version: 0.24.0
name: alertmanager-main
namespace: monitoring
stringData:
alertmanager.yaml: |-
"global":
"resolve_timeout": "5m"
smtp_from: "zzb_chen@163.com"
smtp_smarthost: "smtp.163.com:465"
smtp_hello: "163.com"
smtp_auth_username: "zzb_chen@163.com"
smtp_auth_password: "授权码"
smtp_require_tls: false
"inhibit_rules":
- "equal":
- "namespace"
- "alertname"
"source_matchers":
- "severity = critical"
"target_matchers":
- "severity =~ warning|info"
- "equal":
- "namespace"
- "alertname"
"source_matchers":
- "severity = warning"
"target_matchers":
- "severity = info"
- "equal":
- "namespace"
"source_matchers":
- "alertname = InfoInhibitor"
"target_matchers":
- "severity = info"
"receivers":
- "name": "Default"
email_configs:
- to: "szgetshell@163.com"
send_resolved: true
- "name": "Watchdog"
- "name": "Critical"
- "name": "null"
"route":
"group_by":
- "namespace"
- "job"
- "alertname"
"group_interval": "5m"
"group_wait": "30s"
"receiver": "Default"
"repeat_interval": "12h"
"routes":
- "matchers":
- "alertname = InfoInhibitor"
"receiver": "null"
- "matchers":
- "severity = critical"
"receiver": "Critical"
type: Opaque
主要上面文件中,修改了这些部分。
#在global中配置发件人信息
smtp_from: "k8s_prome_test@163.com"
smtp_smarthost: "smtp.163.com:465"
smtp_hello: "163.com"
smtp_auth_username: "k8s_prome_test@163.com"
smtp_auth_password: "授权密码"
smtp_require_tls: false
#告警分组
group_by:
- namespace
- job
- alertname
#默认没有告警路由到的都发到这个Default上
- "name": "Default"
email_configs:
- to: "k8s_prome_test@163.com" #收件人,可以配置多个,逗号隔开。
send_resolved: true #告警如果被解决是否发送解决通知。
Alertmanager访问
可以通过 Alertmanager 提供的 Web UI 查看分组信息,和 Prometheus 一致,将 Alertmanager 的 Service 更改为 NodePort:
kubectl edit svc -n monitoring alertmanager-main
查看监听的端口号
kubectl get svc -n monitoring alertmanager-main
将更改好的 Alertmanager 配置加载到 Alertmanager
kubectl replace -f alertmanager-secret.yaml
稍等几分钟即可在 Alertmanager 的 Web 界面看到更改的配置(Status)
邮箱也收到了已有的告警信息
8.4 自定义告警模板
8.4.1 简易告警模板
配置一个简易的告警模板,在之前配置邮件通知的基础上继续进行。
[root@k8s-master01 manifests]# cat alertmanager-secret.yaml
apiVersion: v1
kind: Secret
metadata:
labels:
app.kubernetes.io/component: alert-router
app.kubernetes.io/instance: main
app.kubernetes.io/name: alertmanager
app.kubernetes.io/part-of: kube-prometheus
app.kubernetes.io/version: 0.24.0
name: alertmanager-main
namespace: monitoring
stringData:
demo.tmpl: |-
{{ define "test.html" }}
{{- if gt (len .Alerts.Firing) 0 -}}
{{- range $index, $alert := .Alerts -}}
========= 告警通知 ==========<br>
告警名称:{{ .Labels.alertname }}<br>
告警级别:{{ .Labels.severity }}<br>
告警机器:{{ .Labels.instance }} {{ .Labels.device }}<br>
告警情况:{{ .Annotations.summary }}<br>
详细信息:{{ .Annotations.description }}<br>
告警时间:{{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}<br>
========= END ==========<br>
{{- end }}
{{- end }}
{{- if gt (len .Alerts.Resolved) 0 -}}
{{- range $index, $alert := .Alerts -}}
========= 告警恢复 ==========<br>
告警名称:{{ .Labels.alertname }}<br>
告警级别:{{ .Labels.severity }}<br>
告警机器:{{ .Labels.instance }}<br>
告警情况:{{ .Annotations.summary }}<br>
详细信息:{{ .Annotations.description }}<br>
告警时间:{{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}<br>
恢复时间:{{ (.EndsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}<br>
========= END ==========<br>
{{- end }}
{{- end }}
{{- end }}
alertmanager.yaml: |-
"global":
"resolve_timeout": "5m"
smtp_from: "k8s_prome_test@163.com"
smtp_smarthost: "smtp.163.com:465"
smtp_hello: "163.com"
smtp_auth_username: "k8s_prome_test@163.com"
smtp_auth_password: "授权码"
smtp_require_tls: false
templates:
- '/etc/alertmanager/config/*.tmpl'
"inhibit_rules":
- "equal":
- "namespace"
- "alertname"
"source_matchers":
- "severity = critical"
"target_matchers":
- "severity =~ warning|info"
- "equal":
- "namespace"
- "alertname"
"source_matchers":
- "severity = warning"
"target_matchers":
- "severity = info"
- "equal":
- "namespace"
"source_matchers":
- "alertname = InfoInhibitor"
"target_matchers":
- "severity = info"
"receivers":
- "name": "Default"
email_configs:
- to: "k8s_prome_test@163.com"
html: '{{ template "test.html" . }}'
send_resolved: true
- "name": "Watchdog"
- "name": "Critical"
- "name": "null"
"route":
"group_by":
- "namespace"
- "job"
- "alertname"
"group_interval": "5m"
"group_wait": "30s"
"receiver": "Default"
"repeat_interval": "1m"
"routes":
- "matchers":
- "alertname = InfoInhibitor"
"receiver": "null"
- "matchers":
- "severity = critical"
"receiver": "Critical"
type: Opaque
主要上面文件中,修改了这些部分。
#添加告警模板,定义为test.html
demo.tmpl: |-
{{ define "test.html" }}
{{- if gt (len .Alerts.Firing) 0 -}}
{{- range $index, $alert := .Alerts -}}
========= 告警通知 ==========<br>
告警名称:{{ .Labels.alertname }}<br>
告警级别:{{ .Labels.severity }}<br>
告警机器:{{ .Labels.instance }} {{ .Labels.device }}<br>
告警情况:{{ .Annotations.summary }}<br>
详细信息:{{ .Annotations.description }}<br>
告警时间:{{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}<br>
========= END ==========<br>
{{- end }}
{{- end }}
{{- if gt (len .Alerts.Resolved) 0 -}}
{{- range $index, $alert := .Alerts -}}
========= 告警恢复 ==========<br>
告警名称:{{ .Labels.alertname }}<br>
告警级别:{{ .Labels.severity }}<br>
告警机器:{{ .Labels.instance }}<br>
告警情况:{{ .Annotations.summary }}<br>
详细信息:{{ .Annotations.description }}<br>
告警时间:{{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}<br>
恢复时间:{{ (.EndsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}<br>
========= END ==========<br>
{{- end }}
{{- end }}
{{- end }}
#加载模板
templates:
- '/etc/alertmanager/config/*.tmpl'
#使用email告警模板,test.html与模板中定义的一样。
html: '{{ template "test.html" . }}'
将更改好的 Alertmanager 配置加载到 Alertmanager
kubectl replace -f alertmanager-secret.yaml
效果展示
8.4.2 美化告警模板
与上面一样,添加告警模板,定义为123.html,同时email_configs中也需要修改对应模板名称。
[root@k8s-master01 manifests]# cat alertmanager-secret.yaml
apiVersion: v1
kind: Secret
metadata:
labels:
app.kubernetes.io/component: alert-router
app.kubernetes.io/instance: main
app.kubernetes.io/name: alertmanager
app.kubernetes.io/part-of: kube-prometheus
app.kubernetes.io/version: 0.24.0
name: alertmanager-main
namespace: monitoring
stringData:
123.tmpl: |-
{{ define "123.html" }}
{{- if gt (len .Alerts.Firing) 0 -}}
{{- range $index, $alert := .Alerts -}}
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title></title>
<link rel="shortcut icon" href="">
<style>
@font-face {
font-family: Unicons;
src: url(fonts/Unicons.woff2) format("woff2"), url(fonts/Unicons.woff) format("woff");
font-weight: 400;
font-style: normal;
font-display: block
}
@font-face {
font-family: Custom;
src: url(fonts/Custom.woff2) format("woff2"), url(fonts/Custom.woff) format("woff");
}
.img-mask.mask-1 img {
-webkit-mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/blob.svg) !important;
mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/blob.svg) !important;
}
.img-mask.mask-2 img {
-webkit-mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/hex.svg) !important;
mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/hex.svg) !important;
}
.img-mask.mask-3 img {
-webkit-mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/blob2.svg) !important;
mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/blob2.svg) !important;
}
</style>
<style type="text/css">
* {
box-sizing: border-box;
}
body {
margin: 0;
}
* {
box-sizing: border-box;
}
body {
margin-top: 0px;
margin-right: 0px;
margin-bottom: 0px;
margin-left: 0px;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft Yahei", "Microsoft Jhenghei", sans-serif;
}
.logo path {
pointer-events: none;
fill: none;
stroke-linecap: round;
stroke-width: 7;
stroke: rgb(255, 255, 255);
}
.wechat-group img {
max-width: 220px;
height: auto;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
margin-top: 0px;
margin-right: auto;
margin-bottom: 0px;
margin-left: auto;
width: 100%;
}
.welcome-img img {
width: 100%;
}
.hover-underline:hover {
text-decoration-line: underline !important;
text-decoration-thickness: initial !important;
text-decoration-style: initial !important;
text-decoration-color: initial !important;
}
.c3402 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
display: block;
font-size: 16px;
font-weight: 600;
line-height: 100%;
color: #ffffff;
text-decoration: none;
background-color: #7367f0;
text-align: center;
padding: 12px 16px 12px 16px;
float: none;
}
.c3394 {
mso-line-height-rule: exactly;
mso-padding-alt: 16px 24px;
border-radius: 4px;
background-color: #7367f0;
font-family: Montserrat, -apple-system, 'Segoe UI', sans-serif;
}
.c3476 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
height: 1px;
background-color: #eceff1;
line-height: 1px;
}
.c3468 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
padding-top: 32px;
padding-bottom: 32px;
}
.c3533 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
color: #7367f0;
text-decoration: none;
}
.c3217 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
}
.c3244 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
margin: 0;
margin-top: 24px;
margin-bottom: 24px;
}
.c3289 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
margin-bottom: 0;
font-size: 18px;
font-weight: 500;
}
.c3307 {
margin-bottom: 24px;
}
.c3444 {
width: 100%;
}
.c3516 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
margin: 0;
margin-bottom: 16px;
}
.c3572 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
margin: 0;
margin-bottom: 16px;
}
.c3164 {
mso-line-height-rule: exactly;
border-radius: 4px;
background-color: #ffffff;
padding: 48px;
text-align: left;
font-family: Montserrat, -apple-system, 'Segoe UI', sans-serif;
font-size: 16px;
line-height: 24px;
color: #626262;
}
.c3708 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
height: 20px;
}
.c4009 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
height: 16px;
}
.c3140 {
width: 100%;
}
.c3132 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
}
.c3045 {
width: 600px;
}
.c3037 {
mso-line-height-rule: exactly;
background-color: #eceff1;
font-family: Montserrat, -apple-system, 'Segoe UI', sans-serif;
}
.c3013 {
width: 100%;
font-family: Montserrat, -apple-system, 'Segoe UI', sans-serif;
}
.c2987 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
display: none;
}
.c3005 {
font-family: 'Montserrat', sans-serif;
mso-line-height-rule: exactly;
}
.row {
display: table;
padding-top: 10px;
padding-right: 10px;
padding-bottom: 10px;
padding-left: 10px;
width: 100%;
}
.cell {
width: 8%;
display: table-cell;
height: 75px;
}
@media (max-width: 768px) {
.cell {
width: 100%;
display: block;
}
}
@media (max-width: 600px) {
.sm-w-full {
width: 100% !important;
}
.sm-px-24 {
padding-left: 24px !important;
padding-right: 24px !important;
}
}
</style>
</head>
<body>
<body>
<meta charset="utf-8" />
<meta name="x-apple-disable-message-reformatting" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no" />
<title>欢迎使用HTMLPAGE模板 👋</title>
<div role="article" aria-roledescription="email" aria-label="欢迎使用HTMLPAGE模板 👋" lang="en" id="i7m0o" class="c3005">
<table cellpadding="0" cellspacing="0" role="presentation" id="ivm4l" class="c3013">
<tbody>
<tr>
<td align="center" id="iateu" class="c3037">
<table cellpadding="0" cellspacing="0" role="presentation" id="i4cwv" class="sm-w-full c3045">
<tbody>
<tr></tr>
<tr>
<td align="center" id="itbcd" class="sm-px-24 c3132">
<table cellpadding="0" cellspacing="0" role="presentation" id="i53eh" class="c3140">
<tbody>
<tr>
<td id="i2lri" class="sm-px-24 c3164">
<!-- <p style="font-family: 'Montserrat', sans-serif; mso-line-height-rule: exactly; margin-top: 12px; font-size: 20px; font-weight: 700; color: #263238;">陈先生!</p> -->
<div class="row">
<div class="cell"><a href="" class="c3402">告警通知</a>
<div class="row"></div>
</div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
</div><a href="https://htmlpage.cn" class="c3217"></a>
<p id="i19pj" class="c3244"></p>
<p id="ilj5l" class="c3289"></p>
<ul id="if9r7" class="c3307">
<li>告警名称:{{ .Labels.alertname }}</li>
<li>告警级别:{{ .Labels.severity }}</li>
<li>告警机器:{{ .Labels.instance }} {{ .Labels.device }}</li>
<li>告警信息:{{ .Annotations.summary }}</li>
<li>告警详情:{{ .Annotations.description }}</li>
<li>告警时间:{{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}</li>
</ul>
<table cellpadding="0" cellspacing="0" role="presentation">
<tbody>
<tr>
<td id="iz67p" class="c3394"></td>
</tr>
</tbody>
</table>
<table cellpadding="0" cellspacing="0" role="presentation" id="i7dvy" class="c3444">
<tbody>
<tr>
<td id="i0ol4" class="c3468">
<div id="ifrkk" class="c3476"></div>
</td>
</tr>
</tbody>
</table>
<p id="i9omt" class="c3516">处理过程中如遇到问题,请与我们联系
<a href="mailto:k8s_prome_test@163.com.com" class="hover-underline c3533">回复邮件</a>。
</p>
<p id="inqma" class="c3572"> — 运维团队</p>
</td>
</tr>
<tr>
<td id="ir37w" class="c3708"></td>
</tr>
<tr></tr>
<tr>
<td id="ixrj3" class="c4009"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</body>
<html>
{{- end }}
{{- end }}
{{- if gt (len .Alerts.Resolved) 0 -}}
{{- range $index, $alert := .Alerts -}}
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title></title>
<link rel="shortcut icon" href="">
<style>
@font-face {
font-family: Unicons;
src: url(fonts/Unicons.woff2) format("woff2"), url(fonts/Unicons.woff) format("woff");
font-weight: 400;
font-style: normal;
font-display: block
}
@font-face {
font-family: Custom;
src: url(fonts/Custom.woff2) format("woff2"), url(fonts/Custom.woff) format("woff");
}
.img-mask.mask-1 img {
-webkit-mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/blob.svg) !important;
mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/blob.svg) !important;
}
.img-mask.mask-2 img {
-webkit-mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/hex.svg) !important;
mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/hex.svg) !important;
}
.img-mask.mask-3 img {
-webkit-mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/blob2.svg) !important;
mask-image: url(https://gitee.com/ggbaoo/k8s-img/raw/master/blob2.svg) !important;
}
</style>
<link rel="stylesheet" href="./css/user-style.css">
</head>
<body>
<body>
<meta charset="utf-8" />
<meta name="x-apple-disable-message-reformatting" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no" />
<title>欢迎使用HTMLPAGE模板 👋</title>
<div role="article" aria-roledescription="email" aria-label="欢迎使用HTMLPAGE模板 👋" lang="en" id="i7m0o" class="c3005">
<table cellpadding="0" cellspacing="0" role="presentation" id="ivm4l" class="c3013">
<tbody>
<tr>
<td align="center" id="iateu" class="c3037">
<table cellpadding="0" cellspacing="0" role="presentation" id="i4cwv" class="sm-w-full c3045">
<tbody>
<tr></tr>
<tr>
<td align="center" id="itbcd" class="sm-px-24 c3132">
<table cellpadding="0" cellspacing="0" role="presentation" id="i53eh" class="c3140">
<tbody>
<tr>
<td id="i2lri" class="sm-px-24 c3164">
<!-- <p style="font-family: 'Montserrat', sans-serif; mso-line-height-rule: exactly; margin-top: 12px; font-size: 20px; font-weight: 700; color: #263238;">陈先生!</p> -->
<div class="row">
<div class="cell"><a href="" class="c3402">告警恢复</a>
<div class="row"></div>
</div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
</div><a href="https://htmlpage.cn" class="c3217"></a>
<p id="i19pj" class="c3244"></p>
<p id="ilj5l" class="c3289"></p>
<ul id="if9r7" class="c3307">
<li>告警名称:{{ .Labels.alertname }}</li>
<li>告警级别:{{ .Labels.severity }}</li>
<li>告警机器:{{ .Labels.instance }}</li>
<li>告警信息:{{ .Annotations.summary }}</li>
<li>告警详情:{{ .Annotations.description }}</li>
<li>告警时间:{{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}</li>
<li>恢复时间:{{ (.EndsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}</li>
</ul>
<table cellpadding="0" cellspacing="0" role="presentation">
<tbody>
<tr>
<td id="iz67p" class="c3394"></td>
</tr>
</tbody>
</table>
<table cellpadding="0" cellspacing="0" role="presentation" id="i7dvy" class="c3444">
<tbody>
<tr>
<td id="i0ol4" class="c3468">
<div id="ifrkk" class="c3476"></div>
</td>
</tr>
</tbody>
</table>
<p id="i9omt" class="c3516">处理过程中如遇到问题,请与我们联系
<a href="mailto:k8s_prome_test@163.com" class="hover-underline c3533">回复邮件</a>。
</p>
<p id="inqma" class="c3572"> — 运维团队</p>
</td>
</tr>
<tr>
<td id="ir37w" class="c3708"></td>
</tr>
<tr></tr>
<tr>
<td id="ixrj3" class="c4009"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</body>
<html>
{{- end }}
{{- end }}
{{- end }}
alertmanager.yaml: |-
"global":
"resolve_timeout": "5m"
smtp_from: "k8s_prome_test@163.com"
smtp_smarthost: "smtp.163.com:465"
smtp_hello: "163.com"
smtp_auth_username: "k8s_prome_test@163.com"
smtp_auth_password: "授权码"
smtp_require_tls: false
templates:
- '/etc/alertmanager/config/*.tmpl'
"inhibit_rules":
- "equal":
- "namespace"
- "alertname"
"source_matchers":
- "severity = critical"
"target_matchers":
- "severity =~ warning|info"
- "equal":
- "namespace"
- "alertname"
"source_matchers":
- "severity = warning"
"target_matchers":
- "severity = info"
- "equal":
- "namespace"
"source_matchers":
- "alertname = InfoInhibitor"
"target_matchers":
- "severity = info"
"receivers":
- "name": "Default"
email_configs:
- to: "szgetshell@163.com"
html: '{{ template "123.html" . }}'
send_resolved: true
- "name": "Watchdog"
- "name": "Critical"
- "name": "null"
"route":
"group_by":
- "namespace"
- "job"
- "alertname"
"group_interval": "5m"
"group_wait": "30s"
"receiver": "Default"
"repeat_interval": "1m"
"routes":
- "matchers":
- "alertname = InfoInhibitor"
"receiver": "null"
- "matchers":
- "severity = critical"
"receiver": "Critical"
type: Opaque
将更改好的 Alertmanager 配置加载到 Alertmanager
kubectl replace -f alertmanager-secret.yaml
8.5 域名访问延迟告警
假设需要对域名访问延迟进行监控,访问延迟大于 1 秒进行告警,此时可以创建一个PrometheusRule 如下:
[root@k8s-master01 manifests]# cat blackbox-rule.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
labels:
app.kubernetes.io/component: exporter
app.kubernetes.io/name: blackbox-exporter
name: blackbox-exporter-rules
namespace: monitoring
spec:
groups:
- name: blackbox.rules
rules:
- alert: DomainAccessDelayExceeds1s
annotations:
description: 域名:{{ $labels.instance }} 探测延迟大于 0.5 秒,当前延迟为:{{ $value }}
summary: 域名探测,访问延迟超过 0.5 秒
expr: sum(probe_http_duration_seconds{job=~"blackbox"}) by (instance) > 0.5
for: 1m
labels:
type: blackbox
level: high
配置项详解:
- name:rule名称
- alert:告警策略的名称
- annotations:告警注释信息,一般写为告警信息
- expr:告警表达式
- for:评估等待时间,告警持续多久才会发送告警数据
- labels:告警的标签,用于告警的路由
创建并查看该 PrometheusRule
#创建
kubectl create -f blackbox-rule.yaml
#查看
[root@k8s-master01 manifests]# kubectl get prometheusrule -n monitoring
NAME AGE
alertmanager-main-rules 12d
blackbox-exporter-rules 31s
grafana-rules 12d
kube-prometheus-rules 12d
kube-state-metrics-rules 12d
kubernetes-monitoring-rules 12d
node-exporter-rules 12d
prometheus-k8s-prometheus-rules 12d
prometheus-operator-rules 12d
在prometheus上查看规则是否生效,没问题!
修改alertmanager-secret.yaml,配置告警路由,新添加一个组,并且定义好收件人和告警信息模板。
"receivers":
- "name": "devops"
email_configs:
- to: "2428581996@qq.com"
html: '{{ template "test.html" . }}'
send_resolved: true
在route.routes下进行配置匹配路由,该例子是匹配labels为type: blackbox
的告警,然后发给devops组,而devops组中已经定义好要发送的人的邮箱。
"route":
"routes":
- "matchers":
- "type = blackbox"
"receiver": "devops"
将更改好的 Alertmanager 配置加载到 Alertmanager
kubectl replace -f alertmanager-secret.yaml
稍等片刻就能收到
注:本篇学习笔记内容参考杜宽的《云原生Kubernetes全栈架构师》,视频、资料文档等,大家可以多多支持!还有YinJayChen语雀、k8s训练营、“我为什么这么菜”知乎博主等资料文档,感谢无私奉献!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)