0%

rancher部署prometheus和grafana以及简单使用


概述

通常服务部署成功之后,对服务进行实时监控是一件很正常的事情,之前只用过别人搭建好的grafana。这篇博客中我自己来尝试完成这一整套流程。

首先对环境进行部署,然后编写一个简单的代码来在go程序中使用prometheus监控数据,通过prometheus服务端的采集,然后在grafana上进行展示。


prometheus和grafana部署

关于rancher的部署,在我之前的博客中已经写过了rancher+k3s的高可用部署以及k3s集群的导入

对于prometheus和grafana,这两个东西不使用容器,直接部署很简单,下载下来配置一下运行就好了。我这里环境中既然有rancher,那就直接使用rancher来部署,这样方便我进行服务管理。


方法一,在rancher的应用商店中部署

首先进入到应用商店中,选择启动:

image

在其中找到prometheus:

image

点进去进行部署前的设置,我这里首先改了一下名称,选择了一下命名空间:

image

我这里选择不使用默认镜像,因为我使用默认镜像后,发现默认镜像的prometheus版本太低了,根本不支持basic auth,安全性没有任何保证,所以这里把prometheus改成了官方的最新镜像:

image

这里为prometheus启用了ingress,这个比直接端口映射方便,不过图中的.xip.io这个域名网站已经不能用了,后面换一下就好:

image

然后我关闭了我不需要的alert和push功能,开启了需要的grafana,同样为grafana启用了ingress:

image

点击部署之后,可以在应用商店中找到它:

image

点进这个应用,首先去改一下ingress规则,将.xip.io这个域名换成.nip.io:

image

image

image

现在就可以通过域名直接访问到这两个服务了:

image

image

image

到这里为止,服务部署已经完毕。


方法二,使用rancher直接部署

本来我用应用商店部署,发现用不了basic auth,当时没有发现可以修改docker版本,于是重新部署了一个最新版本的prometheus,整半天才加上basic auth

首先,直接在工作负载中,选择部署服务:

image

改一下名字,选一下镜像和命名空间(新创建也行),然后直接部署即可:

image

image

这时服务已经启动了,但是没有向外暴露服务,所以我们是访问不了的。

手动添加一下ingress规则,选择负载均衡->添加规则,修改规则名,选择命名空间,工作负载,内部服务端口(prometheus默认是9090端口):

image

image

同样的,还是要修改一下域名,将.xip.io这个域名换成.nip.io

image

image

这样就出现了访问端口80,可以直接点击它来访问到prometheus服务:

image

到这里直接部署已经完成。


应用商店中prometheus启用基本认证

在上面rancher部署完毕之后,可以发现现在prometheus是没有启用basic auth的。

通过官方文档SECURING PROMETHEUS API AND UI ENDPOINTS USING BASIC AUTH可以知道,最新的prometheus是可以直接支持认证的,而不是像以前一样要加个反向代理来曲线救国。

依据官方文档所述,现在可以额外添加一个web.yml文件来指定连接时的用户名和账号,格式如下:

1
2
basic_auth_users:
admin: $2b$12$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay

这里的密码是test,经过bcrypt算法hash之后得到$2b$12$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay

因为这个文件需要我们自己生成,也就意味着这个密码的hash过程得我们自己实现。

这里提供一个golang生成bcrypt进行hash之后的密码的简单代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"golang.org/x/crypto/bcrypt"
)

func main() {
password := "mypassword"
buf, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
panic(err)
}
fmt.Println(string(buf))
}

在运行prometheus时,通过指定这个文件来启用认证:

1
prometheus --config.file=/etc/prometheus/prometheus.yml --web.config.file=/etc/prometheus/web.yml

当然现在我们是通过rancher来启动得prometheus,所以需要通过修改rancher的配置来让prometheus容器启动时,运行如上命令。


应用商店中prometheus启用基本认证

首先,还是同样的在应用商店中对prometheus进行管理,添加配置映射,在其中加入我们的web-auth.yml:

image

image

然后,配置一下容器启动时命令,选择工作负载,在启动对入口命令进行修改:

image

image

image

上面的命令(CMD)中本来是如下内容:

1
--storage.tsdb.retention.time=15d --config.file=/etc/config/prometheus.yml --storage.tsdb.path=/data --web.console.libraries=/etc/prometheus/console_libraries --web.console.templates=/etc/prometheus/consoles --web.enable-lifecycle

添加--web.config.file=/etc/config/web-auth.yml到其中,(注意这里的配置文件是prometheus-server-configmap-reload这个服务来映射的,在/etc/config/路径下,而默认配置文件在/etc/prometheus/路径下):

1
--storage.tsdb.retention.time=15d --config.file=/etc/config/prometheus.yml --web.config.file=/etc/config/web-auth.yml --storage.tsdb.path=/data --web.console.libraries=/etc/prometheus/console_libraries --web.console.templates=/etc/prometheus/consoles --web.enable-lifecycle

点击升级之后,容器开始重启,但是这里我发现重启失败了,原因是健康检查没有通过,因为其默认使用的是http认证,但是启用认证之后,普通的http请求会直接403,所以健康检查无法通过。这里改成端口检查即可:

image

image

修改之后,prometheus服务健康启动,查看界面,可以发现需要输入密码了:

image

输入刚才所设置的用户名和密码,即可登录成功。


直接部署的prometheus启用基本认证

使用rancher直接部署的prometheus,由于是单个的独立服务,并没有其它的服务来辅助进行配置文件的管理,所以每次重新部署服务,之前容器都会被摧毁。

所以这里需要我们自己来对配置文件进行管理,最开始我试用了主机目录映射的方法,然后发现两个k3s节点有一个有这个目录,有一个没有,这样就有可能部署会失败。于是换用了挂载pvc卷的方法,pv卷底层选择了nfs。

首先选择一台机器192.168.88.60来安装nfs:

1
sudo yum  install  -y nfs-utils

修改nfs配置文件:

1
2
3
# vim /etc/exports

/home/lrm/rancher-pv 192.168.88.58(rw,sync,root_squash) 192.168.88.59(rw,sync,root_squash)

因为nfs没有太多的安全性可以配置,所以这里配置时指定了能够访问的ip以及权限,这两个ip就是两个k3s节点。这里将/home/lrm/rancher-pv目录进行分享。

启动nfs:

1
sudo systemctl start nfs

另外的一些常用命令:

1
2
3
4
5
6
7
8
// 查看当前向外提供的挂载目录
showmount -e

// 查看当前nfs被哪些客户端所挂载
showmount -a

// 应用配置文件修改
exportfs -r

然后就可以到rancher上配置pv卷。选择集群(但是不选择具体项目),可以看到存储,选择其中的持久卷,然后点击添加pv:

image

image

修改持久卷名字,选择卷插件NFS Share,填写容量大小,路径是我们刚才分享的目录/home/lrm/rancher-pv,分享的机器192.168.88.60

image

添加完成pv卷后,来到刚才部署好的long-prometheus这个服务中进行升级,选择数据卷->添加卷->添加新的pvc,然后选择使用现有的持久卷,也就是刚才创建的pv卷long-nfs-pv:

image

image

我这里将它映射到容器内路径/etc/prometheus/myconfig下:

image

添加好数据卷后,点击升级,服务会重启。完成之后,进入容器内命令行,可以看到目录已经成功映射进来:

image

image

剩下的事情就很简单了,在这个目录中编写好我们的配置文件prometheus.ymlweb-auth.yml,然后配置容器启动时命令,指定prometheus使用这两个配置文件启动即可:

image

image

我这里将启动命令配置为:

1
--config.file=/etc/prometheus/myconfig/prometheus/prometheus.yml --web.config.file=/etc/prometheus/myconfig/prometheus/web-auth.yml

服务重启之后,prometheus的基本认证就成功启用了。


使用prometheus监控go程序

一个简单的测试程序编写如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package main

import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"math/rand"
"net/http"
"time"
)

func main() {
metricLabels := prometheus.Labels{
"program_name": "test",
}

prometheus.DefaultRegisterer = prometheus.WrapRegistererWith(metricLabels,
prometheus.WrapRegistererWithPrefix("long_", prometheus.DefaultRegisterer))

prometheus.MustRegister(promTestUsageFunc)
prometheus.MustRegister(promTestUsage)
prometheus.MustRegister(promTestCounter)
prometheus.MustRegister(promTestMemUsage)
prometheus.MustRegister(promTestHist)

go updatePromTestHist()

http.Handle("/metrics", promhttp.HandlerFor(
prometheus.DefaultGatherer,
promhttp.HandlerOpts{
EnableOpenMetrics: true,
},
))

err := http.ListenAndServe(":12345", nil) // prometheus的/metrics监听在12345端口
if err != nil {
panic(err)
}
}

// 这里定义四个变量用于测试
var (
// 使用函数调用的方式每次返回 Gauge 类型的值
promTestUsageFunc = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
Name: "test_usage_func",
Help: "test usage like cpu",
}, func() float64 {
return getTestUsage()
})

// 使用变量的方式每次返回 Gauge 类型的值,与promTestUsageFunc完全一样
promTestUsage = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "test_usage",
Help: "test usage like cpu",
})

// 使用函数调用的方式每次返回 Gauge 类型的值
promTestMemUsage = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
Name: "test_mem_usage",
Help: "test usage like mem",
}, func() float64 {
return getMemUsage()
})


// 使用函数调用的方式每次返回 counter 类型的值
promTestCounter = prometheus.NewCounterFunc(prometheus.CounterOpts{
Name: "test_counter",
Help: "test counter always increase",
}, func() float64 {
return getTestCounter()
})

// Histogram 类型的值
promTestHist = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "test_hist",
Help: "test hist buckets",
Buckets: prometheus.ExponentialBuckets(0.05, 1.5, 10),
})
)

var testUsage float64 = 0.5

func getTestUsage() float64 { // 简单模拟一下实时cpu使用率
testUsage += 0.1 * (rand.Float64() - rand.Float64())

if testUsage < 0 {
testUsage = 0
} else if testUsage > 1 {
testUsage = 1
}

promTestUsage.Set(testUsage)

return testUsage
}

func getMemUsage() float64 { // 简单模拟一下实时Memory使用率
return 1.2
}

var testCounter float64 = 1

func getTestCounter() float64 { // 简单模拟cpu使用时长(无限累加)
testCounter += float64(rand.Intn(5))

return testCounter
}

func updatePromTestHist() { // 简单模拟请求延迟,使用正态分布
for {
sample := rand.NormFloat64()*0.5+0.09
if sample < 0 {
sample = -sample
}
promTestHist.Observe(sample)
time.Sleep(time.Millisecond*200)
}
}

这里模拟了三种通常使用的统计类型Gauge,counter和Histogram,监听在192.168.88.60的12345端口,修改prometheus配置,让它从此进程中抓取数据,修改prometheus.yml配置文件:

1
2
3
4
scrape_configs:
- job_name: test
static_configs:
- targets: ["192.168.88.60:12345"]

scrape_configs加入上面的配置,在prometheus的界面上可以看到新的target已经加入:

image

可以在prometheus界面上查询到采集的值,以及看到简单的图形:

image

image

到这里就完成了prometheus对go程序监控数据的采集。


使用grafana展示监控数据

首先在grafana中添加datasource,将prometheus服务器添加进来。添加datasource->选择prometheus,填写prometheus服务器的url,打勾basic auth,填写prometheus的用户名密码:

image

image

点击save&test,出现Data source is working表明添加数据源成功。

image

下一步,添加面板。

很多时候我们使用prometheus监控的都是一些常用服务,对于这些服务,我们可以直接到grafana的官方中找到别人分享的面板来直接使用https://grafana.com/grafana/dashboards,然后直接import它的编号或者url就可以了:

image

这里由于是我们自己写的程序,所以并没有现成的面板可以用,那么就只能自己创建了,选择New dashboard,右上+号创建新panel,然后可以对新panel进行编辑:

image

上面的go程序中模拟采集了5个数据:

  1. promTestUsageFunc:模拟cpu实时占用。
  2. promTestUsage:同上。
  3. promTestCounter:模拟cpu累计占用时间长度。
  4. promTestMemUsage:模拟Memory实时占用大小。
  5. promTestHist:模拟http请求响应时长统计。

首先对promTestUsageFunc数据(promTestUsage数据一样的)进行展示,因为它是cpu实时占用数据,所以编写的promQL很简单:

image

image

promQL语句为:

1
long_test_usage_func{program_name="test"} * 100

也就是直接查询出来,再乘上100变成百分数即可。这里的program_name="test"是上面go代码里面写的,对于同样的代码的不同进程,可以使用不同的标签来进行区分,例如test1test2等。

显示效果:

image


promTestCounter数据模拟的是cpu累计占用时长,这种数据在统计单个程序的cpu占用时很常见。它是一个无限递增的值,稍微进行一下处理,就可以把它变为cpu占用率:

image

image

promQL语句为:

1
rate(long_test_counter{program_name="test"}[1m]) * 100

这里的[1m]就是查询一分钟之内的所有long_test_counter数据:

1
2
3
4
5
6
1111 @1632453001.599
1113 @1632453011.599
1116 @1632453021.599
1120 @1632453031.599
1120 @1632453041.599
1121 @1632453051.599

前面就是long_test_counter的值,后面则是时间点,我这里每10秒采集一次,所以正好6个值。

rate函数则会去计算变化率,其实也就是每秒钟的变化量,官网文档可以参考FUNCTIONS

显示效果:

image


promTestMemUsage数据模拟的是实时的内存占用,这个值一般我们也不看它的变化情况,就单纯看一下当前占用了多少:

image

image

上面调了一下背景颜色,用4和8作为界限,也就是4一下背景为绿色,4到8之间背景为橙色,8以上为红色。

promQL语句为:

1
long_test_mem_usage{program_name="test"}

显示效果:

image


promTestHist数据模拟http请求响应时长,一般情况我们会关心分位数,例如P95,也就是95%的请求延迟在多少以下:

image

promQL语句为:

1
histogram_quantile(0.95, sum(rate(long_test_hist_bucket{program_name="test"}[60s])) by (le))

这里使用了histogram_quantile函数来计算分位数,le就是每个桶的边界值,是自带的标签。

显示效果:

image

也许我们不想看分位数,就想看到当前的延迟分布情况,这个时候可以使用grafana的heatmap来实现:

image

image

promQL语句为:

1
sum(increase(long_test_hist_bucket{program_name="test"}[2m])) by (le)

很简单的统计了一下增量,但是需要调整一下LegendFormat,显示效果如下:

image

可以通过颜色深浅来看出当前请求大部分落在了哪个范围之中。


最终效果:

image


添加变量来方便切换数据源

上面的promQL语句中,我们将program_name="test"这句话给写死了,但是如果我们启动了一个test2程序,岂不是还得去改语句?

使用变量就可以不需要再去修改语句:

image

image

image

然后修改一下promQL语句,将之前写死的test换成$program_name

1
long_test_usage_func{program_name="$program_name"} * 100

这样在界面上就可以看到这个变量值:

image

修改这个变量值,就可以看到来自其它程序的统计情况,例如这里我又启动了一个test1程序(当然prometheus也需要修改抓取配置):

image

image


2021-09-26问题:修改promethues.yml配置文件后,服务没有自动重新加载

针对应用商店部署的promethues应用,它带有一个prometheus-server-configmap-reload服务来对prometheus配置文件进行管理,可以看到它启动时的命令行参数--volume-dir=/etc/config --webhook-url=http://127.0.0.1:9090/-/reload,这里的volume-dir是配置文件映射到prometheus容器内的路径,而webhook-url,则是当它发现配置文件有修改时,会访问这个url来触发prometheus服务重新加载配置文件。

思考一下就知道为什么重新加载配置文件失败了,因为这里给prometheus服务添加了http basic auth,所有的http请求都要带有认证信息才行。

按照http basic auth的规则,修改一下url为--webhook-url=http://user:password@127.0.0.1:9090/-/reload,即可。


参考

PromQl中的的rate与irate内部是如何计算的(源码与计算)

histogram_quantile 相关的若干问题

一文搞懂 Prometheus 的直方图