news 2026/9/18 22:32:39

使用 Grafana Tanka 在 Kubernetes 上部署 Grafana Tempo

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Grafana Tanka 在 Kubernetes 上部署 Grafana Tempo

使用 Grafana Tanka 在 Kubernetes 上部署 Grafana Tempo

【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo

本文以 Grafana Tempo 官方部署指南为骨架,结合当前仓库operations/jsonnet/microservices中的 Jsonnet 库源码,系统讲解如何借助 Grafana Tanka 与 jsonnet-bundler 在 Kubernetes 上快速拉起一套 Tempo 微服务集群(含 MinIO 对象存储、可选 metrics-generator 与 KEDA 自动扩缩)。读者完成后将掌握从初始化 Tanka 环境、生成 Kubernetes 清单到tk apply上线并验证的完整实战流程,同时理解微服务 Jsonnet 库中各组件副本数、资源、存储类等关键参数背后的默认值与源码依据。

概述:Tanka + Jsonnet 部署方式与适用场景

Tanka 是 Grafana Labs 开源的 Kubernetes 配置管理工具,它基于 Jsonnet 语言,允许你用可编程、可复用的方式描述集群资源,再渲染为 Kubernetes YAML 清单并通过tk apply下发到集群。相比手写大量 YAML,Jsonnet 天然支持变量、继承与函数抽象,非常适合维护 Tempo 这类组件众多(distributor、querier、query-frontend、metrics-generator、block-builder、live-store、backend-scheduler、backend-worker、memcached 等)的微服务部署。

当前仓库在 operations/jsonnet/microservices 目录下维护了一套完整的微服务 Jsonnet 库,其入口 tempo.libsonnet 通过(import 'common.libsonnet') + (import 'configmap.libsonnet') + ...的方式把各组件(distributor、generator、frontend、querier、block-builder、live-store、backend-scheduler、backend-worker、vulture、memcached、memberlist 等)的 libsonnet 文件组合成一个整体,并额外引入 autoscaling.libsonnet 提供基于 KEDA 的水平自动扩缩能力。

需要特别说明的是:本文的部署方式面向开发集群或沙箱环境,官方文档明确指出该配置不适合直接用于生产环境,但它是学习 Tempo 各组件协作关系的绝佳途径。在生产环境,建议使用云厂商托管的对象存储(如 S3、GCS、Azure Blob)以避免自行运维对象存储的开销。

前置条件

开始部署前,请确认以下资源:

  • 一个 Kubernetes 集群:官方默认配置至少需要40 个 CPU 和 46GB 内存。如果只是小规模的写入或查询量,可以使用远小于此规格的配置(后文会介绍如何下调组件资源)。
  • kubectl:版本需与集群的 Kubernetes API 版本匹配。
  • 本地可用的tk(Tanka)与jb(jsonnet-bundler)命令行工具。

配置 Kubernetes 并安装 Tanka

1. 创建工作目录

mkdir tempo cd tempo

2. 创建 Kubernetes 命名空间

kubectl create namespace tempo

示例中命名空间为tempo,你可以替换为任意名称;后续所有kubectl命令与 Tanka 环境都会使用该命名空间。

3. 安装工具链

  • 安装 Grafana Tanka(参考 Tanka 官方安装文档)。如果你的机器上已有 Go 环境,也可以在非 GOPATH、非 go.mod 项目的目录下直接执行:
    go install github.com/grafana/tanka/cmd/tk@latest go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest
  • 安装jsonnet-bundlerjb),它是 Jsonnet 的依赖管理器,用于拉取k.libsonnet、Tempo 库和 Memcached 库。

设置 Tanka 环境

Tanka 依赖当前 Kubernetes 上下文(context)来生成面向正确集群的清单。

  1. 检查当前上下文是否正确:

    kubectl config current-context
  2. 初始化 Tanka 环境。tk init --k8s=false表示不生成 Kubernetes 相关的默认配置(因为稍后通过tk env set显式指定),然后添加名为environments/tempo的环境,并将该环境绑定到当前上下文对应的集群与tempo命名空间:

    tk init --k8s=false tk env add environments/tempo tk env set environments/tempo \ --namespace=tempo \ --server-from-context=$(kubectl config current-context)

这一步完成后,environments/tempo目录下会生成spec.json(环境规格)与后续我们要写入的main.jsonnet

安装 Jsonnet 库

需要安装三类库:k.libsonnet(Kubernetes API 的 Jsonnet 封装)、Tempo 微服务库及其依赖、Memcached 库。

  1. 安装k.libsonnet。将K8S_VERSION设为与你集群主版本一致的次版本(例如1.32):

    mkdir -p lib export K8S_VERSION=1.32 jb install github.com/jsonnet-libs/k8s-libsonnet/${K8S_VERSION}@main cat <<EOF > lib/k.libsonnet import 'github.com/jsonnet-libs/k8s-libsonnet/${K8S_VERSION}/main.libsonnet' EOF
  2. 安装 Tempo Jsonnet 库及其依赖(即本仓库 operations/jsonnet/microservices 对应发布的模块):

    jb install github.com/grafana/tempo/operations/jsonnet/microservices@main
  3. 安装 Memcached 库及其依赖:

    jb install github.com/grafana/jsonnet-libs/memcached@master

安装完成后,jsonnetfile.jsonjsonnetfile.lock.json会记录这些依赖及其锁定版本,保证环境可复现。

部署 MinIO 对象存储

MinIO 是开源的 Amazon S3 兼容对象存储,可免费在 Kubernetes 上运行。本文用它充当 Tempo 的 trace 后端存储,无论底层是何种云平台或本地环境,这一步骤都保持一致。

  1. 创建minio.yaml,内容如下。根据你的 Kubernetes 平台,可能需要修改storageClassName——例如 GKE 可能不支持local-path,但支持standard等其他名称:

    apiVersion: v1 kind: PersistentVolumeClaim metadata: # This name uniquely identifies the PVC. Will be used in deployment below. name: minio-pv-claim labels: app: minio-storage-claim spec: # Read more about access modes here: http://kubernetes.io/docs/user-guide/persistent-volumes/#access-modes accessModes: - ReadWriteOnce storageClassName: local-path resources: # This is the request for storage. Should be available in the cluster. requests: storage: 50Gi --- apiVersion: apps/v1 kind: Deployment metadata: name: minio spec: selector: matchLabels: app: minio strategy: type: Recreate template: metadata: labels: # Label is used as selector in the service. app: minio spec: # Refer to the PVC created earlier volumes: - name: storage persistentVolumeClaim: # Name of the PVC created earlier claimName: minio-pv-claim initContainers: - name: create-buckets image: busybox:1.28 command: - 'sh' - '-c' - 'mkdir -p /storage/tempo-data' volumeMounts: - name: storage # must match the volume name, above mountPath: '/storage' containers: - name: minio # Pulls the default Minio image from Docker Hub image: minio/minio:latest args: - server - /storage - --console-address - ':9001' env: # MinIO root credentials - name: MINIO_ROOT_USER value: 'minio' - name: MINIO_ROOT_PASSWORD value: 'minio123' ports: - containerPort: 9000 - containerPort: 9001 volumeMounts: - name: storage # must match the volume name, above mountPath: '/storage' --- apiVersion: v1 kind: Service metadata: name: minio spec: type: ClusterIP ports: - port: 9000 targetPort: 9000 protocol: TCP name: api - port: 9001 targetPort: 9001 protocol: TCP name: console selector: app: minio

    该清单包含三部分:

    • PVC:申请 50Gi 存储,accessModesReadWriteOnce
    • Deployment:initContainer 预先在/storage下创建tempo-data目录作为 bucket 根路径;MinIO 容器以server /storage启动,并暴露 9000(S3 API)与 9001(Web 控制台)两个端口;默认凭据为minio/minio123
    • Service:ClusterIP 类型,将 9000/9001 端口映射为apiconsole
  2. 应用该清单:

    kubectl apply --namespace tempo -f minio.yaml
  3. 验证 bucket 已创建。没有该 bucket,Tempo 将无法存储任何数据

    kubectl port-forward --namespace tempo service/minio 9001:9001

    然后用浏览器访问http://localhost:9001,使用minio/minio123登录,确认 Buckets 页面中存在tempo-data

  4. 编写environments/tempo/main.jsonnet,将 Tempo 集群指向 MinIO,并配置各组件副本数、接收器端口、metrics-generator 与 Kafka 等参数:

    cat <<EOF > environments/tempo/main.jsonnet // The jsonnet file used to generate the Kubernetes manifests. local tempo = import 'microservices/tempo.libsonnet'; local k = import 'ksonnet-util/kausal.libsonnet'; local container = k.core.v1.container; local containerPort = k.core.v1.containerPort; tempo { _images+:: { tempo: 'grafana/tempo:3.0.0', }, tempo_distributor_container+:: container.withPorts([ containerPort.new('jaeger-grpc', 14250), containerPort.new('otlp-grpc', 4317), ]), _config+:: { namespace: 'tempo', query_frontend+: { replicas: 2, }, querier+: { replicas: 3, }, block_builder+: { replicas: 2, }, live_store+: { replicas: 2, pvc_size: '10Gi', pvc_storage_class: 'standard', }, backend_scheduler+: { pvc_size: '1Gi', pvc_storage_class: 'standard', }, backend_worker+: { replicas: 1, }, distributor+: { replicas: 3, receivers: { jaeger: { protocols: { grpc: { endpoint: '0.0.0.0:14250', }, }, }, otlp: { protocols: { grpc: { endpoint: '0.0.0.0:4317', }, }, }, }, }, metrics_generator+: { replicas: 1, ephemeral_storage_request_size: '10Gi', ephemeral_storage_limit_size: '11Gi', pvc_size: '10Gi', pvc_storage_class: 'standard', }, memcached+: { replicas: 3, }, bucket: 'tempo-data', backend: 's3', }, tempo_config+:: { storage+: { trace+: { s3: { bucket: $._config.bucket, access_key: 'minio', secret_key: 'minio123', endpoint: 'minio:9000', insecure: true, }, }, }, ingest+: { kafka+: { address: 'kafka:9092', topic: 'tempo-ingest', }, }, block_builder+: { consume_cycle_duration: '30s', }, metrics_generator+: { processor: { span_metrics: {}, service_graphs: {}, }, registry+: { external_labels: { source: 'tempo', }, }, }, overrides+: { defaults+: { metrics_generator+: { processors: ['service-graphs', 'span-metrics'], }, }, }, }, } EOF

    下面对该配置做逐段拆解:

    • _images+:::覆盖 Tempo 镜像版本。仓库 config.libsonnet 中默认定义了tempo: 'grafana/tempo:3.0.0',并将同一个镜像复用到 distributor、querier、query-frontend、metrics-generator、block-builder、live-store、backend-scheduler、backend-worker 等所有 Tempo 组件;这里显式指定版本可保证可复现性。
    • tempo_distributor_container+:::为 distributor 容器追加jaeger-grpc(14250)与otlp-grpc(4317)两个端口,与下方receivers的监听端点一一对应。
    • _config+:::覆盖各组件的副本数与存储配置。query_frontendquerierblock_builderlive_storebackend_workerdistributormetrics_generatormemcached的默认值均定义在 config.libsonnet 中(例如query_frontend.replicas默认 1、querier.replicas默认 2、distributor.replicas默认 1、memcached.replicas默认 3),这里通过+:合并运算符逐项覆盖。注意live_storebackend_schedulermetrics_generatorpvc_sizepvc_storage_class在源码中是必填项(源码中写为error 'Must specify a live-store pvc size'等),因此必须在_config中给出。
    • tempo_config+:::这是写入 ConfigMap 的实际 Tempo 配置:
      • storage.trace.s3:连接 MinIO,endpoint: 'minio:9000'指向 MinIO Service,insecure: true表示走 HTTP(MinIO 未启用 TLS);
      • ingest.kafka:当前部署模式要求 Kafka 兼容系统(如 Kafka 或 Redpanda),address: 'kafka:9092'为示例地址,部署前需根据你的 Kafka 实例修改;
      • block_builder.consume_cycle_duration:block-builder 消费 Kafka 分区并落盘的周期;
      • metrics_generator.processor:启用span_metricsservice_graphs两个处理器,并通过overrides.defaults.metrics_generator.processors在租户级别默认开启。

    官方文档特别提醒:该配置依赖 Kafka 兼容系统,你需要在部署 Tempo 之前先部署 Kafka(或 Redpanda),并将ingest.kafka.address改为指向你的 Kafka 实例。

可选:启用 metrics-generator

前面的配置已经启用了 metrics 生成能力,但你还需要指定生成指标的去向。如果希望将指标远程写入 Prometheus 兼容实例(如 Grafana Cloud Metrics 或 Mimir),请在tempo_configmetrics_generator段中加入下面的 remote_write 配置块(示例假设需要 basic auth,如果不需要则删除basic_auth段):

storage+: { remote_write: [ { url: 'https://<urlForPrometheusCompatibleStore>/api/v1/write', send_exemplars: true, basic_auth: { username: '<username>', password: '<password>', }, } ], },

Grafana Cloud Metrics 实例的端点等细节可通过 Grafana Cloud Portal 获取。需要留意:启用 metrics 生成并远程写入 Grafana Cloud Metrics 会产生额外的活跃序列(active series),可能影响账单计费;metrics-generator 的完整能力说明可参考仓库内 modules/generator 相关实现。

可选:启用 KEDA 自动扩缩

Tempo 的微服务 Jsonnet 库内置了基于 KEDA 的水平自动扩缩支持,适用于 distributor、metrics-generator、backend-worker、live-store 与 block-builder 组件。所有 KEDA scaler默认关闭enabled: false),你可以通过_config.<component>.keda逐组件独立开启。开启前请确保集群已安装 KEDA operator 与 CRD。

以下示例一次性开启所有受支持的 KEDA scaler。将autoscaling_prometheus_url设为你的 Prometheus 兼容后端地址;如果后端是多租户系统(如 Grafana Mimir),还需设置autoscaling_prometheus_tenant为你的租户 ID,KEDA 会在每次抓取请求中附带X-Scope-OrgID头:

_config+:: { autoscaling_prometheus_url: 'http://prometheus-operated.monitoring.svc.cluster.local:9090', // autoscaling_prometheus_tenant: 'my-tenant', // Required for multi-tenant backends (e.g. Grafana Mimir) distributor+: { keda+: { enabled: true, min_replicas: 2, max_replicas: 200, target_cpu: '330m', }, }, metrics_generator+: { keda+: { enabled: true, min_replicas: 1, max_replicas: 200, target_cpu: '500m', // query: '', // Optional: a PromQL query for a Prometheus trigger. When set, it replaces the CPU trigger. }, }, backend_worker+: { keda+: { enabled: true, min_replicas: 3, max_replicas: 200, threshold: 200, }, }, live_store+: { keda+: { enabled: true, min_replicas: 1, max_replicas: 200, // window_seconds: 1800, // retention window; >= complete_block_timeout + query_backend_after // bytes_per_replica: 16800000000, // ~16 GiB at 10 MB/s per pod over 30m }, }, block_builder+: { keda+: { enabled: true, // scaling controls how block-builder replicas track live-store: // 'rollout-operator' (default): rollout-operator mirrors live-store zone-a replicas // directly to block-builder. Faster on both scale-up and scale-down. // Requires live_store.keda.enabled=true. rollout_operator_replica_template_access_enabled // is set automatically. // 'keda': a dedicated KEDA ScaledObject uses a kubernetes-workload trigger counting // live-store zone-a pods. Works with or without live-store KEDA. Reaction time is // bounded by KEDA's polling cycle and stabilization windows. // scaling: 'rollout-operator', }, }, },

结合仓库源码 autoscaling.libsonnet 可以理解各组件实际使用的 KEDA 触发器类型:

  • distributor:使用cpu类型触发器,metricType: 'AverageValue',以target_cpu(默认330m,源码注释表明该值约对应每 pod 10MB/s 的写入吞吐)作为平均 CPU 目标。开启后 distributor Deployment 的spec.replicas会被移除,改由 ScaledObject 管理。
  • metrics-generator:默认同样基于 CPU(target_cpu默认500m)。若设置_config.metrics_generator.keda.query为一段 PromQL,则改用 Prometheus 触发器(metricType: 'Value',阈值固定为 1),此时查询结果必须精确等于期望的 pod 数量。这一机制常用于将副本数限制在 live-store 分区数以内——因为 metrics-generator 从 live-store 分区读取数据,超过分区数的 pod 没有可归属的分区,只会空转。
  • backend-worker:基于 Prometheus 查询tempodb_compaction_outstanding_blocks(默认查询使用avg_over_time计算 backend-scheduler 侧积压 block 数除以 backend-worker 副本数),以threshold(默认 200)为每个 pod 的平均积压阈值;开启时同样要求设置autoscaling_prometheus_url
  • live-store:默认 Prometheus 查询为sum(rate(tempo_distributor_kafka_write_bytes_total{...}[window_seconds])) * window_seconds,即从 distributor(Kafka 生产者)侧度量窗口期内预期持有的总字节数;bytes_per_replica(默认10MB/s × window_seconds,约 16GiB/30 分钟)作为每个副本的字节目标,metricType: 'AverageValue'使期望副本数 = 总字节 / 单副本字节。scale_down_stabilization_window_seconds默认 35 分钟(与 live-store drain 窗口一致),避免短暂负载低谷触发缩容。
  • block-builder:扩缩独立配置于_config.block_builder.keda。默认策略scaling: 'rollout-operator'利用 rollout-operator 将 live-store zone-a 的副本数直接镜像给 block-builder,scale-up/scale-down 都最快(block-builder 在 ReplicaTemplate 变化时立即响应,并且会保持存活直到 live-store drain 窗口结束,避免丢失在途分区数据再写入后端);该策略要求live_store.keda.enabled=true,Tempo 会自动设置rollout_operator_replica_template_access_enabled。另一种策略scaling: 'keda'使用独立的 KEDA ScaledObject(kubernetes-workload触发器)统计 live-store zone-a pod 数量,不依赖 live-store KEDA 是否开启,但由于要经过 KEDA 轮询周期与稳定窗口,反应更慢:scale-up 要等新 zone-a pod 运行并被计数,scale-down 要等 zone-a pod 完全终止。block-builder KEDA 默认使用激进的扩缩窗口来尽量缩短延迟。当 block-builder KEDA 激活时,Tempo 会把_config.block_builder.partitions_per_instance(默认1)注入 block-builder 配置,保证分区分配计算正确。

可选:降低组件资源要求

如果写入与查询量较小,可以下调各组件分配的 CPU 与内存。例如调整 block-builder 的资源配额:

  1. 打开environments/tempo/main.jsonnet
  2. 为对应组件(此处为 block-builder)新增配置块:
    tempo_block_builder_container+:: { resources+: { limits+: { cpu: '3', memory: '5Gi', }, requests+: { cpu: '200m', memory: '2Gi', }, }, },
  3. 保存文件。

+:是 Jsonnet 的合并运算符,它会在库默认资源值(见 config.libsonnet,block-builder 默认 requests 为 500m CPU / 1Gi 内存,limits 为 1 CPU / 2Gi 内存)基础上逐字段覆盖。官方文档提醒:降低资源配额可能影响整体性能,需要结合实际负载权衡。

使用 Tanka 部署 Tempo

  1. 生成并应用全部 Kubernetes 清单:

    tk apply environments/tempo/main.jsonnet

    如果想在下发前先查看生成的 YAML,也可以先用tk show environments/tempo/main.jsonnet预览,或使用tk diff environments/tempo/main.jsonnet对比集群现状,这与仓库 operations/jsonnet/microservices/README.md 中推荐的tk diff/tk apply工作流一致。

  2. 故障排查提示:如果部署后 live-store 没有正常启动,可能与持久化存储选择的存储类有关。此时可在_config(注意不是tempo_config)段中添加合适的存储类,例如将 fast 存储类改为 standard:

    live_store+: { pvc_storage_class: 'standard', },

    从源码 live-store.libsonnet 可以看到,live-store 的 PVC 使用$._config.live_store.pvc_storage_class作为storageClassName,且accessModes固定为ReadWriteOnce,因此存储类必须能被你的集群支持并满足单节点读写要求。

验证与下一步

部署成功后,Tempo 实例将通过 distributor Service(distributor.tempo.svc.cluster.local)接收两种已配置的 trace 协议:

  • OTLP gRPC:端口4317
  • Jaeger gRPC:端口14250

查询入口为query-frontend.tempo.svc.cluster.local3200端口。

接下来需要验证部署是否正常工作,可以使用官方提供的测试应用:参考 Validate Kubernetes deployment using a test application 的步骤,向上述端口推送示例 trace 并通过查询接口检索,确认数据端到端可用。此外,仓库中的示例配置 test/environments/default/main.jsonnet 展示了一套贴近实际的最小化配置(使用 k3d、local-path存储类、单副本 memcached 等),可作为本地快速验证的参考范本。

【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 22:29:18

HMC5883L在STM32F4上的可靠驱动与高精度磁力计校准

1. 为什么HMC5883L在STM32F4上跑不稳&#xff1f;——从“能通信”到“测得准”的真实断层你手里的STM32F4开发板接上了HMC5883L磁力计&#xff0c;I2C扫描能识别0x1E地址&#xff0c;读寄存器也返回了非零值&#xff0c;串口打印出的原始X/Y/Z数据跳变剧烈、方向感混乱&#x…

作者头像 李华
网站建设 2026/9/18 22:26:35

PS2022扩展面板未经正确签署:CEP签名与PlayerDebugMode修复

PS2022装上扩展面板&#xff0c;满怀期待点开&#xff0c;结果面板区域甩出一句"未经正确签署"&#xff0c;旁边那个扩展图标灰着点不动&#xff0c;连配置按钮都是死的——这个场景我在同行群里见过太多次&#xff0c;也帮人远程排查过好几轮。很多人第一反应是&quo…

作者头像 李华
网站建设 2026/9/18 22:25:51

电视盒子刷机装Armbian系统:免费完整指南

电视盒子刷机装Armbian系统&#xff1a;免费完整指南 【免费下载链接】amlogic-s9xxx-armbian Supports running Armbian on Amlogic, Allwinner, and Rockchip devices. Support a311d, s922x, s905x3, s905x2, s912, s905d, s905x, s905w, s905, s905l, rk3588, rk3568, rk33…

作者头像 李华
网站建设 2026/9/18 22:24:58

AD智能路由不切换?健康检查与会话保持排错指南

简介&#xff1a;一份面向网络运维工程师的深信服AD智能路由排错指导PPT&#xff0c;以故障现象为线索&#xff0c;覆盖智能路由不生效、出站链路负载不均、DNS解析异常及DNS代理失效等场景&#xff0c;系统梳理从配置检查、链路状态确认到路由测试的完整排查路径。资源包共1个…

作者头像 李华