Skip to content

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

License

Notifications You must be signed in to change notification settings

wubin1989/promql2influxql

Repository files navigation

promql2influxql

Coverage GoDoc Go Go Report Card Release License: MIT

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

TOC

前置条件

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

项目状态

项目结构和核心代码基本稳定,后续开发以新增特性和性能优化为主,尽量兼容旧版本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

指标类型

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

选择器(8个)

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

聚合操作(13个)

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

二元操作符(20个)

  • +:加法
  • -:减法
  • x:乘法
  • /:除法
  • %:取模
  • ^:求幂
  • and:且
  • or:或
    - [ ] unless:排除(原生influxql不支持)
    - [ ] ==:等于(原生influxql不支持)
  • !=:不等于
  • >:大于
  • <:小于
  • >=:大于等于
  • <=:小于等于
    - [ ] 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 整理

  • abs()
    - [ ] absent()(原生influxql不支持) - [ ] absent_over_time()(原生influxql不支持)
  • 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不支持)
  • deriv()
  • exp()
  • floor()
    - [ ] histogram_count()(原生influxql不支持)
    - [ ] histogram_sum()(原生influxql不支持)
    - [ ] histogram_fraction()(原生influxql不支持)
    - [ ] histogram_quantile()(原生influxql不支持)
  • holt_winters()
    - [ ] hour()(原生influxql不支持)
  • idelta()
  • increase()
  • irate()
  • label_join()
  • label_replace()
  • ln()
  • log2()
  • log10()
    - [ ] minute()(原生influxql不支持)
    - [ ] month()(原生influxql不支持)
  • predict_linear()
  • rate()
  • resets()
  • round()
  • scalar()
  • sgn()
    - [ ] sort():InfluxDB只支持order by time,Prometheus只支持order by value
    - [ ] sort_desc():InfluxDB只支持order by time,Prometheus只支持order by value
  • sqrt()
  • time()
  • timestamp()
  • vector()
    - [ ] year()(原生influxql不支持)
  • avg_over_time()
  • min_over_time()
  • max_over_time()
  • sum_over_time()
  • count_over_time()
  • quantile_over_time()
  • stddev_over_time()
    - [ ] stdvar_over_time()(原生influxql不支持)
  • last_over_time()
    - [ ] present_over_time()(原生influxql不支持)
  • acos()
    - [ ] acosh()(原生influxql不支持)
  • asin()
    - [ ] asinh()(原生influxql不支持)
  • atan()
    - [ ] atanh()(原生influxql不支持)
  • cos()
    - [ ] cosh()(原生influxql不支持)
  • sin()
    - [ ] sinh()(原生influxql不支持)
  • 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