目录
背景
Prometheus的整体设计模型是基于单实例的。如果要打造高可用的Prometheus服务,一般通过2种方式进行:
- 简单的多节点,数据仍就存在本地;
- 使用remote_read和remote_write功能。
这部分内容大家可以参考这篇文章《prometheus学习4:多集群高可用》
需求目标
- 存储必须同时支持读写
- 由于部分方案不支持读,只支持写(如OpenTSDB),使用方法必须自行通过存储接口读取数据。学习成本较高。所以在定义这一次需求的目标时,要求选择的存储必须同时支持读写
- 远端存储属于高可用架构
- 由于整个项目的统一目标关键词为“高可用”所以在选择远程存储时也必须考虑到存储本身也具有相同特性
- 最好是在公司内部有现成的服务
- 可以降低整个需求的推进难度,节约成本
存储选型
Prometheus官方文档
使用开源产品第一原则:Base官网文档。所以咱们先来看一下官方文档中支持哪些远程存储,以及如何使用
存储
在官方文档中找到了Remote Endpoints and Storage这一节,列举了所有目前官方已知的存储清单及读写支持
使用方式
《Remote Endpoints and Storage》这篇文档的第一段“remote write”和“remote read”点击超链接即可跳转到官方Configuration文档中相应的小节。
remote write config
remote read
Demo
官方文档中还有一个Git Hub的文档,里面给了简单的Demo来展示如何配置adapter
小结
基于如上的文档可以得出一些基本结论
- remote read\write是通过各类adapter,通过Http接口进行操作
选型
经过公司内部多方了解,当前公司内部提供的在清单内的公共服务有:
- Elasticsearch
- Kafka
- OpenTSDB
- PostgreSQL(实际在测试阶段,未对外开放)
- TiKV
- Thanos
根据之前指定的需求目标,真正符合需求的只有TiKV
TiKV接入Prometheus
通过官方文档的Git Hub超链接,我们转到了TiPrometheus这个项目的GitHub主页。
话说真心要吐槽一下这篇文档,只标识了如何构建和启动项目,都没有给到Prometheus内的配置Demo甚至接口API。没办法,这部分只能边分析源码边摸索了(坑爹的是这居然还是Go写的!)
Docker镜像
读过我其他文章的童鞋知道前几个月我将整套服务都进行了容器化。由于其他用的都是应用比较广泛的开源项目,官方都放出了Docker image。也有比较完备的官方文档和各种网络事件文章。所以在当前环境下,对于我来说的优选是找到官方镜像
但是!没有官方镜像
好吧,在docker hub上找找有没有其它人已经做好的镜像
第三方镜像
太高兴了,找到了红圈中的镜像(ps:另一个镜像是我后来做的,这个后面再说)
但是高兴地太早了。根据Tiprometheus项目内的conf.toml.example进行并启动后,项目总是报错
time="2020-04-23T10:18:51Z" level=info msg="[pd] create pd client with endpoints []"
time="2020-04-23T10:18:51Z" level=error msg="[pd] failed to get cluster id: rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = \"transport: Error while dialing dial tcp: missing address\""
通过如上的报错,可以确定,错误原因是代码内没有拿到PDHost这个配置!!!
经过各种方式排出己方因素后,我发现这个镜像描述中提示:Updated a year ago
好吧,到了这里还是想用起来,所以去翻一番当时的代码是什么样子的。
。。。。。。。(此处省略一万字)
总之,最后也没有能成功使用这个镜像。
这一段小结
- dreampuf/tiprometheus这个镜像最终没能用起来
- 没有其他可用的公共镜像
自己打包镜像
好吧,靠人不如靠己。
做镜像的过程中的细节我就不再赘述了,讲一下大体经过
编译源码
Tiprometheus的文档Quick Start中描述了打包的方法。但实际通过命令打包时,报错了。具体报错已经有人提交了issue#5
说实话,踩了很多坑。下面一个部分会慢慢讲
正确的编译命令
GOOS=linux CGO_ENABLED=0 GOARCH=amd64 go build -a -o tiprometheus cmd/tiprometheus/app.go
制作Docker Image
这是第一次制作Docker镜像,不过好在之前都接触过公司的发布系统,所以对Dockerfile有一定了解
基础镜像
使用了centos:centos7作为基础镜像,虽然有点儿大,但是能跑起来(各位大佬有好的建议可以留言哈)
Dockerfile
FROM busybox:latest
WORKDIR /opt/
COPY ./tiprometheus /ops/tiprometheus
USER nobody
EXPOSE 12350
ENTRYPOINT [ "/ops/tiprometheus" ]
遇到的坑!
其实最开始编译Tiprometheus的时候,命令跟GitHub的文档一样
go build -o tiprometheus cmd/tiprometheus/app.go
高高兴兴的打包,部署。结果:
[root@01 ti]# docker-compose -f docker-compose.yml up
WARNING: The Docker Engine you're using is running in swarm mode.
Compose does not use swarm mode to deploy services to multiple nodes in a swarm. All containers will be scheduled on the current node.
To deploy your application across the swarm, use `docker stack deploy`.
Removing ti_tiprometheus_1
Recreating d140c131ffe8_ti_tiprometheus_1 ... done
Attaching to ti_tiprometheus_1
tiprometheus_1 | standard_init_linux.go:190: exec user process caused "no such file or directory"
划重点:
standard_init_linux.go:190: exec user process caused "no such file or directory"
心里一万匹草泥马踏过……
各种百度,有说启动脚本不对的,有说编码不对的。。。。
后来将镜像换成了centos:centos7其他脚本不变,运行成功
我靠!
所以问题点定位为:更换了镜像之后能够成功运行
后来注意到了这篇文章:《以alpine镜像为基础将go应用部署在docker中》
最终定位到原因:
- busybox:latest这个精简镜像删减了大量的库,Go在打包时CGO_ENABLED默认为1,此时会依赖一些本地库。而只有为0时,才能打包为一个静态可执行程序
所以正确的打包姿势:
GOOS=linux CGO_ENABLED=0 GOARCH=amd64 go build -a -o tiprometheus cmd/tiprometheus/app.go
Push To Docker Hub
是的,你们猜对了,这就镜像是我做的
关于这个镜像的具体信息,可以查看详细的良心文档
部署
好了,正菜在这里,相信来这里的大部分小伙伴不是看我自嗨的,是想知道怎么用
docker-compose.yml
version: '2'
services:
tiprometheus:
image: shikaiwei/tiprometheus:20200428.RELEASE
restart: always
ports:
- 12350:12350
volumes:
- /usr/local/tipromethuse-docker/conf/conf.toml:/opt/conf.toml
container_name: tiprometheus
在良心文档中有提过,这里要再强调一下:镜像中没有自带conf.toml这个配置文件,必须要在启动Docker的时候将它映射到路径/opt/conf.toml下!
conf.toml
[dev]
AdapterListen = "127.0.0.1:12350"
PDHost = "10.0.0.221:2000"
TimeInterval = 5
AdapterEnableTLS = false
TiKVEnableTLS = false
[default]
AdapterListen = ":12350"
PDHost = "10.0.0.221:2000"
TimeInterval = 5
AdapterEnableTLS = false
AdapterCACertificate = "/path/to/certs/ca.pem"
AdapterServerCertificate = "/path/to/certs/adapter.pem"
AdapterServerKey = "/path/to/certs/adapter.key"
TiKVEnableTLS = false
TiKVCACertificate = "/path/to/certs/ca.pem"
TiKVClientCertificate = "/path/to/certs/client.pem"
TiKVClientKey = "/path/to/certs/client.key"
这里实际生效的是default部分,除非你在启动命令中使用command属性另行定义
注意大坑:AdapterListen如果写成127.0.0.1:12350的形式,那么服务会监听本机请求,并拒绝外部请求。如果使用容器部署则一定要写成:
AdapterListen = ":12350"
prometheus.yml
这里仅给到需要配置的部分
remote_write:
- url: 'http://tiprometheus:12350/write'
remote_read:
- url: 'http://tiprometheus:12350/read'
read_recent: false
注意:
- 这里使用了域名的方式,因为在docker-compose.yml中定义了container_name,同时在实际部署中,我将这个容器和Prometheus写在了同一个docker-compose中,使得其使用了同一个子网(也可以用其他方式实现)。
- 如果没有使用上述方式部署,则这里的url应改成:
http://[宿主机IP]:[宿主机映射端口]/write
http://[宿主机IP]:[宿主机映射端口]/read
部署成功
如果成功部署,则会在Tiprometheus的容器日志中看到如下数据:
2020-04-26 11:41:48:2020/04/26 03:39:42 Naive write data: [name:"__name__" value:"node_network_receive_multicast_total" name:"device" value:"lo" name:"instance" value:"node-exporter:9100" name:"job" value:"node" ] [{0 1587872369102}]
2020-04-26 11:41:48:2020/04/26 03:39:42 Naive write data: [name:"__name__" value:"process_open_fds" name:"instance" value:"localhost:9090" name:"job" value:"prometheus" ] [{158 1587872377008}]
2020-04-26 11:41:48:2020/04/26 03:39:42 Naive write data: [name:"__name__" value:"net_conntrack_dialer_conn_failed_total" name:"dialer_name" value:"remote_storage" name:"instance" value:"localhost:9090" name:"job" value:"prometheus" name:"reason" value:"resolution" ] [{0 1587872377008}]
2020-04-26 11:41:48:2020/04/26 03:39:42 Naive write data: [name:"__name__" value:"go_memstats_mspan_sys_bytes" name:"instance" value:"localhost:9090" name:"job" value:"prometheus" ] [{2.146304e+06 1587872377008}]
2020-04-26 11:41:48:2020/04/26 03:39:42 Naive write data: [name:"__name__" value:"prometheus_engine_query_duration_seconds" name:"instance" value:"localhost:9090" name:"job" value:"prometheus" name:"quantile" value:"0.5" name:"slice" value:"inner_eval" ] [{0.000308029 1587872377008}]
2020-04-26 11:41:48:2020/04/26 03:39:42 Naive write data: [name:"__name__" value:"prometheus_engine_query_duration_seconds" name:"instance" value:"localhost:9090" name:"job" value:"prometheus" name:"quantile" value:"0.9" name:"slice" value:"inner_eval" ] [{0.014236842 1587872377008}]
2020-04-26 11:41:48:2020/04/26 03:39:42 Naive write data: [name:"__name__" value:"prometheus_config_last_reload_successful" name:"instance" value:"localhost:9090" name:"job" value:"prometheus" ] [{1 1587872377008}]
2020-04-26 11:41:48:2020/04/26 03:39:42 Naive write data: [name:"__name__" value:"prometheus_engine_query_duration_seconds" name:"instance" value:"localhost:9090" name:"job" value:"prometheus" name:"quantile" value:"0.99" name:"slice" value:"inner_eval" ] [{0.099016172 1587872377008}]
总结
- 当无可用的官方镜像和第三方镜像时,建议自己做,好用就上传到DockerHub,利人利己
- 对于文档不多的第三方应用,使用有不确定的地方,尝试读一下源码
- 发布的公共服务\组件一定要多写文档,把所有人都当成小白好过要求使用者都成为大师