Windmill 可观测性实战:基于 Tempo、Grafana、Prometheus 与 Loki 的 OpenTelemetry 链路追踪与日志监控
2026/9/14 6:41:11 网站建设 项目流程

Windmill 可观测性实战:基于 Tempo、Grafana、Prometheus 与 Loki 的 OpenTelemetry 链路追踪与日志监控

【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill

导读

本文以仓库中 examples/deploy/otel-tracing-grafana 目录下的官方部署示例为主线,完整讲解如何在自托管 Windmill 环境中一键搭建以Grafana Tempo为核心的分布式追踪、以Prometheus为存储的指标监控、以Loki为后端的日志聚合三合一可观测性方案。读完本文你将掌握:观测栈各容器的编排与配置要点、如何通过 Windmill 的 "Instances Settings → OTEL/Prom" 页面把追踪与日志推送到 OpenTelemetry Collector、如何利用 Windmill 注入的job_idroot_jobflow_step_id等标签在 Tempo 中精准检索特定作业或工作流,以及如何借助 Tempo 的 metrics generator 把 span 转化为可长期对比的性能指标。

为什么选择 Tempo:OTLP 兼容与 Windmill 的天然契合

Tempo 是 Grafana 开源的一款分布式追踪系统,专为监控和调试微服务而设计:它能够跨服务追踪请求、分析延迟、定位瓶颈并诊断故障。其典型使用场景包括排查生产问题、监控性能、可视化服务依赖关系以及优化系统可靠性。

Tempo 之所以能与 Windmill 无缝对接,关键在于它原生支持OpenTelemetry Protocol(OTLP)——Windmill 的追踪与日志输出正是基于 OpenTelemetry 生态(这一点在 backend/windmill-worker/src/worker.rs 的tracing::span!宏以及 backend/windmill-common/src/global_settings.rs 维护的一整组OTEL_EXPORTER_OTLP_*环境变量中可以得到印证)。因此,不需要任何自定义 SDK 或协议转换,Windmill 产生的 span 可以直接被 Tempo 接收、存储与查询。

整体架构:一次docker-compose up拉起完整观测栈

该示例的核心是一份完整的 docker-compose.yml。执行以下命令即可启动全部服务:

docker-compose up -d

启动后,观测数据在容器间按如下链路流转:

  1. Windmill Server / Worker 通过 OTLP/gRPC 将traces 与 logs发送到otel-collector:4317
  2. OpenTelemetry Collector 按管道拆分:traces 转发给tempo:4317,logs 以 OTLP/HTTP 形式转发给loki:3100/otlp
  3. Tempo 的 metrics generator 对 span 进行聚合,把生成的指标通过 remote write 推送给 Prometheus;
  4. Grafana(http://localhost:3000)通过预置的数据源接入 Tempo(Trace 查询)、Prometheus(指标)与 Loki(日志);
  5. 整个 Windmill 平台通过 Caddy 反向代理暴露在http://localhost

docker-compose 中与观测栈相关的服务定义如下(完整内容见 docker-compose.yml):

# Grafana OpenTelemetry Example init: image: &tempoImage grafana/tempo:latest user: root entrypoint: - "chown" - "10001:10001" - "/var/tempo" volumes: - tempo-data:/var/tempo otel-collector: image: otel/opentelemetry-collector:latest container_name: otel-collector expose: - 4317 volumes: - ./otel-config.yaml:/etc/otel/config.yaml command: ["--config=/etc/otel/config.yaml"] tempo: image: *tempoImage command: [ "-config.file=/etc/tempo.yaml" ] volumes: - ./tempo-config.yaml:/etc/tempo.yaml - tempo-data:/var/tempo expose: - 3200 - 4317 depends_on: - init loki: image: grafana/loki:latest expose: - 3100 command: -config.file=/etc/loki/local-config.yaml volumes: - ./loki-config.yaml:/etc/loki/local-config.yaml prometheus: image: prom/prometheus:latest command: - --config.file=/etc/prometheus.yaml - --web.enable-remote-write-receiver - --enable-feature=exemplar-storage - --enable-feature=native-histograms volumes: - ./prometheus-config.yaml:/etc/prometheus.yaml expose: - 9090 grafana: image: grafana/grafana:11.0.0 volumes: - ./grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml environment: - GF_AUTH_ANONYMOUS_ENABLED=true - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin - GF_AUTH_DISABLE_LOGIN_FORM=true - GF_FEATURE_TOGGLES_ENABLE=traceqlEditor metricsSummary - GF_INSTALL_PLUGINS=https://storage.googleapis.com/integration-artifacts/grafana-exploretraces-app/grafana-exploretraces-app-latest.zip;grafana-traces-app ports: - "3000:3000"

几个值得注意的部署细节:

  • init容器:以 root 身份对 Tempo 数据卷执行chown 10001:10001,解决 Tempo 容器(默认非 root 用户)对数据目录的写权限问题;
  • Prometheus 启动参数--web.enable-remote-write-receiver用于接收 Tempo metrics generator 的 remote write;--enable-feature=exemplar-storage--enable-feature=native-histograms分别为 trace 示例(exemplar)存储和原生直方图提供支持,与 Tempo 端generate_native_histograms: both的配置呼应;
  • Grafana 预置插件:通过GF_INSTALL_PLUGINS安装 grafana-traces-app,增强 Tempo 的 trace 查询体验;
  • 默认配置即开即用GF_AUTH_ANONYMOUS_ENABLED=true且匿名角色为 Admin,方便本地演示,生产环境建议关闭。

示例中的 Windmill 本体由windmill_server(1 副本)、windmill_worker(3 副本,默认 worker 组)、windmill_worker_native(native 组,NUM_WORKERS=8)以及可选的windmill_indexerwindmill_worker_reports(默认注释)组成,数据库使用postgres:16。Worker 通过挂载worker_logs:/tmp/windmill/logs与 Server 共享日志卷,这是日志能够统一汇出的基础。

OpenTelemetry Collector:一条管道,双向分流

otel-config.yaml 是整个观测栈的"路由器",其完整内容如下:

receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 processors: batch: timeout: 5s exporters: otlphttp/loki: endpoint: http://loki:3100/otlp tls: insecure: true otlp/tempo: endpoint: http://tempo:4317 tls: insecure: true service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [otlp/tempo] logs: receivers: [otlp] processors: [batch] exporters: [otlphttp/loki]

配置要点:

  • 单一 OTLP 接收端otlpreceiver 仅开启 gRPC 协议,监听0.0.0.0:4317,即 Windmill 的 "OTEL/Prom" 设置中需要填写的 endpoint;
  • batch 处理器timeout: 5s,将 span/log 按 5 秒窗口批量导出,降低网络开销与目标端写入压力;
  • 两条独立管道traces管道导出到 Tempo(OTLP/gRPC,tempo:4317);logs管道导出到 Loki(OTLP/HTTP,loki:3100/otlp)。注意两者使用的 exporter 协议不同——Loki 接收的是 OTLP/HTTP,因此端点带/otlp路径;
  • 内网明文传输tls.insecure: true适用于容器间内网通信;若 Collector 暴露到公网或跨网络部署,应补充 TLS 与认证配置。

配置 Windmill:让追踪与日志流向 Collector

在 Windmill UI(http://localhost)完成初始设置后,进入"Instances Settings"(实例设置)→ "OTEL/Prom" 选项卡

  1. 填写OpenTelemetry Collector endpointotel-collector:4317(即上文 Collector 的 OTLP/gRPC 监听地址);
  2. 填写Service Name(服务名),用于在 Tempo/Grafana 中标识来自 Windmill 的服务;
  3. 打开 "Tracing" 与 "Logging" 两个开关,使 Windmill 开始向otel-collector:4317发送链路追踪与日志。

从源码角度看,这一配置项的作用链路清晰可见:OTEL_TRACING_ENABLED是一个全局原子开关,在 backend/windmill-common/src/lib.rs 中定义(AtomicBool::new(std::env::var("OTEL_TRACING").is_ok())),Worker 在执行每个任务前会判断该开关决定是否注入TRACEPARENTOTEL_TRACE_IDOTEL_SPAN_ID等上下文环境变量(见 backend/windmill-worker/src/worker.rs)。同时 backend/windmill-common/src/global_settings.rs 维护了一份允许在 Worker 中透传的 OTEL 环境变量白名单,涵盖OTEL_EXPORTER_OTLP_ENDPOINTOTEL_EXPORTER_OTLP_TRACES_ENDPOINTOTEL_EXPORTER_OTLP_LOGS_ENDPOINTOTEL_EXPORTER_OTLP_PROTOCOLOTEL_EXPORTER_OTLP_COMPRESSIONOTEL_EXPORTER_OTLP_TIMEOUTOTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCEOTEL_SERVICE_NAME等;其中OTEL_EXPORTER_OTLP_*HEADERS被有意排除在白名单之外,因为其中可能携带导出器的 API Key 等敏感信息。这意味着除了 UI 配置,你也可以直接通过标准 OTLP 环境变量(如OTEL_EXPORTER_OTLP_ENDPOINTOTEL_SERVICE_NAME)对 Windmill 的遥测导出行为进行细粒度控制。

此外,Windmill API 侧还会为每个 HTTP 请求创建带有methoduriworkspace_idtraceId等字段的 request span(见 backend/windmill-api/src/tracing_init.rs),并通过log_context_middleware把请求上下文(含 trace_id)注入到日志记录中(backend/windmill-api/src/tracing_init.rs),为"日志与追踪关联"提供了基础。

在 Tempo UI 中查看与检索追踪

Tempo 的 UI 以 Grafana 插件形式提供。使用本示例的 docker-compose 部署时,Grafana 位于http://localhost:3000。当你通过 Windmill 运行一个脚本或工作流后,即可在 Tempo UI 中看到对应的 trace 并展开调查。

需要说明的是:虽然 OSS 仓库中add_root_flow_job_to_otlpset_job_span_parent在非 EE 构建下是空实现(见 backend/windmill-worker/src/otel_oss.rs,其中注释说明完整实现位于otel_ee),但从 backend/windmill-worker/src/result_processor.rs 中add_root_flow_job_to_otlp(&root_job, success)的调用点以及 backend/windmill-worker/src/worker.rs 中set_job_span_parent(&span, arc_job, &rj)的调用可以推断:在完整版本中,Windmill 会把任务 span 挂接到入站分布式追踪上下文(W3Ctraceparent)或由 UUID 派生的上下文之下,从而形成贯穿"请求 → 工作流 → 单步任务"的完整 trace 树。

用 Windmill 注入的标签精准过滤 trace

Tempo UI 的搜索功能支持按标签(tag)过滤。Windmill 会在每个任务的 span 上写入丰富的标签,以下是官方示例文档列出的、用于精准检索的标签清单:

标签含义
job_id作业(job)的 ID
root_job根作业 ID(即整个 flow 的根任务)
parent_job父作业 ID(flow 中的父任务)
flow_step_id工作流内步骤的 ID
script_path脚本路径
workspace_id工作区名称
worker_id执行该作业的 Worker ID
language脚本语言
tag工作流的队列标签(queue tag)

这些标签并非虚构——它们与 backend/windmill-worker/src/worker.rs 中create_span_with_name函数为 "job" span 注册的字段一一对应。该函数创建的 span 字段包括:job_idroot_jobworkspace_idworker(对应文档中的worker_id)、hostnametaglanguagescript_pathflow_step_idparent_jobjob_kindcreated_bytrigger_kindtriggerscript_hash,以及 OpenTelemetry 规范字段otel.nameotel.status_codeotel.status_message。具体填充逻辑为:

  • language取自arc_job.script_lang
  • 若存在flow_step_id,则otel.name会被改写为"{span_name} {step_id}"并记录flow_step_id
  • parent_job取自arc_job.parent_job
  • script_path取自arc_job.runnable_path
  • root_job取自arc_job.flow_innermost_root_job(即 flow 的最内层根任务)。

实际检索示例(Tempo 搜索语法):搜索某个脚本的所有执行记录可过滤script_path;排查某次工作流整体耗时可按root_job过滤出整棵 trace 树;定位流内单个步骤的性能则可结合flow_step_idparent_job。这种标签体系使得"从一次失败作业反查整条调用链"或"从一条 trace 下钻到具体步骤"都变得非常直接。

失败状态的标记

当任务执行失败时,Windmill 会把otel.status_code置为ERROR,并写入经过截断(上限 512 字符,见 backend/windmill-worker/src/worker.rs 的STATUS_DESCRIPTION_MAX_LEN)的错误信息到otel.status_message,避免单个 verbose 错误撑爆 span 载荷。通过record_job_span_status(backend/windmill-worker/src/worker.rs)可以区分"作业正常运行完成"、"命中缓存"、"执行报错"以及"其他 Worker 已抢先完成"等不同结果,从而在 Tempo 中准确识别真正的失败链路。

用 Prometheus 做指标监控:span → metrics 的自动转化

Tempo 的metrics generator可以把已采集 trace 的时间序列聚合为指标(metrics),进而:

  • 对比工作流内单个步骤的性能表现;
  • 观察各步骤随时间变化的整体性能与相对贡献
  • 识别并排查问题与异常。

在 tempo-config.yaml 中,metrics generator 的启用逻辑如下:

metrics_generator: registry: external_labels: source: tempo cluster: windmill storage: path: /var/tempo/generator/wal remote_write: - url: http://prometheus:9090/api/v1/write send_exemplars: true traces_storage: path: /var/tempo/generator/traces overrides: defaults: metrics_generator: processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator generate_native_histograms: both

要点解读:

  • processors: [service-graphs, span-metrics, local-blocks]service-graphs负责生成服务依赖关系图所需的数据;span-metrics将 span 时长等维度聚合为指标(这正是下面指标系列的来源);local-blocks支持基于 trace 的本地查询;
  • remote_write指向 Prometheushttp://prometheus:9090/api/v1/write,并开启send_exemplars: true,使指标样本携带对应的 trace 示例(exemplar),实现"从指标一键跳转到关联 trace";
  • generate_native_histograms: both:同时生成经典与原生直方图,配合 Prometheus 的--enable-feature=native-histograms启动参数使用;
  • 该示例为演示目的将 trace 保留期(compactor.compaction.block_retention)设为1h,生产部署时应按实际需求调整。

生成的指标会导出到 Prometheus,可在 Grafana 的Metrics Explorer中查看,具体指标系列如下:

  • traces_spanmetrics_calls_total
  • traces_spanmetrics_latency
  • traces_spanmetrics_latency_bucket
  • traces_spanmetrics_latency_count
  • traces_spanmetrics_latency_sum
  • traces_spanmetrics_size_total

其中traces_spanmetrics_calls_total统计 span 调用次数,traces_spanmetrics_latency及其_bucket/_count/_sum兄弟指标构成延迟分布直方图,traces_spanmetrics_size_total反映 span 数据量。在 Grafana 中通常用traces_spanmetrics_latency_bucket绘制直方图、用rate(traces_spanmetrics_calls_total[5m])观察调用速率、用histogram_quantile()计算 p95/p99 延迟。结合这些指标与标签(如span_nameservice及 Windmill 注入的script_path等维度),即可量化评估每个脚本/步骤在整条工作流中的耗时占比。

prometheus-config.yaml中为 Tempo 配置了抓取任务(job_name: 'tempo',targets 为tempo:3200),使 Prometheus 除了接收 remote write 的 span metrics 外,还能直接抓取 Tempo 自身的运行指标。

用 Loki 查看与分析日志

Windmill 的日志会被发送到Loki——一款与 Grafana 无缝集成的日志聚合系统。在 Grafana 中查看日志的步骤:

  1. 打开 Grafana UI(通常为http://localhost:3000);
  2. 进入"Explore"区域;
  3. 选择Loki数据源;
  4. 使用查询编辑器基于各种标签与字段过滤、检索日志。

本示例的 Loki 通过 OTLP 端点(loki:3100/otlp)接收日志,loki-config.yaml 采用单机本地文件存储(schema: v13store: tsdb),并开启了allow_structured_metadata: true以保留结构化元数据——这正是 OTLP 日志中携带的 trace/span 上下文、Windmill 标签等结构化信息能够被检索的前提。由于 Collector 在日志管道中保留了 OTLP 语义,来自 Windmill 的日志与 trace 天然共享同一套标签体系。

日志 × 追踪 × 指标:三者的关联闭环

这套方案真正的价值在于关联

  • 日志 ↔ 追踪:Windmill 的日志与 span 共享标签体系(如job_idworkspace_id),且 backend/windmill-api/src/tracing_init.rs 的中间件会把请求trace_id注入日志上下文;Tempo 的 exemplar 则实现了指标 → trace 的反向跳转。这意味着当你在 Loki 中定位到一条异常日志,可以顺藤摸瓜找到对应的 trace;反之亦然。
  • 指标 ↔ 追踪:Prometheus 中的 span metrics 携带 exemplar,可直接下钻到原始 trace,用于复盘"延迟升高期间到底发生了什么"。

对排查与性能分析而言,这构成了一个完整闭环:Grafana 中看指标(整体趋势与异常)→ 经 exemplar 跳到 Tempo 看 trace(定位到具体作业/步骤)→ 再跳到 Loki 看日志(还原执行细节)

从示例到生产:需要关注的事项

基于仓库内容,部署到生产环境前建议关注以下几点:

  • 数据保留期:示例中 Tempo 的block_retention1h,属演示配置,生产应按合规与排查需求调大,并考虑将storage.trace.backendlocal换为 S3/GCS 等对象存储;
  • Tempo 存储:当前使用本地文件系统,单机演示足够;大规模场景下分布式后端更合适;
  • Loki 存储:示例使用本地文件系统与tsdbschema,生产可切换为对象存储并部署多副本;
  • Grafana 安全:示例开启了匿名访问且匿名角色为 Admin,仅适用于本地演示,生产必须关闭并配置认证与权限;
  • 权限/镜像选择:docker-compose 通过${WM_IMAGE}变量指定 Windmill 镜像,并在注释中提示 EE 版本使用ghcr.io/windmill-labs/windmill-ee:main;若需要完整的 OTLP 根作业/traceparent 关联能力,可结合 EE 版本评估(OSS 构建中相关函数为空实现,见 backend/windmill-worker/src/otel_oss.rs);
  • 敏感环境变量OTEL_EXPORTER_OTLP_*HEADERS不会被透传到 Worker,如需携带认证头需自行规划安全通道。

总结

通过 examples/deploy/otel-tracing-grafana 目录下的这份示例,你可以用一条命令得到一套"开箱即用"的 Windmill 可观测性栈:OpenTelemetry Collector 负责接收与分流,Tempo 负责 trace 存储与查询,metrics generator 把 span 转成 Prometheus 指标,Loki 承接日志,Grafana 统一呈现。Windmill 在每个 job span 上注入的丰富标签(job_idroot_jobparent_jobflow_step_idscript_pathworkspace_idworker_idlanguagetag)与源码级实现(backend/windmill-worker/src/worker.rs)完全对应,让分布式追踪真正落地到"脚本 → 工作流 → 步骤"的每一层,帮助你在生产环境中快速定位故障、量化瓶颈并持续优化工作流性能。

【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询