ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

Windmill 可观测性实战:基于 Tempo、Grafana、Prometheus 与 Loki 的 OpenTelemetry 链路追踪与日志监控 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_id、root_job、flow_step_id等标签在 Tempo 中精准检索特定作业或工作流以及如何借助 Tempo 的 metrics generator 把 span 转化为可长期对比的性能指标。为什么选择 TempoOTLP 兼容与 Windmill 的天然契合Tempo 是 Grafana 开源的一款分布式追踪系统专为监控和调试微服务而设计它能够跨服务追踪请求、分析延迟、定位瓶颈并诊断故障。其典型使用场景包括排查生产问题、监控性能、可视化服务依赖关系以及优化系统可靠性。Tempo 之所以能与 Windmill 无缝对接关键在于它原生支持OpenTelemetry ProtocolOTLP——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启动后观测数据在容器间按如下链路流转Windmill Server / Worker 通过 OTLP/gRPC 将traces 与 logs发送到otel-collector:4317OpenTelemetry Collector 按管道拆分traces 转发给tempo:4317logs 以 OTLP/HTTP 形式转发给loki:3100/otlpTempo 的 metrics generator 对 span 进行聚合把生成的指标通过 remote write 推送给 PrometheusGrafanahttp://localhost:3000通过预置的数据源接入 TempoTrace 查询、Prometheus指标与 Loki日志整个 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-featureexemplar-storage - --enable-featurenative-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_ENABLEDtrue - GF_AUTH_ANONYMOUS_ORG_ROLEAdmin - GF_AUTH_DISABLE_LOGIN_FORMtrue - GF_FEATURE_TOGGLES_ENABLEtraceqlEditor metricsSummary - GF_INSTALL_PLUGINShttps://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-featureexemplar-storage与--enable-featurenative-histograms分别为 trace 示例exemplar存储和原生直方图提供支持与 Tempo 端generate_native_histograms: both的配置呼应Grafana 预置插件通过GF_INSTALL_PLUGINS安装 grafana-traces-app增强 Tempo 的 trace 查询体验默认配置即开即用GF_AUTH_ANONYMOUS_ENABLEDtrue且匿名角色为 Admin方便本地演示生产环境建议关闭。示例中的 Windmill 本体由windmill_server1 副本、windmill_worker3 副本默认 worker 组、windmill_worker_nativenative 组NUM_WORKERS8以及可选的windmill_indexer、windmill_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 设置中需要填写的 endpointbatch 处理器timeout: 5s将 span/log 按 5 秒窗口批量导出降低网络开销与目标端写入压力两条独立管道traces管道导出到 TempoOTLP/gRPCtempo:4317logs管道导出到 LokiOTLP/HTTPloki:3100/otlp。注意两者使用的 exporter 协议不同——Loki 接收的是 OTLP/HTTP因此端点带/otlp路径内网明文传输tls.insecure: true适用于容器间内网通信若 Collector 暴露到公网或跨网络部署应补充 TLS 与认证配置。配置 Windmill让追踪与日志流向 Collector在 Windmill UIhttp://localhost完成初始设置后进入Instances Settings实例设置→ OTEL/Prom 选项卡填写OpenTelemetry Collector endpointotel-collector:4317即上文 Collector 的 OTLP/gRPC 监听地址填写Service Name服务名用于在 Tempo/Grafana 中标识来自 Windmill 的服务打开 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 在执行每个任务前会判断该开关决定是否注入TRACEPARENT、OTEL_TRACE_ID、OTEL_SPAN_ID等上下文环境变量见 backend/windmill-worker/src/worker.rs。同时 backend/windmill-common/src/global_settings.rs 维护了一份允许在 Worker 中透传的 OTEL 环境变量白名单涵盖OTEL_EXPORTER_OTLP_ENDPOINT、OTEL_EXPORTER_OTLP_TRACES_ENDPOINT、OTEL_EXPORTER_OTLP_LOGS_ENDPOINT、OTEL_EXPORTER_OTLP_PROTOCOL、OTEL_EXPORTER_OTLP_COMPRESSION、OTEL_EXPORTER_OTLP_TIMEOUT、OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE与OTEL_SERVICE_NAME等其中OTEL_EXPORTER_OTLP_*HEADERS被有意排除在白名单之外因为其中可能携带导出器的 API Key 等敏感信息。这意味着除了 UI 配置你也可以直接通过标准 OTLP 环境变量如OTEL_EXPORTER_OTLP_ENDPOINT、OTEL_SERVICE_NAME对 Windmill 的遥测导出行为进行细粒度控制。此外Windmill API 侧还会为每个 HTTP 请求创建带有method、uri、workspace_id、traceId等字段的 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_otlp与set_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 注入的标签精准过滤 traceTempo UI 的搜索功能支持按标签tag过滤。Windmill 会在每个任务的 span 上写入丰富的标签以下是官方示例文档列出的、用于精准检索的标签清单标签含义job_id作业job的 IDroot_job根作业 ID即整个 flow 的根任务parent_job父作业 IDflow 中的父任务flow_step_id工作流内步骤的 IDscript_path脚本路径workspace_id工作区名称worker_id执行该作业的 Worker IDlanguage脚本语言tag工作流的队列标签queue tag这些标签并非虚构——它们与 backend/windmill-worker/src/worker.rs 中create_span_with_name函数为 job span 注册的字段一一对应。该函数创建的 span 字段包括job_id、root_job、workspace_id、worker对应文档中的worker_id、hostname、tag、language、script_path、flow_step_id、parent_job、job_kind、created_by、trigger_kind、trigger、script_hash以及 OpenTelemetry 规范字段otel.name、otel.status_code、otel.status_message。具体填充逻辑为language取自arc_job.script_lang若存在flow_step_id则otel.name会被改写为{span_name} {step_id}并记录flow_step_idparent_job取自arc_job.parent_jobscript_path取自arc_job.runnable_pathroot_job取自arc_job.flow_innermost_root_job即 flow 的最内层根任务。实际检索示例Tempo 搜索语法搜索某个脚本的所有执行记录可过滤script_path排查某次工作流整体耗时可按root_job过滤出整棵 trace 树定位流内单个步骤的性能则可结合flow_step_id与parent_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_statusbackend/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实现从指标一键跳转到关联 tracegenerate_native_histograms: both同时生成经典与原生直方图配合 Prometheus 的--enable-featurenative-histograms启动参数使用该示例为演示目的将 trace 保留期compactor.compaction.block_retention设为1h生产部署时应按实际需求调整。生成的指标会导出到 Prometheus可在 Grafana 的Metrics Explorer中查看具体指标系列如下traces_spanmetrics_calls_totaltraces_spanmetrics_latencytraces_spanmetrics_latency_buckettraces_spanmetrics_latency_counttraces_spanmetrics_latency_sumtraces_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_name、service及 Windmill 注入的script_path等维度即可量化评估每个脚本/步骤在整条工作流中的耗时占比。prometheus-config.yaml中为 Tempo 配置了抓取任务job_name: tempotargets 为tempo:3200使 Prometheus 除了接收 remote write 的 span metrics 外还能直接抓取 Tempo 自身的运行指标。用 Loki 查看与分析日志Windmill 的日志会被发送到Loki——一款与 Grafana 无缝集成的日志聚合系统。在 Grafana 中查看日志的步骤打开 Grafana UI通常为http://localhost:3000进入Explore区域选择Loki数据源使用查询编辑器基于各种标签与字段过滤、检索日志。本示例的 Loki 通过 OTLP 端点loki:3100/otlp接收日志loki-config.yaml 采用单机本地文件存储schema: v13、store: tsdb并开启了allow_structured_metadata: true以保留结构化元数据——这正是 OTLP 日志中携带的 trace/span 上下文、Windmill 标签等结构化信息能够被检索的前提。由于 Collector 在日志管道中保留了 OTLP 语义来自 Windmill 的日志与 trace 天然共享同一套标签体系。日志 × 追踪 × 指标三者的关联闭环这套方案真正的价值在于关联日志 ↔ 追踪Windmill 的日志与 span 共享标签体系如job_id、workspace_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_retention为1h属演示配置生产应按合规与排查需求调大并考虑将storage.trace.backend从local换为 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_id、root_job、parent_job、flow_step_id、script_path、workspace_id、worker_id、language、tag与源码级实现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),仅供参考
返回列表