在Kubernetes中我们使用Deployment、DamenSet、StatefulSet来管理应用Workload,使用Service、Ingress来管理应用的访问方式,使用ConfigMap和Secret来管理应用配置。我们在集群中对这些资源的创建,更新,删除的动作都会被转换为事件(Event),Kubernetes的Controller Manager负责监听这些事件并触发相应的任务来满足用户的期望。这种方式我们成为声明式,用户只需要关心应用程序的最终状态,其它的都通过Kubernetes来帮助我们完成,通过这种方式可以大大简化应用的配置管理复杂度。
而除了这些原生的Resource资源以外,Kubernetes还允许用户添加自己的自定义资源(Custom Resource)。并且通过实现自定义Controller来实现对Kubernetes的扩展,不需要用户去二开k8s也能达到给k8s添加功能和对象。
Prometheus-operator的本职就是一组用户自定义的CRD资源以及Controller的实现,Prometheus Operator这个controller有BRAC权限下去负责监听这些自定义资源的变化,并且根据这些资源的定义自动化的完成如Prometheus Server自身以及配置的自动化管理工作。
git clone https://github.com/coreos/prometheus-operator.git
cd prometheus-operator/contrib/
rm -rf kube-prometheus/
git clone https://github.com/coreos/kube-prometheus.git
cd kube-prometheus/manifests
#####分批处理一下方便管理:
mkdir -p operator node-exporter alertmanager grafana kube-state-metrics prometheus serviceMonitor adapter
mv *-serviceMonitor* serviceMonitor/
mv 0prometheus-operator* operator/
mv grafana-* grafana/
mv kube-state-metrics-* kube-state-metrics/
mv alertmanager-* alertmanager/
mv node-exporter-* node-exporter/
mv prometheus-adapter* adapter/
mv prometheus-* prometheus/
######开始部署:
pro1k8s apply -f bundle.yaml
######
kubectl apply -f adapter/
kubectl apply -f alertmanager/
kubectl apply -f node-exporter/
kubectl apply -f kube-state-metrics/
kubectl apply -f grafana/
kubectl apply -f prometheus/
kubectl apply -f serviceMonitor/
打开页面发现:
这是因为serviceMonitor是根据label去选取svc的,我们可以看到对应的serviceMonitor是选取的ns范围是kube-system,然而里面没有这两个组件对应的service,不知道k8s搞什么鬼。
创建一个service即可:
cat 01kube-prom-service.yml
apiVersion: v1
kind: Service
metadata:
namespace: kube-system
name: kube-controller-manager
labels:
k8s-app: kube-controller-manager
spec:
selector:
component: kube-controller-manager
type: ClusterIP
clusterIP: None
ports:
- name: http-metrics
port: 10250
targetPort: 10250
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
namespace: kube-system
name: kube-scheduler
labels:
k8s-app: kube-scheduler
spec:
selector:
component: kube-scheduler
type: ClusterIP
clusterIP: None
ports:
- name: http-metrics
port: 10250
targetPort: 10250
protocol: TCP
然而:
cat /etc/kubernetes/manifests/kube-scheduler.yaml|grep address
- --address=127.0.0.1
cat /etc/kubernetes/manifests/kube-controller-manager.yaml|grep address
- --address=127.0.0.1
sed -ri '/--address/s#=.+#=0.0.0.0#' /etc/kubernetes/manifests/kube-controller-manager.yaml
sed -ri '/--address/s#=.+#=0.0.0.0#' /etc/kubernetes/manifests/kube-scheduler.yaml
时间序列数据库的特点
Prometheus提供多种类型的Exporter用于采集各种不同服务的运行状态。目前支持的有数据库、硬件、消息中间件、存储系统、HTTP服务器、JMX等。
Prometheus的基本原理是通过HTTP协议周期性抓取被监控组件的状态,任意组件只要提供对应的HTTP接口就可以接入监控。不需要任何SDK或者其他的集成过程。这样做非常适合做虚拟化环境监控系统,比如VM、Docker、Kubernetes等。输出被监控组件信息的HTTP接口被叫做exporter 。mysql、kafka、redis、等许多组件都有exporter
./remote_storage_adapter -influxdb-url=http://localhost:8086/ -influxdb.database=prometheus -influxdb.retention-policy=autoge
prometheus的配置文件:
remote_write:
- url: "http://localhost:9201/write"
remote_read:
- url: "http://localhost:9201/read"
scrape_configs:
- job_name: 'federate'
scrape_interval: 15s
honor_labels: true ##(to not overwrite any labels exposed by the source server)
metrics_path: '/federate'
params:
'match[]':
- '{job=~"kubernetes.*"}'
- '{__name__=~"job:.*"}'
#- '{job="mysql"}'
#- '{job="pgsql"}'
static_configs:
- targets:
- '10.111.178.30:30373'
可以解决什么问题呢?
二:alertmanager的分布式思路:
but,但是由于Alertmanager之间不存在并不了解彼此的存在,因此则会出现告警通知被不同的Alertmanager重复发送多次的问题。
Alertmanager基于Gossip实现的集群机制虽然不能保证所有实例上的数据时刻保持一致,但是实现了CAP理论中的AP系统,即可用性和分区容错性。同时对于Prometheus Server而言保持了配置了简单性,Promthues Server之间不需要任何的状态同步。
nohup ./alertmanager --web.listen-address=":9093" --cluster.listen-address="127.0.0.1:8001" --config.file="alertmanager.yml" 2>&1 > alert1.log &
nohup ./alertmanager --web.listen-address=":9094" --cluster.listen-address="127.0.0.1:8002" --cluster.peer=127.0.0.1:8001 --config.file="alertmanager.yml" 2>&1 > alert2.log &
nohup ./alertmanager --web.listen-address=":9095" --cluster.listen-address="127.0.0.1:8003" --cluster.peer=127.0.0.1:8001 --config.file="alertmanager.yml" 2>&1 > alert3.log &
模拟代码测试:
alerts1='[
{
"labels": {
"alertname": "DiskRunningFull",
"dev": "sda1",
"instance": "example1"
},
"annotations": {
"info": "The disk sda1 is running full",
"summary": "please check the instance example1"
}
}
]'
curl -XPOST -d"$alerts1" http://localhost:9093/api/v1/alerts
curl -XPOST -d"$alerts1" http://localhost:9094/api/v1/alerts
curl -XPOST -d"$alerts1" http://localhost:9095/api/v1/alerts
所以这套可以作为prometheus-server的通用报警集群,每套server里面添加配置保证像每个alertmanager都发送报警消息即可:
alerting:
alertmanagers:
- static_configs:
- targets: ["localhost:9093"]
- targets: ["localhost:9094"]
- targets: ["localhost:9095"]
执行脚本sh sent_alert.sh
{"status":"success"}{"status":"success"}{"status":"success"}
查看钉钉消息只收到一条:
<--------------- metric ---------------------><-timestamp -><-value->
http_request_total{status="200", method="GET"}@1434417560938 => 94355
http_request_total{status="200", method="GET"}@1434417561287 => 94334
http_request_total{status="404", method="GET"}@1434417560938 => 38473
http_request_total{status="404", method="GET"}@1434417561287 => 38544
http_request_total{status="200", method="POST"}@1434417560938 => 4748
http_request_total{status="200", method="POST"}@1434417561287 => 4785
grpc_cache_total{app="smpre-svc-ads-go",enable="true",instance="172.24.4.172:2112",job="kubernetes-pods",kubernetes_namespace="staging",kubernetes_pod_name="smpro-svc-ads-go-58d6d44cb5-xthqz",pod_template_hash="1482800761",rpc="Execute",service="svc-rule",status="hit",version="pre"} | 327 |
# HELP mysql_up Whether the MySQL server is up.
# TYPE mysql_up gauge
mysql_up 1
# HELP mysql_version_info MySQL version and distribution.
# TYPE mysql_version_info gauge
mysql_version_info{innodb_version="5.7.25",version="5.7.25-log",version_comment="Source distribution"} 1
# HELP mysqld_exporter_build_info A metric with a constant '1' value labeled by version, revision, branch, and goversion from which mysqld_exporter was built.
# TYPE mysqld_exporter_build_info gauge
mysqld_exporter_build_info{branch="HEAD",goversion="go1.10.3",revision="5d7179615695a61ecc3b5bf90a2a7c76a9592cdd",version="0.11.0"} 1
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 2871.09
五分钟的增长率:rate(http_requests_total[5m])
前五的请求:topk(5, http_requests_total)
mysql_global_status_memory_used
对于Gauge类型的监控指标,通过PromQL内置函数delta()可以获取样本在一段时间返回内的变化情况:
delta(mysql_global_status_memory_used{instance="pro-ab-test"}[1h])
还可以使用deriv()计算样本的线性回归模型,甚至是直接使用predict_linear()对数据的变化趋势进行预测:
predict_linear(mysql_global_status_memory_used{instance="pro-ab-test"}[1h], 4 * 3600)
例如nginx_ingress_controller_ingress_upstream_latency_seconds这个metric为Summary:
其中0.5比例、0.9比例和0.99比例的延时秒分别为0.001 、0.001和 0.002
grpc_server_handling_seconds_bucket这个参数是咱们线上的一个histogram类型数据
histogram_quantile(0.90, sum(rate(grpc_server_handling_seconds_bucket{grpc_service="shimo.svc.comment.CommentService"}[1m])) by (grpc_method,le))
线上某时段的90请求的平均延时
与Summary类型的指标相似之处在于Histogram类型的样本同样会反应当前指标的记录的总数(以_count作为后缀)以及其值的总量(以_sum作为后缀)。不同在于Histogram指标直接反应了在不同区间内样本的个数,区间通过标签len进行定义。
同时对于Histogram的指标,我们还可以通过histogram_quantile()函数计算出其值的分位数。不同在于Histogram通过histogram_quantile函数是在服务器端计算的分位数。 而Sumamry的分位数则是直接在客户端计算完成。因此对于分位数的计算而言,Summary在通过PromQL进行查询时有更好的性能表现,而Histogram则会消耗更多的资源。反之对于客户端而言Histogram消耗的资源更少。在选择这两种方式时用户应该按照自己的实际场景进行选择。
promhttp_metric_handler_requests_total
{}里面的参数都可以用来过滤查询支持=、!=、正则
promhttp_metric_handler_requests_total{app="ingress-nginx",code!="400",instance=~"10.111.178.109:10254|10.111.178.110:10254"}
加入一个5m就能查到最近五分钟内的所有数值:
加offset 5m 可以查五分钟前的瞬时样本:
那么我想求上一个五分的所有数值怎么办:
总结:offset函数是一个相对的时间函数,可以用来时间位移
sum:
avg:
increase:函数获取区间向量中的第一个后最后一个样本并返回其增长量
rate:rate函数可以直接计算区间向量v在时间窗口内平均增长速率。因此,通过以下表达式可以得到与increase函数相同的结果:
需要注意的是使用rate或者increase函数去计算样本的平均增长速率,容易陷入“长尾问题”当中,其无法反应在时间窗口内样本数据的突发变化。 例如,对于主机而言在2分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致CPU占用100%的情况,但是通过计算在时间窗口内的平均增长率却无法反应出该问题。
为了解决该问题,PromQL提供了另外一个灵敏度更高的函数irate(v range-vector)。irate同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。irate函数是通过区间向量中最后两个两本数据来计算区间向量的增长速率。这种方式可以避免在时间窗口范围内的“长尾问题”,并且体现出更好的灵敏度,通过irate函数绘制的图标能够更好的反应样本数据的瞬时变化状态。
举个例子:我们查最近十分钟的cpu利用率sum(irate(process_cpu_seconds_total[10m])) * 100 这个是最好的因为能够比较清晰的反映出十分钟内cpu利用率的变化
但是我们要查大约一个月的cpu利用率,用这个比较好
sum(rate(process_cpu_seconds_total[10m])) * 100
类似的可以直接使用sum,min,max,avg,stddev,stdvar,count,count_values,bottomk,topk,quantile
Prometheus需要加入两个配置项
alerting:
alertmanagers:
#一组配置项目就是一个列表
- scheme: https
static_configs:
- targets:
- "1.2.3.4:9093"
- "1.2.3.5:9093"
- "1.2.3.6:9093"
- scheme: http
static_configs:
- targets:
- "1.2.3.7:9093"
- "1.2.3.8:9093"
- "1.2.3.9:9093"
或者k8s自动发现:
alerting:
alertmanagers:
- kubernetes_sd_configs:
- role: pod
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]
regex: default
action: keep
- source_labels: [__meta_kubernetes_pod_label_app]
regex: prometheus
action: keep
- source_labels: [__meta_kubernetes_pod_label_component]
regex: alertmanager
action: keep
- source_labels: [__meta_kubernetes_pod_container_port_number]
regex:
action: drop
rule_files:
- "/etc/prometheus_rule/rule_*.yml"
具体规则如下:
# 定义alert类型并加上名称
alert:
# PromQL 表达式
#expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
expr:
# 报警间隔,含义就是 当一个报警信息触发以后延迟多少时间再去fire
[ for: | default = 0s ]
# 报警标签
labels:
[ : ]
# 报警中的注释
annotations:
[ : ]
#建议写上summary describipton
#summary: "High request latency on {{ $labels.instance }}"
#description: "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)"
#####label和annotations的值是以模板console templates来渲染的,用到go模板语言,报警注释和标签中常用的是变量$labels和$value,分别表示一个监控实例和expr字段上表达式所拉取的值
项目地址:https://git.shimo.im/shimo/prometheus_rule/tree/master
route定义了报警信息的分发策略,receivers定义了发送媒介,两者合在一起就是什么样的报警信息以什么样的形式发送。主要流程
总路由:
#总路由,下面的配置都是各个子路由所继承的配置,如果子路由中没有配置就是使用总路由的配置,子路由中可以覆盖总路由的配置
route:
#定义全路由默认的接受者
receiver: 'default-receiver'
#报警是否需要继续向兄弟(同级)节点匹配
[ continue: | default = false ]
#等待还有可能到达的同组报警信息,初次发送一条警报包含多个信息
[ group_wait: | default = 30s ]
#已经存在的group等待group_interval这个时间段看报警问题是否解决
[ group_interval: | default = 5m ]
#当上次发送完报警信息之后再次发送的间隔,用来重复发送报警信息
[ repeat_interval: | default = 4h ]
#根据不同的标签来分组,这个主要用来筛选报警信息
group_by: [cluster, alertname]
#使用标签值精确匹配
match:
[ : , ... ]
#使用正则表达进行范围匹配
match_re:
[ : , ... ]
子路由构成的路由树:
route:
receiver: default
routes:
- match:
service: database
receiver: a
routes:
- match:
owner: team-X
receiver: a1
- match:
owner: team-Y
receiver: a2
routes:
- match:
owner: ll
receiver: a2.1
- match:
owner: rr
receiver: a2.2
- match:
receiver: b
- match:
receiver: c
routes:
- match:
receiver: c1
- match:
receiver: c2
拿线上举个例子:
groups:
- name: dingtalk
rules:
- alert: dingtalk api 调用过多
expr: sum(irate(dingtalk_api_call{}[1m])) > 60
for: 1m
labels:
severity: warning
annotations:
summary: "钉钉 API 调用过多, 当前 {{ $value }} qps"
如果定义超过100 级别为critical 也发出告警:
groups:
- name: dingtalk
rules:
- alert: dingtalk api 调用过多
expr: sum(irate(dingtalk_api_call{}[1m])) > 60
for: 1m
labels:
severity: critical
annotations:
summary: "钉钉 API 调用过多, 当前 {{ $value }} qps"
可以设定抑制规则:
inhibit_rules:
- source_match:
altername: 'dingtalk api 调用过多'
severity: 'critical'
target_match:
severity: 'warning'
# Apply inhibition if the alertname is the same.
equal: ['alertname', 'instance']
这个不展开说了,咱们内部用钉钉,可以直接发到群里 prometheus机器里面:
./prometheus-webhook-dingtalk --web.listen-address=:8060 --ding.profile=ops_dingding=https://oapi.dingtalk.com/robot/send?access_token=xxxxxxx
docker pull prom/pushgateway
docker run ‐d ‐p 9091:9091 prom/pushgateway
echo "some_metric 3.14" | curl ‐‐data‐binary @‐ http://127.0.0.1:9091/metrics/job/some_job
因为 Prometheus 配置 pushgateway 的时候,也会指定 job 和 instance, 但是它只表示 pushgateway 实例,不能真正表达收集数据的含义。所以在prometheus 中配置 pushgateway 的时候,需要添加 honor_labels: true 参数,从而避免收集数据本身的 job 和 instance 被覆盖。
注意,为了防止 pushgateway 重启或意外挂掉,导致数据丢失,我们可以通过 -persistence.file 和 -persistence.interval 参数将数据持久化下来。
由于以上原因,不得不使用 pushgateway,但在使用之前,有必要了解一下它的一些弊端:
因此,即使你的监控已经下线,prometheus 还会拉取到旧的监控数据,需要手动清理 pushgateway 不要的数据。