基于OpenTelemetry与ClickHouse构建AI大模型服务监控系统实战 在业务中大规模集成 AI 大模型时你是否遇到过这样的困境用户反馈响应慢但后台日志却一切正常月度账单上的 Token 消耗远超预期却找不到具体是哪个接口或哪个用户消耗的传统的应用性能监控APM工具对 AI 调用的延迟、Token 消耗等关键指标往往无能为力导致成本失控和体验下降成为“黑盒”。本文将为你拆解一套完整的 AI 服务监控解决方案。我们将聚焦于两个核心可观测性指标响应延迟与Token 消耗并使用OpenTelemetry进行指标采集最终将数据存储和展示在ClickHouse中。通过本篇实战指南你将能搭建一个从数据采集、存储到可视化分析的完整监控链路无论是评估模型性能、优化提示词工程还是进行精细化的成本核算都能做到心中有数。1. 核心概念为什么需要专门的 AI 监控在深入技术实现之前我们首先要理解监控 AI 服务与传统 Web 服务的本质区别。1.1 AI 服务的独特挑战传统的 Web 服务监控主要关注请求 QPS、错误率、CPU/内存使用率、数据库查询耗时等。而一次 AI 模型调用例如调用 OpenAI GPT-4 或本地部署的 Llama其核心成本与性能体现在Token 消耗这是 AI 服务最直接的成本驱动因素。无论是输入Prompt还是输出Completion都按 Token 数量计费。监控每个请求的输入/输出 Token 数是进行成本分摊、识别异常消耗如提示词泄露导致长输出、优化提示词效率的基础。响应延迟AI 模型的推理时间通常远长于简单的数据库查询。延迟包括网络传输、模型加载、推理计算等多个环节。监控 P50、P95、P99 分位的延迟对于保障用户体验、设定合理的超时时间、评估不同模型或硬件的性能至关重要。模型与参数同一个服务可能调用不同的模型如gpt-3.5-turbo与gpt-4或使用不同的参数如temperature,max_tokens。监控时需要区分这些维度才能进行有效的对比分析。1.2 监控架构概览我们的目标是构建一个轻量、高效、可扩展的监控系统。整体架构如下[你的AI应用] --(发射指标)-- [OpenTelemetry Collector] --(写入)-- [ClickHouse] --(查询)-- [Grafana]数据采集层 (OpenTelemetry SDK)集成到你的 AI 应用代码中在每次调用 AI 模型时记录耗时、Token 数等指标。收集与转发层 (OpenTelemetry Collector)接收来自多个应用实例的指标数据进行聚合、批处理并导出到指定的存储后端。数据存储层 (ClickHouse)一个高性能的列式数据库特别适合存储和快速查询时序指标数据。可视化层 (Grafana)从 ClickHouse 中读取数据绘制丰富的监控仪表盘。接下来我们将从环境准备开始一步步实现这个架构。2. 环境准备与版本说明在开始动手之前请确保你的开发环境满足以下要求。本文示例将使用 Python 作为 AI 应用的语言但 OpenTelemetry 的概念是语言无关的。2.1 基础软件环境操作系统Linux (Ubuntu 20.04/22.04)、macOS 或 WSL2。大部分命令在 Linux 环境下进行。Docker Docker Compose我们将使用容器化方式快速部署 OpenTelemetry Collector 和 ClickHouse。请确保已安装。# 检查安装 docker --version docker-compose --versionPython版本 3.8 及以上。我们将使用openai库模拟 AI 调用。python3 --version pip3 --version2.2 核心组件版本为了确保兼容性以下是本文演示所用的主要组件版本。你的实际环境可以略有不同但建议保持大版本一致。组件版本说明OpenTelemetry Python SDK1.24.0用于在应用中埋点OpenTelemetry Collector0.104.0(Docker 镜像)指标收集与导出ClickHouse24.8.2-alpine(Docker 镜像)指标存储Grafana11.2.0(Docker 镜像)数据可视化OpenAI Python Client1.30.1模拟 AI 调用重要提示OpenTelemetry 生态系统更新较快配置方式可能随版本变化。本文的代码和配置基于上述版本测试通过如果你的版本不同请参考官方文档进行调整。3. OpenTelemetry 与 ClickHouse 基础3.1 OpenTelemetry 简介OpenTelemetry (简称 OTel) 是一个云原生计算基金会 (CNCF) 下的项目旨在提供一套统一的 API、SDK 和工具用于采集、生成遥测数据包括指标、链路追踪和日志。它的核心优势在于标准化和供应商中立。对于 AI 监控场景我们主要使用其Metrics SDK。一个Meter工具可以创建各种指标例如Counter单调递增的累计值适合记录总请求数、总 Token 消耗量。Histogram记录可聚合的数值分布完美契合测量请求延迟、单次请求的 Token 数。3.2 ClickHouse 为何适合监控数据ClickHouse 是一个开源的列式 OLAP 数据库以其惊人的查询速度著称。对于监控场景它有如下优势高性能聚合对时间序列数据的GROUP BY、SUM、AVG等聚合查询极快。高压缩比列式存储和高效压缩算法大幅降低存储成本。TTL (生存时间)可以轻松为表设置数据自动过期策略符合监控数据“近期热、远期冷”的特点。丰富的表引擎MergeTree系列引擎特别是SummingMergeTree、AggregatingMergeTree是为聚合数据量身定做的。我们将使用 OpenTelemetry Collector 的clickhouseexporter将指标直接写入 ClickHouse 的特定表中。4. 搭建监控基础设施ClickHouse 与 Collector我们首先使用 Docker Compose 搭建数据存储和收集层。4.1 编写 Docker Compose 文件创建一个项目目录ai-monitor-demo并在其中创建docker-compose.yml文件。# docker-compose.yml version: 3.8 services: clickhouse: image: clickhouse/clickhouse-server:24.8.2-alpine container_name: ai-monitor-clickhouse ports: - 8123:8123 # HTTP API 端口 - 9000:9000 # 原生TCP客户端端口 volumes: - ./clickhouse/data:/var/lib/clickhouse - ./clickhouse/config.xml:/etc/clickhouse-server/config.xml - ./clickhouse/users.xml:/etc/clickhouse-server/users.xml environment: - CLICKHOUSE_DBotel - CLICKHOUSE_USERadmin - CLICKHOUSE_PASSWORDadmin123 ulimits: nproc: 65535 nofile: soft: 262144 hard: 262144 networks: - otel-network otel-collector: image: otel/opentelemetry-collector-contrib:0.104.0 container_name: ai-monitor-otel-collector command: [--config/etc/otel-collector-config.yaml] volumes: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml ports: - 4317:4317 # OTLP gRPC 接收端口 - 4318:4318 # OTLP HTTP 接收端口 - 8889:8889 # 健康检查/指标端口 - 13133:13133 # 健康检查扩展端口 depends_on: - clickhouse networks: - otel-network grafana: image: grafana/grafana:11.2.0 container_name: ai-monitor-grafana ports: - 3000:3000 environment: - GF_SECURITY_ADMIN_PASSWORDadmin123 volumes: - ./grafana/provisioning:/etc/grafana/provisioning - ./grafana/dashboards:/var/lib/grafana/dashboards depends_on: - clickhouse networks: - otel-network networks: otel-network: driver: bridge4.2 配置 OpenTelemetry Collector创建otel-collector-config.yaml文件。这个配置定义了 Collector 如何接收指标通过 OTLP以及如何将其导出到 ClickHouse。# otel-collector-config.yaml receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 exporters: debug: verbosity: detailed clickhouse: endpoint: tcp://clickhouse:9000?databaseotel username: admin password: admin123 ttl: 720h # 数据保留30天 timeout: 5s logs_table_name: otel_logs traces_table_name: otel_traces metrics_table_name: otel_metrics # 针对指标表的额外配置 metrics: # 使用 TTL 并设置存储策略 ttl: 720h # 定义表结构映射 OpenTelemetry 指标到 ClickHouse 列 table_columns: - name: ResourceAttributes type: Map(LowCardinality(String), String) - name: ScopeName type: LowCardinality(String) - name: ScopeVersion type: LowCardinality(String) - name: MetricName type: LowCardinality(String) - name: MetricDescription type: String - name: MetricUnit type: LowCardinality(String) - name: Attributes type: Map(LowCardinality(String), String) - name: StartTimeUnix type: UInt64 - name: TimeUnix type: UInt64 - name: Value type: Float64 - name: Flags type: UInt32 - name: HistogramCounts type: Array(UInt64) - name: HistogramBounds type: Array(Float64) - name: Exemplars type: String processors: batch: timeout: 5s send_batch_size: 1000 extensions: health_check: endpoint: 0.0.0.0:13133 pprof: endpoint: 0.0.0.0:1777 service: extensions: [pprof, health_check] pipelines: metrics: receivers: [otlp] processors: [batch] exporters: [debug, clickhouse] # debug 用于调试生产可移除4.3 启动基础设施在项目根目录下运行docker-compose up -d等待所有容器启动成功。你可以使用docker-compose logs -f查看日志。验证服务ClickHouse访问http://localhost:8123/play使用用户名admin和密码admin123登录。执行SHOW DATABASES;应能看到otel数据库。Grafana访问http://localhost:3000使用用户名admin和密码admin123登录。Collector访问http://localhost:13133应返回{status:Server available}。至此监控的后端基础设施已就绪。5. 在 Python AI 应用中集成监控现在我们编写一个简单的 Python 应用模拟调用 AI 模型并使用 OpenTelemetry 发送指标。5.1 创建 Python 虚拟环境与依赖在项目根目录外创建一个新的应用目录ai-app。mkdir ai-app cd ai-app python3 -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate安装必要的 Python 包pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http opentelemetry-metrics pip install openai # 用于模拟AI调用5.2 编写核心监控与 AI 调用代码创建文件app_with_monitoring.py# app_with_monitoring.py import time import random from opentelemetry import metrics from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter from opentelemetry.sdk.resources import Resource # 1. 定义资源标识你的服务 resource Resource.create({ service.name: ai-text-generation-service, service.version: 1.0.0, deployment.environment: demo, }) # 2. 配置指标导出到 OTLP Collector (HTTP) metric_exporter OTLPMetricExporter( endpointhttp://localhost:4318/v1/metrics, # Collector 的 OTLP HTTP 端口 # 可选添加认证头等 ) metric_reader PeriodicExportingMetricReader(exportermetric_exporter, export_interval_millis5000) # 每5秒导出一次 # 3. 设置全局的 MeterProvider provider MeterProvider( resourceresource, metric_readers[metric_reader], ) metrics.set_meter_provider(provider) # 4. 创建 Meter meter metrics.get_meter(__name__) # 5. 创建我们需要的指标 # Counter: 记录总请求数和总Token消耗 request_counter meter.create_counter( nameai.requests.total, descriptionTotal number of AI model requests, unit1, ) input_token_counter meter.create_counter( nameai.tokens.input.total, descriptionTotal number of input tokens consumed, unit1, ) output_token_counter meter.create_counter( nameai.tokens.output.total, descriptionTotal number of output tokens consumed, unit1, ) # Histogram: 记录请求延迟和每次请求的Token数分布 request_duration_histogram meter.create_histogram( nameai.request.duration, descriptionDuration of AI model requests, unitms, ) request_token_histogram meter.create_histogram( nameai.request.tokens.total, descriptionTotal tokens (inputoutput) per request, unit1, ) def simulate_ai_call(prompt: str, model: str gpt-3.5-turbo): 模拟调用 AI 模型。 在实际项目中这里应替换为真实的 OpenAI、Azure OpenAI 或本地模型的调用。 # 模拟网络和计算延迟 (50ms ~ 2000ms) latency_ms random.randint(50, 2000) time.sleep(latency_ms / 1000.0) # 模拟 Token 计数简单假设每个字符约等于 0.25 个 token input_tokens int(len(prompt) * 0.25) random.randint(1, 10) # 模拟生成长度不等的回复 output_length random.randint(20, 200) output_tokens int(output_length * 0.25) random.randint(1, 20) # 模拟小概率失败 if random.random() 0.05: # 5% 失败率 raise Exception(Simulated AI API failure) return { content: Simulated AI response with length str(output_length), input_tokens: input_tokens, output_tokens: output_tokens, latency_ms: latency_ms, model: model } def process_user_request(user_id: str, prompt: str, model: str gpt-3.5-turbo): 处理用户请求并记录监控指标。 start_time time.time() attributes { user.id: user_id, ai.model: model, status.code: 200 # 默认成功 } try: # 调用 AI response simulate_ai_call(prompt, model) duration_ms (time.time() - start_time) * 1000 # 记录指标 request_counter.add(1, attributes) input_token_counter.add(response[input_tokens], attributes) output_token_counter.add(response[output_tokens], attributes) request_duration_histogram.record(duration_ms, attributes) total_tokens response[input_tokens] response[output_tokens] request_token_histogram.record(total_tokens, attributes) print(fRequest from {user_id} succeeded. Tokens: {total_tokens}, Latency: {duration_ms:.2f}ms) return response except Exception as e: duration_ms (time.time() - start_time) * 1000 # 记录失败的请求状态码标记为错误 error_attributes attributes.copy() error_attributes[status.code] 500 request_counter.add(1, error_attributes) request_duration_histogram.record(duration_ms, error_attributes) print(fRequest from {user_id} failed: {e}) return None if __name__ __main__: print(Starting AI service with OpenTelemetry monitoring...) # 模拟连续处理一些请求 users [user_001, user_002, user_003, user_004] models [gpt-3.5-turbo, gpt-4] prompts [ Explain quantum computing in simple terms., Write a Python function to calculate Fibonacci sequence., What are the benefits of renewable energy?, Summarize the history of the Internet. ] for i in range(20): # 模拟20个请求 user random.choice(users) model random.choice(models) prompt random.choice(prompts) process_user_request(user, prompt, model) time.sleep(random.uniform(0.5, 2.0)) # 模拟随机请求间隔 print(Simulation finished. Metrics are being exported...) # 等待指标导出器完成最后的推送 time.sleep(10) print(Done.)5.3 运行应用并查看数据确保docker-compose服务仍在运行。在ai-app目录下运行 Python 脚本python app_with_monitoring.py观察控制台输出会看到模拟的请求成功与失败信息。登录 ClickHouse (http://localhost:8123/play)查询是否已收到指标数据USE otel; SELECT DISTINCT MetricName FROM otel_metrics ORDER BY MetricName;你应该能看到ai.requests.total,ai.request.duration等我们定义的指标名。查询具体的指标数据SELECT toDateTime(TimeUnix/1000000000) as time, MetricName, Attributes[user.id] as user, Attributes[ai.model] as model, Value, HistogramBounds, HistogramCounts FROM otel_metrics WHERE MetricName ai.request.duration ORDER BY time DESC LIMIT 5;此查询会显示最近几条请求延迟的直方图数据。6. 在 Grafana 中可视化监控数据数据已进入 ClickHouse现在我们在 Grafana 中创建仪表盘。6.1 配置 ClickHouse 数据源登录 Grafana (http://localhost:3000)默认账号admin/admin123。点击左侧齿轮图标Configuration-Data sources。点击Add data source搜索并选择ClickHouse。配置连接Name:ClickHouse-OTelHost:clickhouse:8123注意因为 Grafana 和 ClickHouse 在同一 Docker 网络otel-network下所以可以用服务名Database:otelUser:adminPassword:admin123Protocol:HTTP点击Save test应显示 “Data source is working”。6.2 创建监控仪表盘我们可以创建几个关键面板面板 1请求速率与错误率查询(请求总量)SELECT $timeSeries as t, count(*) as value FROM $table WHERE $timeFilter AND MetricName ai.requests.total GROUP BY t ORDER BY t查询(错误请求量属性status.code500)SELECT $timeSeries as t, count(*) as value FROM $table WHERE $timeFilter AND MetricName ai.requests.total AND Attributes[status.code] 500 GROUP BY t ORDER BY t可视化使用Stat或Time series图表。可以计算错误率错误数 / 总数 * 100%。面板 2平均响应延迟与 P99 延迟查询(平均延迟需要利用直方图数据计算这里简化查询平均值)SELECT $timeSeries as t, avg(Value) as value FROM $table WHERE $timeFilter AND MetricName ai.request.duration AND Attributes[status.code] 200 -- 只看成功的请求 GROUP BY t ORDER BY t注意更精确的百分位数计算需要在查询时展开HistogramBounds和HistogramCounts列或使用 ClickHouse 的quantile函数对Value进行估算。生产环境建议对直方图数据进行预聚合。面板 3Token 消耗趋势按用户/模型查询(总输入 Token)SELECT $timeSeries as t, sum(Value) as value FROM $table WHERE $timeFilter AND MetricName ai.tokens.input.total GROUP BY t ORDER BY t查询(按模型分组)SELECT $timeSeries as t, Attributes[ai.model] as metric, sum(Value) as value FROM $table WHERE $timeFilter AND MetricName ai.tokens.input.total GROUP BY t, metric ORDER BY t, metric可视化使用Time series图表并开启Stack模式可以清晰看到不同模型的 Token 消耗占比。面板 4单次请求 Token 数量分布查询SELECT Value as tokens_per_request FROM $table WHERE $timeFilter AND MetricName ai.request.tokens.total AND Attributes[status.code] 200可视化使用Histogram图表可以直观看到大部分请求消耗的 Token 范围有助于识别异常值例如提示词泄露导致的长文本输出。将这些面板组合在一个仪表盘中你就得到了一个专属的 AI 服务监控看板可以实时观察服务的健康度、性能与成本。7. 常见问题与排查思路在搭建和使用过程中你可能会遇到以下问题问题现象可能原因排查思路Python 应用启动报错提示opentelemetry-exporter-otlp相关错误依赖版本不兼容或未安装1. 检查pip list确认包已安装。2. 查看 OpenTelemetry Python SDK 和 Exporter 的版本兼容性尽量使用较新且版本匹配的包。应用运行后ClickHouse 中查不到数据Collector 配置错误或网络不通1. 检查 Collector 容器日志docker-compose logs otel-collector。2. 确认 Python 应用中endpoint指向正确的 Collector 地址和端口 (http://localhost:4318)。3. 在 Collector 配置中启用debugexporter查看是否收到数据。Grafana 中查询数据报错或为空数据源配置错误或 SQL 查询语法问题1. 在 Grafana 的Explore页面使用配置好的 ClickHouse 数据源执行简单查询如SELECT 1测试连接。2. 检查 SQL 中的表名 (otel_metrics)、字段名是否与 ClickHouse 中实际创建的表一致。3. 确认查询的时间范围 ($timeFilter) 内有数据。监控数据延迟很高Collector 的batch处理器配置或 MetricReader 导出间隔过长1. 检查otel-collector-config.yaml中batch处理器的timeout建议 5-10s。2. 检查 Python 代码中PeriodicExportingMetricReader的export_interval_millis建议 5000-10000 ms。3. 对于需要近实时监控的场景可以适当缩短这些间隔但会增加 Collector 负载。ClickHouse 磁盘空间增长过快数据没有设置 TTL 或监控指标过于频繁1. 确认 Collector 配置中ttl: 720h30天已生效。2. 可以在 ClickHouse 中为otel_metrics表额外设置 TTLALTER TABLE otel_metrics MODIFY TTL TimeUnix INTERVAL 30 DAY。3. 评估指标发射频率非核心指标可以降低频率。8. 最佳实践与工程建议将监控系统投入生产环境时需要考虑更多工程细节。8.1 监控指标设计遵循命名规范使用点分隔的命名方式如ai.request.duration、business.order.value。添加前缀如ai.避免冲突。精心设计属性 (Attributes)属性是进行数据下钻 (drill-down) 分析的维度。像user.id、ai.model、prompt.type、status.code都是非常有价值的属性。但注意高基数字段如直接使用用户ID可能导致查询变慢可以考虑使用哈希值或分组。区分指标类型Counter用于只增不减的值请求数、Token总数。Histogram用于记录分布延迟、包大小、单个请求Token数。Gauge用于可增可减的瞬时值并发请求数、内存使用量。8.2 性能与成本优化采样与聚合对于极高并发的服务不是每个请求都需要记录完整的直方图。可以在 SDK 端或 Collector 端配置采样率或使用AggregatingMeterProvider在客户端进行预聚合。ClickHouse 表引擎优化生产环境建议使用AggregatingMergeTree或SummingMergeTree引擎来存储预聚合后的数据而不是原始的指标数据这能极大提升查询性能和降低存储成本。这通常需要在 Collector 或一个独立的聚合服务中完成。控制数据粒度根据需求决定数据存储的粒度。例如原始数据保留7天按小时聚合的数据保留30天按天聚合的数据保留1年。8.3 生产环境部署Collector 高可用生产环境至少部署两个 Collector 实例前端通过负载均衡器如 Nginx分发流量避免单点故障。安全为 ClickHouse 和 Grafana 配置强密码并考虑网络隔离如将 ClickHouse 置于内网不暴露8123端口到公网。OTLP 端点可以考虑启用 TLS 加密传输。资源限制为 Docker 容器或 Pod 设置合理的 CPU 和内存限制防止某个组件异常拖垮整个主机。8.4 告警集成监控的最终目的是发现问题并及时响应。在 Grafana 中可以基于我们创建的仪表盘设置告警规则延迟告警当ai.request.duration的 P95 值超过 5 秒时触发。错误率告警当错误请求率 (status.code500的请求占比) 连续 5 分钟超过 1% 时触发。Token 消耗异常告警当某个用户的每小时 Token 消耗量突增 10 倍时触发可能提示提示词被恶意利用或程序漏洞。通过将 Grafana 告警连接到 Slack、钉钉、PagerDuty 等通知渠道团队可以在第一时间获知服务异常。从环境搭建、应用埋点、数据存储到可视化告警我们完成了一个完整的 AI 服务可观测性闭环。这套方案的核心优势在于标准化和可扩展性——OpenTelemetry 让你未来可以无缝切换监控后端ClickHouse 的高性能则确保了即使面对海量监控数据查询也能快速响应。