Prometheus介绍
首先看到Prometheus官方架构图:
exporter和pushgatway
数据通过exporter
和pushgatway
收集存储起来,再由Prometheus
服务进行拉取操作。这样的设计把数据压力分摊到了各个收集节点,不会导致收集节点过多而Prometheus
承受不住数据冲击而宕机。
对于收集的方式通过exporter
或pushgatway
暴露http/https
端口地址进行采集,也就是除了官方提供的exporter
之外,我们可以很好的进行定制exporter
,只需满足Prometheus
的数据格式,在暴露到地址即可。具体细节可以查看 开发自己的exporter。pushgatway
用于不方便去提供一个采集接口时,Prometheus提供的官方接口,可以直接把数据发到pushgatway
中,这样也可以收集到发送到数据。但是pushgatway
不保证数据的生命周期在你的管控之中,因为pushgatway
的控制权在Prometheus,如果pushgatway
宕机了,你发的数据就会丢失。所以官方推荐短期任务可以借助pushgatway
,而不自己开发exporter
。
Prometheus Server
在使用Prometheus
监控时,需要有一个念头:Prometheus
监控指标,而不能详细到具体数据。而指标不是百分百精确,它有大概的趋势,来帮助我们发现和预判问题。如果需要详细的问题调查体系,则需要日志收集框架和链路追踪框架支撑。所以查看问题的顺序是这样子的:指标监控->日志查询->链路追踪查询,获得的信息逐渐增多,但是所要做的事也增多了。
了解了上面的信息,还需要注意的是:Prometheus
认为只有最近的监控数据才有查询的需要,所有Prometheus
本地存储的设计初衷只是保存短期(如一个月)的数据,不会针对大量的历史数据进行存储。也就是说,数据保留可能不会太久(长久的数据价值可能远超保留它的代价)。如果需要存储,则需要其他的扩展方案。在图中可以看到,对于存储Prometheus
使用自带的TSDB进行存储,对于采集节点服务发现和Kubernetes
集成,这也是很关键的一点。
数据展示、报警
Prometheus
收集数据存储到本地,这些数据可以配置成rule、alert两种聚合数据,rule会被Grafana收集进行展示、alert会被Alertmanager进行收集报警。这两个组件的灵活度都特别的高,但是不是本节重点,继续往后看。
使用
在之前开发流程是这样的,下载Prometheus、Exporter、Grafana、Alertmanager。之后在Prometheus中配置对应的exporter信息,rule、alert。当然这些都可以通过Docker
或者Kubernetes
的配置文件一件部署,在kubernetes下则可能是这样子的:
可以看到Prometheus连接着其他的组件。当然,连接会通过Service组件连接,防止exporter或组件宕机、升级在集群中重新调度时的ip丢失。但是如果需要添加组件,如新的采集节点,则需要手动修改配置文件。这样维护起来在大集群里可能成本比较高。于是之后就出现了Prometheus-Operator。
Prometheus-Operator是在Kubernetes中的一组CRD,所以它所有的操作都是依赖于Kubernetes的。通过Kubernetes平台优化了上面的问题和添加功能,先看看架构图:
可以看到在Prometheus和组件的连接中又抽象了一层ServiceMonitor,ServiceMonitor通过label进行选择组件的连接,和Kubernetes中的一样,我们通过操作类似label选择器的机制去管理组件。除了和Prometheus的连接管理之外,Operator还管理着Grafana和Alertmanager,它们各自的配置文件都可以动态进行更新,此外还抽象出了rule组件,这样rule、alert都可以进行动态更新和管理。
上面所有的管理工作都是由Operator组件完成,它通过prometheus对应reload的http接口/-/reload
,如果Operator监听到了配置的变化,就对调用此接口进行配置动态更新。具体细节可能会更加复杂一点。
Kube-Prometheus介绍
有了Kube-Prometheus
之后,虽然知道它扩展很方便,但是我们还是希望有一个通用的配置,之后我们在通过修改配置去定制自己的监控集群。原因是一套完整的监控架构需要写的配置文件还是挺多的。😦
于是就有了Kube-Prometheus
项目,Kube-Prometheus
项目可以通过一些配置快速的生成一套适应你集群的配置文件,之后你在修改生成的配置去适应你的集群,这样就节省了很多时间了。下面就介绍它是如何实现的。
- 使用官方推荐的Kube-Prometheus进行部署,原理是使用
Jasonnet
工具生成配置文件。 - 使用
Helm
社区维护的Chart
部署
使用哪一种方法好?
- jsonnet本质是类似模版语言工具进行生成配置文件, helm 从某种程度上来说也是做这件事的,由于 Prometheus 相关社区(Grafana、Prometheus-Operator、 Kube-prometheus) 都使用 Jsonnet 做配置管理, 应该有它的理由。
- Helm维护的配置文件没有Jsonnet来的完备、及时。(官方亲儿子)
- Json格式上的优势,它不需要考虑空格锁进问题。
我们这里就选择Jsonnet
方式进行生成配置然后部署,在开始前先简单介绍jsonnet。
Jsonnet介绍
Jsonnet是一个模版语言工具,语法酷似Python
,所以上手起来还是满快的。下面通过例子快速了解下它能做到什么,具体细节和语法推荐到官方上学习。jsonnet官网
下面你可以跟着做,然后观察它的输出。当然,安装jsonnet
是必须的,如果你还要使用Kube-Prometheus
的话。
最后的引用库文件代码会生成一个库文件,除此之外不会有其他输出文件。
# 安装 Jsonnet(C 实现)
brew install jsonnet
# 也可以安装 Go 实现
go get github.com/google/go-jsonnet/cmd/jsonnet
# 基本用法: 解释运行一个 jsonnet 源码文件
echo "{hello: 'world'}" > test.jsonnet
jsonnet test.jsonnet
# 对于简单的代码也可以使用 -e 直接运行
jsonnet -e '{hello: "world"}'
# 字段可以不加引号 自动格式化
jsonnet -e '{key: 1+2, hello: "world"}'
# 类比: Jsonnet 支持与主流语言类似的四则运算, 条件语句, 字符串拼接,
# 字符串格式化, 数组拼接, 数组切片以及 python 风格的列表生成式
jsonnet - <<EOF
{
array: [1, 2] + [3],
math: (4 + 5) / 3 * 2,
format: 'Hello, %s' % 'world',
concat: 'Hello, ' + 'world',
slice: [1,2,3,4][1:3],
'list': [x * x for x in [1,2,3,4]],
condition:
if 2 > 1 then
'true'
else
'false',
}
EOF
# 使用变量:
# 使用 :: 定义的字段是隐藏的(不会被输出到最后的 JSON 结果中),
# 这些字段可以作为内部变量使用(非常常用)
# 使用 local 关键字也可以定义变量
# JSON 的值中可以引用字段或变量, 引用方式:
# 变量名
# self 关键字: 指向当前对象
# $ 关键字: 指向根对象
jsonnet - <<EOF
{
local hello = 'hello',
world:: 'world',
message: {
hello: hello,
world: $.world,
_hello: self.world,
}
}
EOF
# 使用函数:
# 定义与引用方式与变量相同, 函数语法类似 python
jsonnet - <<EOF
{
local hello(name) = 'hello %s' % name,
sum(x, y):: x + y,
newObj(name='hello', age=23):: {
name: name,
age: age
},
call_sum: $.sum(1, 2),
call_hello: hello('world'),
me: $.newObj(age=24),
}
EOF
# Jsonnet 使用组合来实现面向对象的特性(类似 Go)
# Json Object 就是 Jsonnet 中的对象
# 使用 + 运算符来组合两个对象, 假如有字段冲突,
# 使用右侧对象(子对象)中的字段
# 子对象中使用 super 关键字可以引用父对象,
# 用这个办法可以访问父对象中被覆盖掉的字段
jsonnet - <<EOF
local base = {
o: 'hello world',
no: {
hello: 'world'
}
};
base + {
o: 'hello zhangsan',
# +: 组合而非覆盖
no+: {
nihao: 'zhangsan'
},
super_o: super.o
}
EOF
# 库与 import:
# jsonnet 共享库复用方式其实就是将库里的代码整合到当前文件中来,
# 引用方式也很暴力, 使用 -J 参数指定 lib 文件夹, 再在代码里 import 即可
# jsonnet 约定库文件的后缀名为 .libsonnet
cat > hello.libsonnet <<EOF
{
helloObj(hello='world', name):: {
hello: hello,
names: [], #声明数组
sayhello(name):: self + {
names +: [name],
},
}
}
EOF
#import调用
jsonnet -J . - <<EOF
local helloPackage = import './hello.libsonnet';
helloPackage.helloObj(name='world')
.sayhello('zhangsan')
.sayhello('lisi')
EOF
Kube-Prometheus安装、配置
有了上面的知识之后,就可以着手定制自己的配置文件了。我会逐一介绍,按照官方的配置的定制流程。
首先看生成脚本
#!/usr/bin/env bash
# This script uses arg $1 (name of *.jsonnet file to use) to generate the manifests/*.yaml files.
set -e
set -x
# only exit with zero if all commands of the pipeline exit successfully
set -o pipefail
# Make sure to use project tooling
#PATH="$(pwd)/tmp/bin:${PATH}"
# Make sure to start with a clean 'manifests' dir
rm -rf manifests
mkdir -p manifests/setup
# 执行jsonnet生成定制的文件,引入的包为vendor,配置文件为脚本传入(默认为example.jsonnet)之后gojsontoyaml将json转化为yaml格式,最后输出到文件里
jsonnet -J vendor -m manifests "${1-example.jsonnet}" | xargs -I{} sh -c 'cat {} | gojsontoyaml > {}.yaml' -- {}
# Make sure to remove json files
find manifests -type f ! -name '*.yaml' -delete
rm -f kustomization
了解到安装方式之后,只要下载项目中的 vendor
包就可以了。之后创建一个定制的配置文件。安装完成之后的目录是这样的:
.
├── build.sh #生成配置文件的脚本
├── jsonnetfile.json #版本信息
├── jsonnetfile.lock.json
├── monitor.jsonnet # 手动创建脚本的配置信息
└── vendor
├──...jsonnet依赖的包
之后编写定制文件:
local kp =
(import 'kube-prometheus/kube-prometheus.libsonnet') + //基本配置
// (import 'kube-prometheus/kube-prometheus-kube-aws.libsonnet') + //Aws集群特殊配置
// (import 'kube-prometheus/kube-prometheus-strip-limits.libsonnet') +
// Uncomment the following imports to enable its patches
// (import 'kube-prometheus/kube-prometheus-anti-affinity.libsonnet') +
// (import 'kube-prometheus/kube-prometheus-managed-cluster.libsonnet') +
// (import 'kube-prometheus/kube-prometheus-node-ports.libsonnet') +
// (import 'kube-prometheus/kube-prometheus-static-etcd.libsonnet') +
// (import 'kube-prometheus/kube-prometheus-thanos-sidecar.libsonnet') +
// (import 'kube-prometheus/kube-prometheus-custom-metrics.libsonnet') +
// (import 'kube-prometheus/kube-prometheus-external-metrics.libsonnet') //+
// 配置全局或提供的动态配置,这里只设置了全局的命名空间
{
_config+:: {
namespace: 'monitoring'
}
};
{ ['setup/0namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } +
{
['setup/prometheus-operator-' + name]: kp.prometheusOperator[name]
for name in std.filter((function(name) name != 'serviceMonitor'), std.objectFields(kp.prometheusOperator))
} +
// 如果要添加更多的export 在这里添加
// serviceMonitor is separated so that it can be created after the CRDs are ready
{ 'prometheus-operator-serviceMonitor': kp.prometheusOperator.serviceMonitor } +
{ ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } +
//{ ['blackbox-exporter-' + name]: kp.blackboxExporter[name] for name in std.objectFields(kp.blackboxExporter) } + #如果需要黑盒则放开
{ ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } +
{ ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } +
{ ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } +
{ ['prometheus-adapter-' + name]: kp.prometheusAdapter[name] for name in std.objectFields(kp.prometheusAdapter) } +
{ ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
AWS集群由于kube-system中组件的label可能丢失,导致监控丢失。所以会帮我们自动生成service文件。这里只要记住如果是aws管理的集群添加这个是保险的。
运行脚本
./build.sh monitor.jsonnet
脚本会生成监控相关的所有配置文件,CRD
文件存放在./manifests/setup
中,资源文件存放在./manifests
中。查看manifests
的文件如下。现在就可以开始修改一些配置信息,来满足你的需要了。接下来挑几个重要的文件,并添加常用的一些配置提供参考。
manifests
# alertmanager相关配置
├── alertmanager-alertmanager.yaml
├── alertmanager-secret.yaml # 告警配置
├── alertmanager-service.yaml
├── alertmanager-serviceAccount.yaml
├── alertmanager-serviceMonitor.yaml
# grafana相关配置
├── grafana-dashboardDatasources.yaml
├── grafana-dashboardDefinitions.yaml
├── grafana-dashboardSources.yaml
├── grafana-deployment.yaml
├── grafana-service.yaml
├── grafana-serviceAccount.yaml
├── grafana-serviceMonitor.yaml
# kubernetes集群指标监控
├── kube-state-metrics-clusterRole.yaml
├── kube-state-metrics-clusterRoleBinding.yaml
├── kube-state-metrics-deployment.yaml
├── kube-state-metrics-service.yaml
├── kube-state-metrics-serviceAccount.yaml
├── kube-state-metrics-serviceMonitor.yaml
# 各个几点指标监控
├── node-exporter-clusterRole.yaml
├── node-exporter-clusterRoleBinding.yaml
├── node-exporter-daemonset.yaml
├── node-exporter-service.yaml
├── node-exporter-serviceAccount.yaml
├── node-exporter-serviceMonitor.yaml
# kubernetes hpa相关适配器
├── prometheus-adapter-apiService.yaml
├── prometheus-adapter-clusterRole.yaml
├── prometheus-adapter-clusterRoleAggregatedMetricsReader.yaml
├── prometheus-adapter-clusterRoleBinding.yaml
├── prometheus-adapter-clusterRoleBindingDelegator.yaml
├── prometheus-adapter-clusterRoleServerResources.yaml
├── prometheus-adapter-configMap.yaml
├── prometheus-adapter-deployment.yaml
├── prometheus-adapter-roleBindingAuthReader.yaml
├── prometheus-adapter-service.yaml
├── prometheus-adapter-serviceAccount.yaml
├── prometheus-adapter-serviceMonitor.yaml
# prometheus相关配置
├── prometheus-clusterRole.yaml
├── prometheus-clusterRoleBinding.yaml
├── prometheus-kubeControllerManagerPrometheusDiscoveryService.yaml
├── prometheus-kubeSchedulerPrometheusDiscoveryService.yaml
├── prometheus-operator-serviceMonitor.yaml
├── prometheus-prometheus.yaml # 监控配置
├── prometheus-roleBindingConfig.yaml
├── prometheus-roleBindingSpecificNamespaces.yaml
├── prometheus-roleConfig.yaml
├── prometheus-roleSpecificNamespaces.yaml
├── prometheus-rules.yaml # 默认监控项
├── prometheus-service.yaml
├── prometheus-serviceAccount.yaml
# serviceMonitor相关配置
├── prometheus-serviceMonitor.yaml
├── prometheus-serviceMonitorApiserver.yaml
├── prometheus-serviceMonitorCoreDNS.yaml
├── prometheus-serviceMonitorKubeControllerManager.yaml
├── prometheus-serviceMonitorKubeScheduler.yaml
├── prometheus-serviceMonitorKubelet.yaml
└── setup # CRD文件
├── 0namespace-namespace.yaml
├── prometheus-operator-0alertmanagerConfigCustomResourceDefinition.yaml
├── prometheus-operator-0alertmanagerCustomResourceDefinition.yaml
├── prometheus-operator-0podmonitorCustomResourceDefinition.yaml
├── prometheus-operator-0probeCustomResourceDefinition.yaml
├── prometheus-operator-0prometheusCustomResourceDefinition.yaml
├── prometheus-operator-0prometheusruleCustomResourceDefinition.yaml
├── prometheus-operator-0servicemonitorCustomResourceDefinition.yaml
├── prometheus-operator-0thanosrulerCustomResourceDefinition.yaml
├── prometheus-operator-clusterRole.yaml
├── prometheus-operator-clusterRoleBinding.yaml
├── prometheus-operator-deployment.yaml
├── prometheus-operator-service.yaml
└── prometheus-operator-serviceAccount.yaml
prometheus配置
查看 prometheus-prometheus.yaml
文件
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.22.1
nodeSelector:
kubernetes.io/os: linux
podMonitorNamespaceSelector: {}
podMonitorSelector: {}
probeNamespaceSelector: {}
probeSelector: {}
replicas: 2 # 设置副本数
retention: 15d # 添加数据保留期限
# 如果需要添加集群外部数据采集, 在这里添加配置文件
#additionalScrapeConfigs:
#name: additional-scrape-configs
#key: prometheus-additional.yaml
#添加sc
storage:
volumeClaimTemplate:
spec:
storageClassName: xxx
resources:
requests:
storage: 50Gi
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.22.1
如果配置了外部采集节点,创建 prometheus-additional-secret.yaml
:
# prometheus-additional-secret.yaml
apiVersion: v1
data: {}
kind: Secret
metadata:
name: additional-scrape-configs
namespace: monitoring
stringData:
prometheus-additional.yaml: |-
- job_name: "prometheus-demo"
metrics_path: "metrics"
static_configs:
- targets: ["xxx:8080"]
type: Opaque
prometheus-rule配置
查看 prometheus-rules.yaml
文件
可以看到该项目内置了很多告警项,可以根据自己需要删减。过多的rule可能会影响性能和消耗更多的内存,所以还是根据自己的需要来配置。当然这些rule对于后面Grafana
展示都是有用到的。Grafana
中的图表也是根据这些规则配置好了的,两者是相关联的。
alertmanager-secret.yaml配置
创建 alertmanager-secret.yaml
文件。具体需要的配置看 alertmanager config。
apiVersion: v1
data: {}
kind: Secret
metadata:
name: alertmanager-main
namespace: monitoring
stringData:
alertmanager.yaml: |-
global:
resolve_timeout: 2h
smtp_smarthost: 'smtp.qq.com:465'
smtp_from: 'xxx@xx.com'
smtp_auth_username: 'xxx@xx.com'
smtp_auth_password: 'xxx'
smtp_require_tls: false
route:
group_by: ['alertname']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: default
routes:
- receiver: 'email-receiver'
group_wait: 30s
match:
notify: notify
receivers:
- name: email-receiver
email_configs:
- to: 'xxx@xx.com'
send_resolved: true
- to: 'xxx@xx.com'
send_resolved: true
- name: 'default'
webhook_configs:
- url: 'http://xxx'
send_resolved: true
type: Opaque
查看监控信息
完成配置定制之后运行
kubectl apply -f manifests/setup
kubectl apply -f manifests/
以下内容为将端口映射到本地主机端口
kubectl --namespace monitoring port-forward svc/prometheus-k8s 9090
kubectl --namespace monitoring port-forward svc/grafana 3000
访问地址 http://localhost:3000 用户名和密码为 admin:admin
kubectl --namespace monitoring port-forward svc/alertmanager-main 9093
扩展
Grafana仪表盘图
参考 https://grafana.com/grafana/dashboards
更多监控报警规则参考 https://awesome-prometheus-alerts.grep.to/rules
遇到的问题
manifests/prometheus-rules.yaml 部署失败
Error from server (InternalError): error when creating “manifests/prometheus-rules.yaml”: Internal error occurred: failed calling webhook “prometheusrulemutate.monitoring.coreos.com”: Post https://kube-prometheus-stack-1607-operator.default.svc:443/admission-prometheusrules/mutate?timeout=10s: service “kube-prometheus-stack-1607-operator” not found
具体问题查看 https://github.com/helm/charts/issues/21080
解决:
kubectl get validatingwebhookconfigurations.admissionregistration.k8s.io
#do 删除 kube-prometheus..资源
kubectl get MutatingWebhookConfiguration
#do 删除 kube-prometheus..资源
#重新载入
kubectl apply -f manifests/prometheus-rules.yaml
两个监控任务没有对应的目标
访问 Prometheus
遇到这种情况一般是你下载的版本和你的Kubernetes
集群版本不匹配,导致label丢失,所以ServiceMonitor
找不到对应的Service
。
解决思路
- 查看
ServiceMonitor
对应的服务端口是否存在 - 服务端口是否匹配到正确到po(查看po是否含有匹配标签)
- 通信协议是否正确(
http/https
)