探究envoy metrics与prometheus集成

哎,失踪人口又回归了,三个月没发了,不该不该

最近工作主要分析向量DB的查询性能瓶颈。由于我们是嵌入式DB,并没搞服务器DB 那套业界流利的监控框架。但是由于业务场景是针对千万、亿级的向量查询,其实也不再那么"嵌入式"了,所以完全是可以把优秀的监控方法借鉴过来。

我指的是就是那套exporter + prometheus + grafana组合,利用强大的PromSQL能力 + grafana面板监控来更好地捕捉系统正在发生的事情,放大 observability 能力,避免简单地只从日志、火焰图上看信息,对复杂性能问题,远远不够!

但目前DB又没有暴露满足符合prometheus格式的数据。按照我以前的经验,对redis-cerberus 程序做过如下的事情:

  1. 在各个地方记录请求数和请求时延

  2. 创建单独http 监听线程,将其他线程的counter, gauge获取到,借助prometheus-cpp库暴露到http端口,以让prometheus来抓。即程序本身就自带了exporter功能。

这一套弄下来,感觉要费点时间。我想有没有更简单的暴露方式?问claude: prometheus在哪些c/c++程序中得到应用了?看到了envoy,说它里面用到了prometheus-cpp(实际发现并没有),但意外发现它自己内部生成了简单格式的数据,然后发给statsd-exporter,而后者是一个通用性的exporter,可自定义mapping rule来将任何数据转换成prometheus 接受的格式。顿时感觉这个好,研究一下。

本文研究一下怎么快速部署envoy和发现其和statsd-export交互的过程。

直接二进制启动

envoy 直接可从github release 下载。

本是可以用docker compose快速搭建的,比如我找到了 [1],可以跑通,但发现无常规命令去跟踪envoy 发了什么内容给 statsd-exporter,这是我最想知道的。

于是我参考了另外一篇文章[2],对其配置文件改造如下:

node:
  id: front-envoy
  cluster: mycluster

# Envoy 管理配置
admin:
  # 访问日志路径
  access_log_path: "/data/tmp/envoy.log"
  # 管理界面监听地址和端口
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 10001

stats_sinks:
- name: envoy.stat_sinks.statsd
  typed_config:
    "@type": type.googleapis.com/envoy.config.metrics.v3.StatsdSink
    tcp_cluster_name: statsd_exporter
    prefix: front-envoy

# 静态资源配置
static_resources:
  listeners:
    # 定义一个监听器,监听在 0.0.0.0:10000
    - address:
        socket_address: { protocol: TCP, address: 0.0.0.0, port_value: 10000 }
      filter_chains:
        - filters:
            # 使用 HTTP 连接管理器过滤器
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                codec_type: auto
                stat_prefix: http
                access_log:
                  # 记录 HTTP 请求到文件
                  - name: envoy.access_loggers.file
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
                      path: "/data/tmp/envoy_http.log"
                      log_format:
                        text_format: |
                          >>>>
                          start_time: "%START_TIME%"
                          method: "%REQ(:METHOD)%", original_path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", protocol: "%PROTOCOL%", response_code: "%RESPONSE_CODE%", response_flags: "%RESPONSE_FLAGS%"
                          user_agent: "%REQ(USER-AGENT)%"
                          authority: "%REQ(:AUTHORITY)%"
                          upstream_host: "%UPSTREAM_HOST%"
                          request_id: "%REQ(X-REQUEST-ID)%"
                          custom_header: "%REQ(custom_header)%"
                          static_header: "%REQ(static_header)%"
                route_config:
                  name: search_route
                  virtual_hosts:
                    - name: backend
                      domains:
                        - "*"
                      routes:
                        - match:
                            prefix: "/"
                          route:
                            # 将请求发送到 baidu 集群
                            #cluster: baidu
                            cluster: localserver
                http_filters:
                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
    - name: baidu
      connect_timeout: 1s
      # 使用 STRICT_DNS 模式进行 DNS 解析
      type: STRICT_DNS
      dns_lookup_family: V4_ONLY
      lb_policy: round_robin
      load_assignment:
        cluster_name: baidu
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      # 目标服务器地址
                      address: www.baidu.com
                      port_value: 80

    - name: localserver
      connect_timeout: 1s
      # 使用 STATIC 模式,直接指定目标地址
      type: STATIC
      dns_lookup_family: V4_ONLY
      lb_policy: round_robin
      load_assignment:
        cluster_name: localserver
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      # 本地服务器地址
                      address: 127.0.0.1
                      port_value: 8081

    - name: statsd_exporter
      connect_timeout: 0.25s
      type: strict_dns
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: statsd_exporter
        endpoints:
        - lb_endpoints:
          - endpoint:
              address:
                socket_address:
                  address: localhost
                  port_value: 9125

其中,

  1. 配置一个简单的http 代理,将请求发往8081端口,我会在那用 python -m http.server 8081 创建一个简单的http server.
  2. 要想数据发往statsd-exporter,必须配置stats_sink和stats_exporter 的cluster,打算用docker启动一个statsd-exporter容器

启动

envoy: envoy -c front-proxy.yaml -l info

statsd-exporter: docker run -p 9102:9102 -p 9125:9125 -p 9125:9125/udp prom/statsd-exporter 看官方的例子是:

docker run -d -p 9102:9102 -p 9125:9125 -p 9125:9125/udp \
        -v $PWD/statsd_mapping.yml:/tmp/statsd_mapping.yml \
        prom/statsd-exporter --statsd.mapping-config=/tmp/statsd_mapping.yml

暂未研究mapping是怎么弄的,后期可能需要考虑将我们DB的格式通过它映射成prometheus格式

抓取envoy主动向exporter发送的数据

用tcpdump抓取方便(与9102端口区分开来,它是向prometheus暴露的http 端口): tcpdump -i any tcp dst port 9125 -A -nn

数据如下(定期接收到):

13:50:46.463664 lo    In  IP6 ::1.41804 > ::1.9125: Flags [P.], seq 910642444:910649206, ack
882327520 ecr 3882322520], length 6762
`.@....@.................................L#.6GM./..............
.g...g.Xfront-envoy.cluster_manager.cluster_added:0|c
front-envoy.cluster.localserver.default.total_match_count:0|c
front-envoy.cluster.localserver.upstream_cx_destroy:0|c
front-envoy.cluster.statsd_exporter.upstream_cx_tx_bytes_total:6762|c
front-envoy.cluster.localserver.external.upstream_rq_200:0|c
front-envoy.listener_manager.listener_create_success:0|c
front-envoy.dns.cares.resolve_total:2|c
front-envoy.cluster.localserver.upstream_rq_200:0|c
front-envoy.runtime.override_dir_not_exists:0|c
front-envoy.filesystem.flushed_by_timer:2|c
front-envoy.cluster.localserver.upstream_rq_total:0|c
front-envoy.http.http.downstream_cx_total:0|c

13:50:46.463871 docker0 Out IP 172.17.0.1.32918 > 172.17.0.2.9125: Flags [P.], seq 241
[nop,nop,TS val 889465382 ecr 807951972], length 6762
E...JD@.@.}...........#.....).......r......
5.*&0(^dfront-envoy.cluster_manager.cluster_added:0|c
front-envoy.cluster.localserver.default.total_match_count:0|c
front-envoy.cluster.localserver.upstream_cx_destroy:0|c
front-envoy.cluster.statsd_exporter.upstream_cx_tx_bytes_total:6762|c
front-envoy.cluster.localserver.external.upstream_rq_200:0|c
front-envoy.listener_manager.listener_create_success:0|c
front-envoy.dns.cares.resolve_total:2|c
front-envoy.cluster.localserver.upstream_rq_200:0|c
front-envoy.runtime.override_dir_not_exists:0|c
front-envoy.filesystem.flushed_by_timer:2|c

原来是这样的格式,后面的c 代表counter, g 代表 gauge

导入到prometheus并简单测试

我是在另外一条机器上单独起了prometheus 容器,

docker run --rm --name prom -p 9090:9090 -v ./coding/prom.yml:/etc/prometheus/prometheus.yml prom/prometheus

增加配置:

- job_name: 'statsd'
    scrape_interval: 5s
    static_configs:
      - targets: ['192.168.0.111:9102']
        labels:
          group: 'group1

bash端循环发请求:

while true; do curl -s localhost:10000 | grep wtperf.svg; sleep 0.01; done

一段时间后prometheus 9090端口简单查询:

有点没明白平均qps 35 是怎么来的。。

OK. 基本上弄清楚了。下一步就可看看envoy 源码怎么生成类似front-envoy.cluster.localserver.external.upstream_rq_200:0|c 这样的格式,以便用在内部DB上, statsd-exporter可拿来即用


更多硬核技术,关注公众号"极客影忍", Happy Coding ~

References

参考资料
[1]

https://www.cnblogs.com/wangguishe/p/16700590.html: docker-compose启动envoy

[2]

https://juejin.cn/post/7321410883875143730: 单机启动envoy的一个前端代理示例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值