promql2influxql icon indicating copy to clipboard operation
promql2influxql copied to clipboard

Convert PromQL to InfluxQL, plus a RESTful service as Prometheus adaptor service for Grafana

promql2influxql

Coverage GoDoc Go Go Report Card Release License: MIT

本项目是PromQL转InfluxQL转译器和适配器,实现了传入原生PromQL查询语句,转成InfluxQL语句,并查询InfluxDB数据库返回结果。

TOC

  • 前置条件
  • 项目状态
  • 特性说明
  • 截图
  • 应用场景
  • 项目结构
  • UML类图
  • Prometheus数据写入InfluxDB格式转换
  • 查询结果数据格式
  • 使用方式
    • 第三方库
    • RESTful服务
      • 架构设计
      • 本地启动
      • 测试环境
  • 如何扩展
    • 给适配服务扩展其他数据源适配器
    • 扩展其他适配服务和数据源适配器
  • TODO
    • 指标类型
    • 选择器(8个)
    • 聚合操作(13个)
    • 二元操作符(20个)
    • 内置函数(共70个,已支持24个)
  • 其他说明
    • 关于查询时间范围
    • 关于图表数据查询
    • 暂不支持PromQL多measurement查询和二元操作符两边同时为VectorSelector或MatrixSelector表达式查询
  • Credits
  • License

前置条件

本程序基于以下前置条件开发:

项目状态

项目结构和核心代码基本稳定,后续开发以新增特性和性能优化为主,尽量兼容旧版本API。v1.0版本之前不建议用于生产环境。

特性说明

  • 支持Prometheus四种指标类型:Counter、Gauge、Histogram和Summary。
  • 支持PromQL的7种选择器表达式、10种聚合操作表达式、13种二元操作表达式、24种内置函数转译到InfluxQL查询语句。
  • 支持作为Prometheus数据源的适配器服务接入Grafana,输入PromQL查询语句实际由适配器服务向InfluxDB实例发起查询请求和返回结果。
  • 既可以作为第三方库在你的项目中依赖,也可以作为微服务单独部署。
  • 遵循SOLID软件设计原则,采用面向微服务架构的代码组织结构,易扩展。

截图

截图中的dashboard来自Go Metrics。有部分PromQL函数和表达式未支持,所以有个别图没有数据。后续版本都会支持到。
原dashboard是针对部署在Kubernetes平台的应用的,作者修改了原dashboard里的查询标签。修改后的dashboard的存放在applications/prom/promethues_influxdb_grafana_stack/grafana/provisioning/dashboards/Go Metrics-1673758370201.json路径下。
screencapture-go-metrics-2023-01-12-16_37_22.png

应用场景

promql2influxql.png

如果你想用InfluxDB作为时序数据的底层存储,同时又希望能继续使用Prometheus的PromQL查询语句做数据分析,可以采用本项目applications模块下的prom适配服务替换掉Prometheus的接口服务,仅将Prometheus用作监控数据采集服务。

项目结构

本项目采用单仓库多模块的项目结构开发。包含:

  • applications模块:负责提供适配层RESTful接口服务
    • prom模块:负责提供适配Grafana的Prometheus数据源的RESTful接口服务
  • adaptors模块:负责提供各种适配器和转译器
.
├── LICENSE
├── README.md
├── adaptors                                 # 负责提供各种适配器和转译器
│   ├── coverage.out
│   ├── go.mod
│   ├── go.sum
│   ├── prom                                 # PromQL相关适配器和转译器
│   │   ├── influxdb                         # PromQL转InfluxQL,适配InfluxDB数据源
│   │   ├── influxdbadaptor.go               # InfluxDB适配器
│   │   ├── influxdbadaptor_test.go
│   │   └── testdata
│   └── storages                             # 数据源相关的结构体
│       └── influxdb
├── applications                             # 负责提供适配层RESTful接口服务
│   ├── constants.go
│   ├── go.mod
│   ├── prom                                 # 适配Grafana的Prometheus数据源的RESTful接口服务
│   │   ├── Dockerfile
│   │   ├── client
│   │   ├── cmd
│   │   ├── config
│   │   ├── db
│   │   ├── docker-compose.yml
│   │   ├── dto
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── helper.go
│   │   ├── prom_openapi3.go
│   │   ├── prom_openapi3.json
│   │   ├── promethues_influxdb_grafana_stack
│   │   ├── svc.go
│   │   ├── svcimpl.go
│   │   ├── svcimpl_test.go
│   │   └── transport
│   └── prom.go                              # 需要适配器实现的接口
├── architecture.png
├── coverage.out
├── promql2influxql.png
├── screencapture-go-metrics-2023-01-12-16_37_22.png
├── screencapture-node-exporter-full-2023-01-12-16_41_13.pdf
└── uml.png

15 directories, 26 files

UML类图

uml.png

Prometheus数据写入InfluxDB格式转换

# Prometheus metric
example_metric{queue="0:http://example:8086/api/v1/prom/write?db=prometheus",le="0.005"} 308

# Same metric parsed into InfluxDB
measurement
  example_metric
tags
  queue = "0:http://example:8086/api/v1/prom/write?db=prometheus"
  le = "0.005"
  job = "prometheus"
  instance = "localhost:9090"
  __name__ = "example_metric"
fields
  value = 308

查询结果数据格式

{
  "resultType": "vector",
  "result": [
    {
      "metric": {
        "container": "alertmanager",
        "endpoint": "web",
        "instance": "172.17.0.4:9093",
        "job": "alertmanager-main",
        "namespace": "monitoring",
        "pod": "alertmanager-main-0",
        "service": "alertmanager-main"
      },
      "value": [
        1672995857.892,
        "8060"
      ]
    }
  ]
}

使用方式

本项目有两种使用方式:第三方库、RESTful服务等

第三方库

直接在你的项目根路径下执行go get命令即可。

go get -d github.com/wubin1989/promql2influxql/[email protected]

RESTful服务

RESTful服务代码在applications/prom路径下,是一个单独的go模块。已经有了Dockerfile和docker-compose.yml文件。推荐测试环境采用docker方式部署。

架构设计

architecture.png

本地启动

go run cmd/main.go

可看到如下命令行日志输出:

➜  rpc git:(main) ✗ go run cmd/main.go                                 
2023/01/12 19:57:18 maxprocs: Leaving GOMAXPROCS=16: CPU quota undefined
                           _                    _
                          | |                  | |
  __ _   ___   ______   __| |  ___   _   _   __| |  ___   _   _
 / _` | / _ \ |______| / _` | / _ \ | | | | / _` | / _ \ | | | |
| (_| || (_) |        | (_| || (_) || |_| || (_| || (_) || |_| |
 \__, | \___/          \__,_| \___/  \__,_| \__,_| \___/  \__,_|
  __/ |
 |___/
2023-01-12 19:57:18 INF ================ Registered Routes ================
2023-01-12 19:57:18 INF +---------------------------+--------+----------------------------------+
2023-01-12 19:57:18 INF |           NAME            | METHOD |             PATTERN              |
2023-01-12 19:57:18 INF +---------------------------+--------+----------------------------------+
2023-01-12 19:57:18 INF | Query                     | POST   | /api/v1/query                    |
2023-01-12 19:57:18 INF | GetQuery                  | GET    | /api/v1/query                    |
2023-01-12 19:57:18 INF | Query_range               | POST   | /api/v1/query_range              |
2023-01-12 19:57:18 INF | GetQuery_range            | GET    | /api/v1/query_range              |
2023-01-12 19:57:18 INF | GetLabel_Label_nameValues | GET    | /api/v1/label/:label_name/values |
2023-01-12 19:57:18 INF | GetDoc                    | GET    | /go-doudou/doc                   |
2023-01-12 19:57:18 INF | GetOpenAPI                | GET    | /go-doudou/openapi.json          |
2023-01-12 19:57:18 INF | Prometheus                | GET    | /go-doudou/prometheus            |
2023-01-12 19:57:18 INF | GetConfig                 | GET    | /go-doudou/config                |
2023-01-12 19:57:18 INF | GetStatsvizWs             | GET    | /go-doudou/statsviz/ws           |
2023-01-12 19:57:18 INF | GetStatsviz               | GET    | /go-doudou/statsviz/*            |
2023-01-12 19:57:18 INF | GetDebugPprofCmdline      | GET    | /debug/pprof/cmdline             |
2023-01-12 19:57:18 INF | GetDebugPprofProfile      | GET    | /debug/pprof/profile             |
2023-01-12 19:57:18 INF | GetDebugPprofSymbol       | GET    | /debug/pprof/symbol              |
2023-01-12 19:57:18 INF | GetDebugPprofTrace        | GET    | /debug/pprof/trace               |
2023-01-12 19:57:18 INF | GetDebugPprofIndex        | GET    | /debug/pprof/*                   |
2023-01-12 19:57:18 INF +---------------------------+--------+----------------------------------+
2023-01-12 19:57:18 INF ===================================================
2023-01-12 19:57:18 INF Http server is listening at :9090
2023-01-12 19:57:18 INF Http server started in 6.225365ms

在线Swagger接口文档地址:http://localhost:9090/go-doudou/doc
接口文档http basic用户名/密码:admin/admin

测试环境

打包docker镜像

docker build -t promql2influxql_promql2influxql .

启动RESTful服务和基础设施容器

docker-compose -f docker-compose.yml up -d --remove-orphans

可以看到如下命令行日志输出

➜  rpc git:(main) ✗ docker-compose -f docker-compose.yml up -d --remove-orphans
[+] Running 6/6
 ⠿ Network rpc_default                        Created                                                                                                                                  0.1s
 ⠿ Container promql2influxql_influxdb         Started                                                                                                                                  1.1s
 ⠿ Container promql2influxql_node_exporter    Started                                                                                                                                  0.3s
 ⠿ Container promql2influxql_promql2influxql  Started                                                                                                                                  1.0s
 ⠿ Container promql2influxql_grafana          Started                                                                                                                                  1.0s
 ⠿ Container promql2influxql_prometheus       Started 

以下是各服务的请求地址:

  • promql2influxql服务:http://promql2influxql_promql2influxql:9090(需要配置到grafana数据源)
  • promql2influxql服务在线Swagger接口文档地址:http://localhost:9091/go-doudou/doc
    接口文档http basic用户名/密码:admin/admin
  • Grafana:http://localhost:3000
  • Prometheus:http://localhost:9090(仅用作监控数据采集服务)
  • Influxdb:http://promql2influxql_influxdb:8086

如何扩展

给适配服务扩展其他数据源适配器

如果需要新增其他数据源的PromQL转译和适配,只需在adaptors/prom路径下复制一套influxdb包的代码,修改使用即可。比如需要新增对Elasticsearch数据源的适配,只需将influxdb包的代码复制一套改成elastic(或随便什么名字)包,在里面实现转译逻辑,然后在prom包路径下新增一个elasticadaptor.go文件,把influxdbadaptor.go文件里的代码复制进去改改就可以了。

扩展其他适配服务和数据源适配器

如果需要为Grafana新增其他查询语言的适配服务,需要首先在applications路径下复制一套prom包的代码改造成一套适配服务,然后在adaptors路径下复制一套prom包的代码改造成适配器,再在适配服务里调用即可。比如需求是用Elasticsearch查询语言去查询InfluxDB里的数据,需要首先在applications路径下复制一套prom包的代码,命名为elastic包,然后在adaptors路径下复制一套prom包的代码,命名为elastic包,在applications模块下的elastic包里开发适配服务,在adaptors模块下的elastic包里开发适配器,最后在适配服务里调用适配器就可以了。

TODO

指标类型

  • [x] Counter:计数器
  • [x] Gauge:仪表盘
  • [x] Histogram:直方图
  • [x] Summary:摘要

选择器(8个)

  • [x] =:相等匹配器
  • [x] !=:不相等匹配器
  • [x] =~:正则表达式匹配器
  • [x] !~:正则表达式相反匹配器
  • [x] {}:瞬时向量选择器
  • [x] {}[]:区间向量选择器
    ~~- [ ] {}[:]:子查询~~(原生influxql不支持)
  • [x] offset:偏移量修改器

聚合操作(13个)

  • [x] by:相当于InfluxQL的group by语句
    ~~- [ ] without:忽略指定标签,by的相反操作~~(原生influxql不支持)
  • [x] sum:求和
  • [x] min:最小值
  • [x] max:最大值
  • [x] avg:平均值
  • [x] stddev:标准差
    ~~- [ ] stdvar:标准差异~~(原生influxql不支持)
  • [x] count:统计结果行数
    ~~- [ ] count_values:按值分组,统计每组的结果行数~~(原生influxql不支持)
  • [x] bottomk:样本值最小的k个元素
  • [x] topk:样本值最大的k个元素
  • [x] quantile:分布统计

二元操作符(20个)

  • [x] +:加法
  • [x] -:减法
  • [x] x:乘法
  • [x] /:除法
  • [x] %:取模
  • [x] ^:求幂
  • [x] and:且
  • [x] or:或
    ~~- [ ] unless:排除~~(原生influxql不支持)
    ~~- [ ] ==:等于~~(原生influxql不支持)
  • [x] !=:不等于
  • [x] >:大于
  • [x] <:小于
  • [x] >=:大于等于
  • [x] <=:小于等于
    ~~- [ ] bool:0表示false,1表示true~~(原生influxql不支持)
    ~~- [ ] ignoring:忽略标签~~(原生influxql不支持)
    ~~- [ ] on:与ignoring相反,类似by~~(原生influxql不支持)
    ~~- [ ] group_left:多对一,类似sql的左连接~~(原生influxql不支持)
    ~~- [ ] group_right:一对多,类似sql的右连接~~(原生influxql不支持)

内置函数(共70个,已支持24个)

根据官方文档 https://prometheus.io/docs/prometheus/latest/querying/functions/#trigonometric-functions 整理

  • [x] abs()
    ~~- [ ] absent()~~(原生influxql不支持) ~~- [ ] absent_over_time()~~(原生influxql不支持)
  • [x] ceil()
    ~~- [ ] changes()~~(原生influxql不支持)
  • [ ] clamp():按最大值、最小值区间范围筛选
  • [ ] clamp_max():按最大值筛选
  • [ ] clamp_min():按最小值筛选
    ~~- [ ] day_of_month()~~(原生influxql不支持)
    ~~- [ ] day_of_week()~~(原生influxql不支持)
    ~~- [ ] day_of_year()~~(原生influxql不支持)
    ~~- [ ] days_in_month()~~(原生influxql不支持)
    ~~- [ ] delta()~~(原生influxql不支持)
  • [x] deriv()
  • [x] exp()
  • [x] floor()
    ~~- [ ] histogram_count()~~(原生influxql不支持)
    ~~- [ ] histogram_sum()~~(原生influxql不支持)
    ~~- [ ] histogram_fraction()~~(原生influxql不支持)
    ~~- [ ] histogram_quantile()~~(原生influxql不支持)
  • [ ] holt_winters()
    ~~- [ ] hour()~~(原生influxql不支持)
  • [ ] idelta()
  • [ ] increase()
  • [ ] irate()
  • [ ] label_join()
  • [ ] label_replace()
  • [x] ln()
  • [x] log2()
  • [x] log10()
    ~~- [ ] minute()~~(原生influxql不支持)
    ~~- [ ] month()~~(原生influxql不支持)
  • [ ] predict_linear()
  • [x] rate()
  • [ ] resets()
  • [x] round()
  • [ ] scalar()
  • [ ] sgn()
    ~~- [ ] sort()~~:InfluxDB只支持order by time,Prometheus只支持order by value
    ~~- [ ] sort_desc()~~:InfluxDB只支持order by time,Prometheus只支持order by value
  • [x] sqrt()
  • [ ] time()
  • [ ] timestamp()
  • [ ] vector()
    ~~- [ ] year()~~(原生influxql不支持)
  • [x] avg_over_time()
  • [x] min_over_time()
  • [x] max_over_time()
  • [x] sum_over_time()
  • [x] count_over_time()
  • [x] quantile_over_time()
  • [x] stddev_over_time()
    ~~- [ ] stdvar_over_time()~~(原生influxql不支持)
  • [ ] last_over_time()
    ~~- [ ] present_over_time()~~(原生influxql不支持)
  • [x] acos()
    ~~- [ ] acosh()~~(原生influxql不支持)
  • [x] asin()
    ~~- [ ] asinh()~~(原生influxql不支持)
  • [x] atan()
    ~~- [ ] atanh()~~(原生influxql不支持)
  • [x] cos()
    ~~- [ ] cosh()~~(原生influxql不支持)
  • [x] sin()
    ~~- [ ] sinh()~~(原生influxql不支持)
  • [x] tan()
    ~~- [ ] tanh()~~(原生influxql不支持)
    ~~- [ ] deg()~~(原生influxql不支持)
    ~~- [ ] pi()~~(原生influxql不支持)
    ~~- [ ] rad()~~(原生influxql不支持)

其他说明

关于查询时间范围

  • 结束时间取值优先级从最高到最低依次是:
    • End参数
    • PromQL查询命令中的@表达式
    • Evaluation参数
    • 当前时间 以上的结果会跟PromQL查询命令中的offset表达式再计算得出最终的结束时间
  • 开始时间只取Start参数

关于图表数据查询

因为原生InfluxQL不支持Prometheus的/api/v1/query_range接口的step参数和相应的计算机制,例如一段时间范围内,每隔3分钟,计算一次前10分钟的http请求增长速率,原生InfluxQL只能做到利用group by time(3m)语句实现一段时间范围内每隔3分钟,计算一次前3分钟的http请求增长速率,所以本项目对此的处理方式是:当PromQL查询语句中包含区间向量查询,例如go_gc_duration_seconds_count[5m]中的[5m],同时传了Step参数,则忽略Step参数,取区间时间范围的5m作为group by time(interval)语句中的interval参数值。

暂不支持PromQL多measurement查询和二元操作符两边同时为VectorSelector或MatrixSelector表达式查询

原生InfluxQL语句实现不了,后续计划通过进行多次InfluxQL查询后在内存中计算实现。

Credits

本项目参考了 https://github.com/influxdata/flux 项目的PromQL转Flux转译器的代码。此外,还依赖了很多非常优秀的开源项目。在此向各位开源作者表示感谢!

License

MIT