本文主要介绍基于promethues的go统计功能,利用promethues配合grafana进行实时数据的展示。因此promethues和promethues的测试功能是以docker的形式存在,并且以默认的方式启动docker,并不能用于实际的生产环境中。
1.promethues的docker环境准备
这里使用了promethues的service和exporter两部分。exporter类似一个代理,安装了exporter的机器可以将配置的需要采集的信息发送给service,service集中展示各个exporter发送来的数据。分两部分来介绍docker下的promethues简单使用。
1.1 拉去镜像
1.1.1 promethues的最新的docker镜像
docker pull prom/prometheus
1.1.2 promethues的exporter最新的镜像
docker pull prometheusnet/docker_exporter
1.2启动docker镜像
1.2.1 启动service
docker run --name myprometheus -p 9090:9090 -d prom/prometheus
或者
docker run -v /home/prometheus/prometheus.yml:/etc/prometheus/prometheus.yaml --name myprometheus -p 9090:9090 -d prom/prometheus
启动了一个容器名为myprometheus
的promethues
的docker容器,映射本地端口9090到docker容器的9090端口。无误启动之后在浏览器上查看promethues的9090端口是否有自带的监控界面出现,输入http://ip地址:9090
后,如下如则说明promethues已经安装正常。
1.2.2 启动exporter
docker run --name mynode-exporter -p 9100:9100 -d prom/node-exporter
启动了一个容器名为mynode-exporter
的node-exporter
的docker容器,映射本地端口9100到docker容器的9100端口。9100端口是exporter镜像的数据发送端口,当在一台机器上启动多个exporter的时候需要规划好端口,防止端口冲突。
启动后我们在同一局域网的一台机器上查看已经启动的exporter的9100端口是否有数据产生。在浏览器输入http://ip地址:9100/metrics
,如下图所示,说明exporter已经有采集的数据产生。
1.2.3 service展示exporter的数据
将第2)步的exporter增加到service中,为了能够在service中显示exporter的数据。需要在service中的配置文件promethues.yaml
中增加exporter的信息。因为启动的myprometheus
容器是没有挂载到宿主机磁盘的,因此需要进入该容器内部修改,操作如下:
-
执行
docker exec -it myprometheus sh
进入容器 -
打开
promethues.yaml
文件,执行vi /etc/prometheus/prometheus.yml
,在scrape_configs
下增加一组job_name
,增加完入下图:
红色部分为新增的名为node
节点配置,targets
地址填写exporter的映射地址。第2)步中为localhost:9100
,我们在这直接将宿主机的ip地址替换了localhost,到此就将新的exporter增加到了service中。
- 在promethues的docker-bash中执行
exit
退出docker容器,然后执行docker restart 容器ID
重启service
重启完后再看一下promethues的监控页面,打开http://172.20.35.40:9090
,点击Status
菜单栏下的targets
,就可以看到增加的exporter了,如下图:
2.go程序实现的exporter
2.1 promethues客户端
promethues是一个开源的分布式监控软件,除了使用自身的service和exporter外,也提供了一些客户端api,供二次开发使用。官方提供的客户端有下面:
这里使用的是client_golang
客户端,目的是通过客户端将这个程序的一些数据写入到promethues中,并且在promethues或者grafana中实时显示这些数据。
在这个程序中加载下面两个包:
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
promethues的客户端提供了4种数据:Counter
、Gauge
、Histogram
和Summary
。分别对应“累加指标”、“测量指标”、“直方图”和“概略图”。
counter
:随着时间值会增长。比如:某个数据的数据量,运行发生的错误次数,持续增加的数据包等。
Gauge
:代表了采集的单个数据,这个数据可以增加也可以减少。比如cpu的使用率,内存的使用率,硬盘的容量等。
Histogram
和Summary
:这两种的使用频率稍小,这两个都是基于采样的的方式。使用者两个时会产生多组数据,_count
表示采样的总数,_sum
表示采样的总和,_bucket
表示落入此范围的数据。
2.2 go代码实现
定义client_golang`中的四种数据类型的对象实例。
var (
MyTestCounter = prometheus.NewCounter(prometheus.CounterOpts{
//因为Name不可以重复,所以建议规则为:"部门名_业务名_模块名_标量名_类型"
Name: "my_test_counter", //唯一id,不可重复Register(),可以Unregister()
Help: "my test counter", //对此Counter的描述
})
MyTestGauge = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "my_test_gauge",
Help: "my test gauge",
})
MyTestHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "my_test_histogram",
Help: "my test histogram",
Buckets: prometheus.LinearBuckets(20, 5, 5), //第一个桶20起,每个桶间隔5,共5个桶。 所以20, 25, 30, 35, 40
})
MyTestSummary = prometheus.NewSummary(prometheus.SummaryOpts{
Name: "my_test_summary",
Help: "my test summary",
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, //返回五分数, 九分数, 九九分数
})
)
将上面定义的四种数据实例对象注册到promethues
上。promethues提供了两种注册方式,MustRegister
和Register
,Register
能够捕获注册时的错误。下面是注册的过程:
//不能注册多次Name相同的Metrics
//MustRegister注册失败将直接panic(),如果想捕获error,建议使用Register()
if err := prometheus.Register(MyTestCounter); err != nil {
log.Fatal(err)
}
prometheus.MustRegister(MyTestGauge)
prometheus.MustRegister(MyTestHistogram)
prometheus.MustRegister(MyTestSummary)
为了模拟数据,再增加一个goroutine函数。不断的向这四种数据实例上写数据,如下:
go func() {
var i float64
for {
i++
MyTestCounter.Add(10000) //每次加常量
MyTestGauge.Add(i) //每次加增量
MyTestHistogram.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10) //每次观察一个18 - 42的量
MyTestSummary.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10)
time.Sleep(time.Second)
}
}()
注册后将这个实现的exporter导出到指定的端口,这个端口用于promethues的service端采集数据。在这里我们使用go的net/http
包导出,如下:
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe("0.0.0.0:7070", nil)) //多个进程不可监听同一个端口
之后编译运行该程序,待程序运行后,在本地的浏览器或者终端上应该可以查看到该exporter产生的数据。在浏览器中输入http://127.0.0.1:7070/metrics
或者在终端中输入culr http://127.0.0.1:7070/metrics
查看,浏览器结果如下:
2.3 添加到promethues-service中
上一步完成之后实现的exporter已经产生了采集数据,但是还没有在promethues的service上显示。需要在service上进行一些配置,之后就可以显示了。
进入已经启动的service-docker镜像中,打开promethues.yaml文件,在scrape_configs
下增加一组job_name
,将自定义的exporter端口加进去,增加完入下图:
红色部分表示:定一个一个名为gotest
的job任务,目标地址是172.20.35.2:7070
,因为这个测试程序不是运行在服务器上的,而是在另一台机器上,所以要使用程序运行的ip和对应的端口。
配置完成之后退出docker容器并重启promethues的service容器。然后重新打开promethues的service数据监控页面,就能看到新添加的名为gotest
的job节点。如下:
2.4 grafana显示数据
使用promethues自带的检测工具显示采集到的数据是可以的,但是和另一个开源工具grafana比较起来就稍微差一点,promethues自带的数据展示示例如下:
我们在已经安装好的grafana上展示基于golang客户端采集的数据,分别根据四种数据类型展示对应的视图。但是这个数据和程序的编写的数据看的不是跟清楚,这一点我们在下一章节详细处理这个问题,暂时先把路走通。
3.采集系统信息的示例
编写一个基于promethues的client_golang客户端程序,这个程序能够采集当前运行机器的cpu使用率,采集虚拟内存的使用情况,并且将这些数据在grafana上展示出来。
3.1 实例代码
3.1.1 定义实例对象
var (
MyTestCounter = prometheus.NewCounter(prometheus.CounterOpts{
//因为Name不可以重复,所以建议规则为:"部门名_业务名_模块名_标量名_类型"
Name: "demo3_counter", //唯一id,不可重复Register(),可以Unregister()
Help: "this is a counter", //对此Counter的描述
})
MyTestGauge = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "demo3_gauge",
Help: "this is a gauge",
})
memGauge = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "demo3_mem_gauge_vec",
Help: "this is a mem gauge_vec",
}, []string{
"percent",
})
)
MyTestCounter
是单个的增长值,记录一个数,每次增加100。MyTestGauge
是瞬时值,记录当前的cpu使用率。memGauge
同样是瞬时值,但是定义了一个gauge的矢量,注意定义时的函数。
3.1.2 注册实例对象
if err := prometheus.Register(MyTestCounter); err != nil {
logger.Fatal(err)
}
if err := prometheus.Register(MyTestGauge); err != nil {
logger.Fatal(err)
}
if err := prometheus.Register(memGauge); err != nil {
logger.Fatal(err)
}
使用Register
捕获错误,防止由于定义实例对象时可能的名字一致导致的常见错误。
3.1.3 定义获取数据
go func() {
for {
select {
case <-tick.C:
MyTestCounter.Add(100)
totalPercent, _ := cpu.Percent(time.Second*1, false)
MyTestGauge.Set(totalPercent[0])
logger.Printf("CPU use precent:[%v]", totalPercent[0])
memInfo, _ := mem.VirtualMemory()
memGauge.WithLabelValues("total").Set(float64(memInfo.Total))
memGauge.WithLabelValues("used").Set(float64(memInfo.Used))
memGauge.WithLabelValues("Available").Set(float64(memInfo.Available))
logger.Printf("virtual memory total:[%v], used:[%v], free:[%v]", memInfo.Total, memInfo.Used, memInfo.Available)
}
}
}()
定义了一个func
,在定时器里面对实例MyTestCounter
模拟增加100。获取cpu使用率和获取虚拟内存的值,并将值用标签函数WithLabelValues
增加到实例对象中。
3.1.4 监听/metrics
http.Handle("/metrics", promhttp.Handler())
logger.Fatal(http.ListenAndServe("0.0.0.0:5050", nil))
在4040端口上发布这些数据,供promethues的service端查询数据。
3.2 运行
3.2.1 运行服务并查看数据
直接编译程序后启动程序,在浏览器中输入http://ip:5050/metrics
查看是否有数据产生,如下:
3.2.2 配置promethues的service
修改docker中promethues的service端,将新的5050端口加到配置文件中,重启docker容器。在promethues的service9090端口上应该能看到该节点的数据,如下:
3.3 grafana配置
grafana的配置比较多,在别处单独说明,在这个地方展示一下已经配置好的图形。
3.3.1 cpu使用率
左边的仪表盘记录的cpu瞬时使用率,右边部分记录了cpu的历史使用率。
3.3.2 虚拟内存使用情况
三种内存的数据。
3.3.3 count的累加
4.某代码
定义的state实例对象:
CPUTest = New().WithState("cpu_gauge", []string{"cpu", "gauge"})
state实例的实现函数:
func (this *Prom) WithState(name string, lables []string) *Prom {
if this == nil || this.state != nil {
return this
}
this.state = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: name,
Help: name,
}, lables)
prometheus.MustRegister(this.state)
return this
}
其实就是定义了一个gauge对象,使用带有lable的函数将数据写到对应的name
中去。每一个数据带有一个lable
。
将采集到的cpu数据通过CPUTest实例写到prometheus中的代码:
stat.CPUTest.State("name1", int64(totalPercent[0]), "extra1")
grafana中的图形数据:
在grafana上显示了这个数据的一些信息,解析数据的描述信息。
cpu_gauge
:是定义gauge
实例时的名称,对应GaugeOpt{}
中的name
字段。
cpu="name1"
:是标签的名字
gague="extra1"
:是额外额拓展字段名字
job="exporter_demo1"
:是这个prometheus
的exporter
在service
配置文件中的名字
修改一下调用处的函数:
go func() {
for {
totalPercent, _ := cpu.Percent(time.Second*1, true)
for index, precent := range totalPercent {
stat.CPUTest.State(fmt.Sprintf("cpu-%v", index), int64(precent), "realtime")
}
time.Sleep(time.Second)
}
}()
grafana的图像:
采集各个cpu的数据,并将每一个cpu的数据据都显示出来。
标签名cpu="cpu-0"
,gauge
显示了固定的字符串。