You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何聚合Prometheus指标?多Docker容器指标合并方案问询

解决Prometheus多端点指标聚合问题的几种方案

针对你遇到的场景——需要将多个Docker容器的Prometheus指标聚合到单一可抓取端点,同时处理重复HELP文本这类格式问题,这里有几个实用的方案:

1. 使用现成的轻量Docker镜像:prometheus-merge-exporter

这是专门为这类场景设计的工具,会自动处理重复的HELP和TYPE行,确保输出完全符合Prometheus的格式规范,无需自己写复杂的处理逻辑。

用法示例:

直接用Docker启动这个容器,通过环境变量指定需要聚合的多个指标端点即可:

docker run -d \
  --name prometheus-merge \
  -p 9201:9201 \
  -e MERGE_TARGETS=http://container1:9100/metrics,http://container2:9101/metrics,http://container3:9102/metrics \
  quay.io/prometheus-community/prometheus-merge-exporter:latest

启动后,Prometheus就可以抓取http://<your-host>:9201/metrics这个端点,获取所有聚合后的指标。工具会自动为每个指标只保留第一次出现的HELP和TYPE定义,同时完整保留所有合法的样本数据。

2. 自定义Python脚本(灵活可控)

如果需要更定制化的逻辑——比如根据标签过滤指标、自定义去重规则,写个简单的Python脚本是个不错的选择。以下是一个基础可运行的示例:

脚本代码(aggregator.py):

from flask import Flask
import requests

app = Flask(__name__)

# 定义需要聚合的指标端点列表
TARGETS = [
    "http://container1:9100/metrics",
    "http://container2:9101/metrics",
    "http://container3:9102/metrics"
]

def fetch_and_merge_metrics():
    help_lines = {}
    type_lines = {}
    sample_lines = []

    for target in TARGETS:
        try:
            response = requests.get(target, timeout=5)
            response.raise_for_status()
            lines = response.text.splitlines()
            for line in lines:
                line = line.strip()
                if not line or line.startswith("#"):
                    # 处理注释行,仅保留每个指标的第一条HELP和TYPE
                    if line.startswith("# HELP"):
                        metric_name = line.split(maxsplit=2)[1]
                        if metric_name not in help_lines:
                            help_lines[metric_name] = line
                    elif line.startswith("# TYPE"):
                        metric_name = line.split(maxsplit=2)[1]
                        if metric_name not in type_lines:
                            type_lines[metric_name] = line
                    continue
                # 样本行直接加入(相同指标不同标签是合法的)
                sample_lines.append(line)
        except Exception as e:
            print(f"Failed to fetch metrics from {target}: {str(e)}")
    
    # 拼接最终的指标文本,按规范顺序排列
    merged = []
    for metric in sorted(help_lines.keys()):
        merged.append(help_lines[metric])
    for metric in sorted(type_lines.keys()):
        merged.append(type_lines[metric])
    merged.extend(sample_lines)
    return "\n".join(merged)

@app.route("/metrics")
def metrics():
    return fetch_and_merge_metrics(), 200, {"Content-Type": "text/plain; version=0.0.4"}

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=9201)

打包成Docker镜像:

创建Dockerfile:

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir flask requests

COPY aggregator.py .

EXPOSE 9201

CMD ["python", "aggregator.py"]

requirements.txt内容:

flask==2.3.3
requests==2.31.0

然后构建并运行:

docker build -t prometheus-metrics-aggregator .
docker run -d --name aggregator -p 9201:9201 prometheus-metrics-aggregator

3. Nginx + Lua脚本(适合已有Nginx部署的场景)

如果你的环境已经在用Nginx,可以通过Lua脚本实现聚合逻辑,无需额外部署新服务。比如在Nginx配置中添加一个专门的location:

简化的Nginx配置示例(基于OpenResty):

http {
    server {
        listen 9201;

        location /metrics {
            default_type text/plain;
            content_by_lua_block {
                local http = require "resty.http"
                local targets = {
                    "http://container1:9100/metrics",
                    "http://container2:9101/metrics",
                    "http://container3:9102/metrics"
                }

                local help = {}
                local type = {}
                local samples = {}

                for _, target in ipairs(targets) do
                    local httpc = http.new()
                    local res, err = httpc:request_uri(target, {method = "GET", timeout = 5000})
                    if res and res.status == 200 then
                        local lines = string.split(res.body, "\n")
                        for _, line in ipairs(lines) do
                            line = string.trim(line)
                            if line ~= "" then
                                if string.sub(line, 1, 6) == "# HELP" then
                                    local metric = string.match(line, "# HELP (%S+)")
                                    if metric and not help[metric] then
                                        help[metric] = line
                                    end
                                elseif string.sub(line, 1, 5) == "# TYPE" then
                                    local metric = string.match(line, "# TYPE (%S+)")
                                    if metric and not type[metric] then
                                        type[metric] = line
                                    end
                                else
                                    table.insert(samples, line)
                                end
                            end
                        end
                    end
                    httpc:close()
                }

                # 按规范输出聚合后的指标
                for _, line in pairs(help) do
                    ngx.say(line)
                end
                for _, line in pairs(type) do
                    ngx.say(line)
                end
                for _, line in pairs(samples) do
                    ngx.say(line)
                end
            }
        }
    }
}

注:这个配置需要Nginx安装ngx_http_lua_module模块,使用OpenResty镜像可以直接支持该模块。

关键注意事项

  • 端点可达性:确保聚合工具/脚本能够访问到所有Docker容器的指标端点(比如将它们加入同一个Docker网络)。
  • 超时与错误处理:添加适当的超时设置和错误捕获,避免某个端点故障导致整个聚合服务不可用。
  • 标签冲突:如果不同容器的相同指标有完全一致的标签(比如没有instance标签区分),Prometheus抓取时会视为重复样本,可能会覆盖或报警,建议确保每个容器的指标带有唯一标识标签。

内容的提问来源于stack exchange,提问作者Andy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:42:32