【问题标题】:How can I use an ngnix proxy to scrape Prometheus metrics using a custom HTTP header?如何使用 nginx 代理使用自定义 HTTP 标头来抓取 Prometheus 指标?
【发布时间】:2022-11-04 15:15:55
【问题描述】:

我需要从需要自定义 HTTP 标头x-service-token 的端点刮取 Prometheus 指标。

Prometheus 执行not include an option to scrape using a custom HTTP header,只有Authorization 标头。

One user shared a workaround 使用 nginx 创建反向代理

以防其他人来这里寻找如何做到这一点(至少还有 2 个其他问题),我有一个可以工作的小 nginx 配置。我不是 nginx 专家所以不要嘲笑! ;)

我在 docker 中运行它。用于在 9191 上监听的 nginx 的转发代理配置文件:

http {
  map $request $targetport {
    ~^GET\ http://.*:([^/]*)/ "$1";
  }
  server {
    listen 0.0.0.0:9191;
    location / {
      proxy_redirect off;
      proxy_set_header NEW-HEADER-HERE "VALUE";
      proxy_pass  $scheme://$host:$targetport$request_uri;
    }
  }
}
events {
}

运行透明转发代理:

docker run -d --name=nginx --net=host -v /path/to/nginx.conf:/etc/nginx/nginx.conf:ro nginx

在您的 prometheus 作业(或全局)中添加 proxy_url

  - job_name: 'somejob'
    metrics_path: '/something/here'
    proxy_url: 'http://proxyip:9191'
    scheme: 'http'
    static_configs:
    - targets:
      - '10.1.3.31:2004'
      - '10.1.3.31:2005'

最初由 @sra 在 https://github.com/prometheus/prometheus/issues/1724#issuecomment-282418757 发表

我尝试过配置它,但没有“主机”网络并使用host.docker.internal 而不是 localhost,但 nginx 无法连接

nginx               | 172.26.0.4 - - [31/Oct/2022:16:07:38 +0000] "GET http://host.docker.internal:8080/actuator/prometheus HTTP/1.1" 502 157 "-" "Prometheus/2.39.1"

此解决方法还需要将 API 密钥保存在文件中,这并不理想,因为这可能会意外提交到存储库。

Prometheus 锁定了 GitHub 问题,因此用户无法寻求帮助或跟进问题。

关于此主题还有另外两个 StackOverflow 问题,但答案并未尝试提供解决方法:

【问题讨论】:

  • 你真的需要使用代理吗?您不能在应用程序中实现过滤器以跳过 Prometheus 端点中的身份验证吗?
  • @SergioSantiago 好主意,但应用程序本身没有身份验证。 API 密钥保护在 Google Cloud 网络中的某处实现。

标签: nginx prometheus


【解决方案1】:

我在docker-compose 中为您提供了一个完整的设置,其中包含一个应用程序、一个转发代理和 prometheus。很长,所以我把它放在解释之后。请注意,就像您的解决方案一样,它不适用于host.docker.internal,因为似乎NGINX 在解析主机时不使用/etc/hostshttps://github.com/NginxProxyManager/nginx-proxy-manager/issues/259#issuecomment-1125197753。所有其他主机都应该可以正常工作,如果需要,您可以使用主机的 IP 地址而不是 host.docker.internal

您可以通过将内容保存到 docker-compose.yml 并从同一目录运行 docker-compose up 来运行它。大约 10 秒后,您应该在日志中看到请求如何通过代理到达应用程序,并且应用程序将向您显示它获得的标头。然后,您可以进入 Prometheus UI (localhost:9090) 并查询指标 the_answer_is 以进一步检查一切是否到位。

代理的工作原理如下:

  • target 参数(例如GET /?target=example.com)是实际指标所在的主机或 IP,这是唯一的强制参数;
  • 如果有scheme参数,则使用它作为协议,默认-“http”;
  • 如果有host参数,则将其用作Host标头,默认-target参数的值;
  • 如果有port参数,则作为TCP端口使用,默认为""(由方案决定);
  • 如果有secret_token参数,则注入X-Custom-Header,默认为"";

我建议使用curl 测试代理,如下所示:

curl 'localhost/something/here?target=someapp&port=8000&secret_token=foo

现在转到docker-compose.yml

version: '3'
networks:
  app: # a network where the app is
  no_app: # a network where there is no app so that prometheus can't reach it directly

services:
  # A basic http server that exposes one metric and prints some headers along the way
  someapp:
   image: tiangolo/uwsgi-nginx-flask:python3.8-alpine
   entrypoint: ["/usr/local/bin/python", "-c"]
   networks:
    - app
   ports:
     - 8000:8000
   command:
   - |-
     import json
     from flask import Flask, request
     app = Flask(__name__)

     import logging
     log = logging.getLogger('werkzeug')

     @app.route('/', defaults={'path': ''})
     @app.route('/<path:path>')
     def print_request(path):
         log.info(f"{'-'*78}
{str(request.headers).strip()}")
         if request.path == "/something/here":
           return "the_answer_is 42
"
         else:
           return "OK
"

     app.run("0.0.0.0", port=8000)

  # A forward proxy for Prometheus
  forward_proxy:
    image: nginx
    networks:
      - app
      - no_app
    ports:
      - 80:80
    entrypoint: ["/bin/bash", "-c"]
    environment:
      # Note that single "$" is considered by docker-compose as its variable, 
      # double "$$" is just an escape here
      config: |
        # Default scheme
        map $$arg_scheme $$target_scheme {
          ~.+     $$arg_scheme;
          default http;
        }
        # Default host (header) from target
        map $$arg_host $$target_host {
          ~.+     $$arg_host;
          default $$arg_target;
        }
        # This is to add ":" between target ip or host and port
        map $$arg_port $$has_port {
          ~.+     ":";
          default "";
        }

        # use docker internal DNS to resolve "someapp"
        resolver 127.0.0.11 ipv6=off;

        server {
          listen 80;

          location / {
            proxy_set_header Host $$target_host;
            proxy_set_header X-Custom-Header $$arg_secret_token;
            proxy_pass $$target_scheme://$$arg_target$$has_port$$arg_port$$request_uri;
          }
        }

    command:
    - |-
      set -euo pipefail
      echo -e "$$config" >/etc/nginx/conf.d/default.conf
      echo -e "==== NGINX Config ====
$$(cat /etc/nginx/conf.d/default.conf)"
      nginx -g 'daemon off;'

  prometheus:
    image: prom/prometheus:v2.29.2
    entrypoint: ["/bin/sh", "-c"]
    ports:
      - 9090:9090
    command:
    - |
      echo -e "$$config" > /etc/prometheus/prometheus.yml
      echo -e "==== Prometheus Config ====
$$(cat /etc/prometheus/prometheus.yml)"
      /bin/prometheus --config.file=/etc/prometheus/prometheus.yml 
                      --storage.tsdb.path=/prometheus 
                      --web.console.libraries=/usr/share/prometheus/console_libraries 
                      --web.console.templates=/usr/share/prometheus/consoles
    networks:
      - no_app
    environment:
      config: |
        scrape_configs:
        - job_name: 'somejob'
          scrape_interval: 10s
          metrics_path: '/something/here'

          # set params for the proxy ($$arg_NAME)
          params:
            port: ["8000"]
            secret_token: ["foo"]  # beware, this will be visible in Prometheus UI under "config" section

          static_configs:
          - targets:
            - 'someapp'
          
          # Here we replace actual target with the address and port of our forward_proxy
          # If you're familiar with it, this is exactly the same as for blackbox exporter
          relabel_configs:
          - source_labels: [__address__]
            target_label: __param_target
          - source_labels: [__param_target]
            target_label: instance
          - target_label: __address__
            replacement: forward_proxy:80  # The forward proxy address and port

【讨论】:

  • 谢谢,但我仍然收到身份验证错误。我不明白生成的最终文件是什么。你能把你的答案分成多个文件吗?删除 Python 服务器也会有所帮助 - 这不是必需的。
  • @aSemy 有一个命令会显示文件,当容器启动时在单独的终端中运行它:docker-compose exec forward_proxy cat /etc/nginx/conf.d/default.confdocker-compose exec prometheus cat /etc/prometheus/prometheus.yml。至于该应用程序,我相信它可能对来到这里有同样问题的其他人有用。
  • 想一想,为什么不在启动时打印配置?固定的。
【解决方案2】:

我已经设法使用与 Prometheus 实例在同一 Docker 网络上运行的 nginx 代理来实现这一点。

.
├── config/
│   ├── nginx.conf
│   └── prometheus.yml
└── docker-compose.yml

Prometheus 配置为从 nginx 抓取 Prometheus 指标。

网址

我有 3 个环境,“本地”、“开发”和“产品”。

Prometheus 指标可在以下 URL 获得。请注意,dev 和 prod 需要 HTTPS 和 API 密钥,但 local 不需要。

  • 本地 - http://localhost:8080/metrics/prometheus
  • 开发-https://dev.my-app.website.com/metrics/prometheus
  • 产品-http://prod.my-app.website.com/metrics/prometheus

nginx 配置

nginx 服务器已配置为根据端口将请求转发到每个环境。

  • :9191 - 本地
  • :9192 - 开发
  • :9193 - 产品

我已经在每个 nginx server { } 块('localhost' 除外)中手动定义了每个环境的 URL,因为 nginx 或 Prometheus 似乎不喜欢解析正确的 URL。这是一个谜。

http {

  resolver 127.0.0.11 ipv6=off; # use the docker DNS, to resolve host.docker.internal

  map $request $target_port {
    ~^GET http://.*:([^/]*)/ "$1";
  }

  # local
  server {
    listen 9191;

    location / {
      # no need for API key on local env
      # proxy_set_header x-api-key ...;
      proxy_set_header Host localhost;
      proxy_pass http://$host:$target_port$request_uri;
    }
  }

  # dev
  server {
    listen 9192;

    location / {
      proxy_set_header x-api-key DEV_API_KEY_123_ABC;
      proxy_set_header Host dev.my-app.website.com;
      proxy_pass https://dev.my-app.website.com:443$request_uri;
    }
  }

  # prod
  server {
    listen 9193;

    location / {
      proxy_set_header x-api-key PROD_API_KEY_999_XYZ;
      proxy_set_header Host prod.my-app.website.com;
      proxy_pass https://prod.my-app.website.com:443$request_uri;
    }
  }
}
events {
}

普罗米修斯配置

Prometheus 配置为使用 nginx 容器作为代理 URL。

因为 nginx 和 Prometheus 运行在同一个 Docker 网络中,所以我可以通过容器名称来指定 nginx。

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:

  - job_name: "my-backend-local"
    proxy_url: "http://nginx:9191"
    metrics_path: "/monitor/prometheus"
    scrape_interval: 2s
    static_configs:
      - targets: [ "host.docker.internal:6060" ]
        labels:
          application: "my-backend"
          env: "local"

  - job_name: "my-backend-dev"
    proxy_url: "http://nginx:9192"
    metrics_path: "/monitor/prometheus"
    scrape_interval: 2s
    static_configs:
      - targets: [ "dev.my-app.website.com" ]
        labels:
          application: "my-backend"
          env: "dev"

  - job_name: "my-backend-prod"
    proxy_url: "http://nginx:9193"
    metrics_path: "/monitor/prometheus"
    scrape_interval: 2s
    static_configs:
      - targets: [ "prod.my-app.website.com" ]
        labels:
          application: "my-backend"
          env: "prod"

Docker 撰写配置

最后,将 Prometheus 和 nginx Docker 实例配置为读取 ./config/prometheus.yml./config/nginx.conf 文件。

version: "3.9"

services:

  prometheus:
    image: prom/prometheus:v2.39.1
    container_name: prometheus
    volumes:
      - "./config/prometheus.yml:/etc/prometheus/prometheus.yml"
      - "./data/prometheus:/prometheus"
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
      - "--storage.tsdb.path=/prometheus"
      - "--web.console.libraries=/etc/prometheus/console_libraries"
      - "--web.console.templates=/etc/prometheus/consoles"
      - "--web.enable-lifecycle"
    ports:
      - "9090:9090"

  backend-proxy:
    image: nginx
    container_name: nginx
    restart: unless-stopped
    volumes:
      - "./config/nginx.conf:/etc/nginx/nginx.conf:ro"

【讨论】:

    猜你喜欢
    • 2021-05-07
    • 1970-01-01
    • 1970-01-01
    • 2021-01-13
    • 1970-01-01
    • 1970-01-01
    • 2020-12-30
    • 1970-01-01
    • 2011-04-27
    相关资源
    最近更新 更多