亚马逊AWS官方博客
在 Amazon EKS 上构建开源可观测性数据栈:VictoriaMetrics + Grafana Loki + Grafana Tempo
摘要:本文分享在 Amazon EKS 上自建开源可观测性数据栈的完整实战:用 VictoriaMetrics、Grafana Loki、Grafana Tempo 分别承载指标、日志与链路,数据存入 Amazon S3 与 EBS gp3,通过 IRSA 安全集成。借助 OpenTelemetry Operator 实现存量微服务零代码接入,并在 Grafana 中打通 trace、log、metrics 三向一键关联,自动生成服务拓扑,显著提升故障排查效率。
目录
一、背景与需求
随着容器化微服务架构在企业中广泛落地,运行在 Amazon Elastic Kubernetes Service(Amazon EKS)上的工作负载日益复杂。一个典型的生产集群可能包含数十个微服务、数百个 Pod,每天产生大量日志、指标时间序列以及分布式链路追踪数据。如何高效地采集、存储、查询并关联日志(Logs)、指标(Metrics)、链路追踪(Traces)这三类可观测性数据,是 Amazon EKS 运维团队面临的核心挑战。
在 Amazon EKS 上构建可观测性体系,通常有亚马逊云科技托管与开源自建两条路径,二者并无绝对优劣,适用于不同团队与场景。
本文聚焦开源自建路径,介绍如何在 Amazon EKS 上落地一套开源可观测性方案:以 VictoriaMetrics + Grafana Loki + Grafana Tempo 组合覆盖三大支柱,配合 Amazon S3 与 Amazon EBS gp3 存储,通过 Grafana 实现 trace ↔ log ↔ metrics 的一键关联,并借助 OpenTelemetry Operator 对存量微服务实现零代码接入。
二、技术选型与方案概述
即便在开源自建路径内,每一支柱也有多种选择。本文选用 VictoriaMetrics + Grafana Loki + Grafana Tempo,最根本的考量是生态一致——三者同属或兼容 Grafana 生态,能在同一个 Grafana 中实现三大支柱的原生关联跳转,即“选一套能打通的组合”而非“每个支柱各选最强”;成本与运维上的优势则体现在下面各支柱的具体取舍中。
各支柱的取舍如下:
- 指标支柱:VictoriaMetrics 集群版。它由 vmstorage、vminsert、vmselect 三个组件构成,存储与计算分离、可独立水平扩缩,兼容 PromQL(基于 MetricsQL,PromQL 超集)。与 Prometheus + Thanos 这类组合相比,它以更少的组件(对比 Thanos 的 Sidecar、Store、Compactor、Query 等)提供长期存储与水平扩展能力,运维面更小;指标数据存放在 Amazon EBS gp3,存储成本随数据量平缓增长;官方资料也指出其在内存与磁盘占用上更为精简(具体倍数建议以自行压测为准)。
- 日志支柱:Grafana Loki。其核心设计是“只索引标签,不索引日志内容”,存储成本远低于 Elasticsearch、OpenSearch 等全文索引方案,并可借助 Amazon S3 实现近乎无限的数据保留;LogQL 配合 Grafana 可与链路数据直接关联。日志采集使用 Grafana Alloy。此前常用的 Promtail 已于 2026 年 3 月停止支持(EOL),Grafana 官方推荐迁移到 Alloy,存量 Promtail 配置可通过
alloy convert --source-format=promtail一键转换,从 Promtail 迁移的详细步骤参见 迁移指南。 - 链路支柱:Grafana Tempo。它接收 OpenTelemetry OTLP 数据、长期存储在 Amazon S3,相比 Jaeger 存储依赖更轻;其内置的
metrics_generator可从 span 自动生成 RED 指标与服务拓扑,是实现 trace 到 metrics 关联的关键。 - 可视化环节:Grafana。一个界面连接上述三个数据源,并通过数据源关联配置打通三向跳转。整套栈组件数量少、排查路径清晰,运维负担较低。
全部组件复用 EKS 集群内的现有节点,并通过 IRSA(IAM Roles for Service Accounts)与 Amazon S3 等亚马逊云科技服务安全集成。存量微服务借助 OpenTelemetry Operator 自动注入接入,无需改动任何业务代码(演示应用见第七章)。
三、总体架构
整体架构如下图所示。
[图1 整体架构] |
图示说明
架构自上而下分为应用层、采集层、存储层,底部为 Grafana。应用层的每个微服务产生的三类数据经三条路径分别采集:
(1)trace 由 OpenTelemetry 探针通过 OTLP 协议直接发送到 Grafana Tempo,无中间采集器;
(2)标准输出日志由 Grafana Alloy(DaemonSet)采集后推送到 Grafana Loki;
(3)/metrics 端点由 vmagent 抓取后写入 VictoriaMetrics。
图中右侧为存储指向:日志与链路的长期数据存入 Amazon S3,指标数据存放在 Amazon EBS gp3;Tempo 的 metrics_generator 从 trace 生成的指标也写回 VictoriaMetrics(对应图中回流箭头)。
四、前置条件与基础设施
在部署可观测性组件之前,需要确保以下基础设施就绪。
4.1 Amazon EKS 集群
- Amazon EKS 集群(本文使用 1.34 版本)
- 已启用 OIDC Provider(IRSA 的前提)
- 已安装 Amazon EBS CSI Driver
本文所述配置在以下版本组合上实测通过(供复现参考):EKS 1.34、VictoriaMetrics v1.106.1(集群版)、Grafana Loki 3.3.2、Grafana Tempo 2.7.1、Grafana Alloy v1.17.1、Grafana 11.4.0。不同大版本的配置项可能存在差异,请以对应版本的官方文档为准。
各存储组件(VictoriaMetrics、Grafana Loki、Grafana Tempo)通过 StorageClass 动态申请 Amazon EBS gp3 卷。这里创建一个 ebs-gp3-retain,将回收策略设为 Retain(删除 PVC 时保留卷,防止误删数据),绑定模式设为 WaitForFirstConsumer(等 Pod 调度到节点后再创建卷,保证卷与 Pod 处于同一可用区):
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ebs-gp3-retain
annotations:
storageclass.kubernetes.io/is-default-class: "true" # 若集群已有默认 StorageClass,请勿重复设为默认
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
allowVolumeExpansion: true
parameters:
type: gp3
encrypted: "true"
csi.storage.k8s.io/fstype: ext4
StorageClass 的完整参数说明(如 type、encrypted、csi.storage.k8s.io/fstype 等取值)可参考 Amazon EBS CSI Driver 参数文档。
4.2 Amazon S3 与 IRSA
Grafana Loki 与 Grafana Tempo 使用 Amazon S3 作为长期对象存储,其 Pod 需通过 IRSA 获取 S3 读写权限。核心步骤是:创建限定到指定 Bucket 的 IAM Policy,再创建信任 EKS OIDC Provider、限定 observability 命名空间下 loki 与 tempo ServiceAccount 的 IAM Role,最后在两个 ServiceAccount 上注解 role-arn。
IRSA 信任策略的关键在于 Condition 中的 sub 字段,它将可假扮该角色的身份严格限定为指定命名空间下的指定 ServiceAccount:
"Condition": {
"StringEquals": {
"<oidc-provider>:aud": "sts.amazonaws.com",
"<oidc-provider>:sub": [
"system:serviceaccount:observability:loki",
"system:serviceaccount:observability:tempo"
]
}
}
配置完成后,Pod 启动时 Amazon EKS 会自动注入 OIDC token 与环境变量,Pod 内的 AWS SDK 据此向 STS 换取临时凭证访问 Amazon S3,整个过程不需要在集群中存放任何长期密钥。
最后创建 observability 命名空间,并为 loki 与 tempo 两个 ServiceAccount 注解上一步创建的 IAM Role ARN:
apiVersion: v1
kind: Namespace
metadata:
name: observability
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: loki
namespace: observability
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/eks-obs-irsa-s3
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tempo
namespace: observability
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/eks-obs-irsa-s3
IRSA 的完整配置步骤(创建 OIDC Provider、IAM Role 与信任策略)可参考 Amazon EKS 官方文档。
五、核心组件部署
5.1 指标支柱:VictoriaMetrics 集群版
VictoriaMetrics 集群版由三个组件构成:vmstorage(数据存储,以 StatefulSet 部署并挂载 Amazon EBS gp3)、vminsert(写入路由)、vmselect(查询路由)。这种架构实现了存储与计算分离,写入压力与查询压力互不影响。其中数据保留周期由 vmstorage 控制,多副本写入与查询侧去重则由 vminsert、vmselect 的参数配合完成:
# vmstorage 关键参数
args:
- --storageDataPath=/storage
- --retentionPeriod=7d # 数据保留周期,生产建议按需调大
- --dedup.minScrapeInterval=30s # 与 vmselect 设相同值,保证查询结果一致
# vmselect 关键参数
args:
- --replicationFactor=2 # 与 vminsert 一致
- --dedup.minScrapeInterval=30s # 多副本数据在查询侧去重(复制启用时必需)
# vminsert 通过 --replicationFactor=2 将每条数据写入 2 个 vmstorage 节点(高可用)
指标采集由 vmagent 负责,它通过 Kubernetes 服务发现自动发现带 prometheus.io/scrape: "true" 注解的 Pod,并将指标 remote_write 到 vminsert;同时,vmagent 还可抓取节点级的 cAdvisor 容器指标,覆盖应用指标与基础资源指标。抓取 cAdvisor 节点指标需要 vmagent 的 ClusterRole 具备 nodes 与 nodes/proxy 权限。
vmagent 的服务发现依赖 Pod 上的标准注解,应用只需声明以下注解即可被自动纳入采集,无需逐个配置抓取目标:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "<metrics-port>" # 应用暴露指标的端口
prometheus.io/path: "/metrics" # 指标端点路径,按应用实际情况填写
各组件的完整部署清单可参考 VictoriaMetrics 集群版官方文档;vmagent 的部署(含基于注解的服务发现与 RBAC 配置)可参考 vmagent 官方 Helm chart 文档。
5.2 日志支柱:Grafana Loki 与 Grafana Alloy
Grafana Loki 支持单进程、SimpleScalable、Microservices 三种部署模式。本文演示环境采用单进程模式,组件少、易于排查;生产环境建议采用 SimpleScalable 模式,将组件拆分为 write、read、backend 三个角色,实现读写分离与按需扩缩。Loki 的关键配置是 TSDB 索引、Amazon S3 对象存储与保留策略:
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: s3
schema: v13
index:
prefix: index_
period: 24h
storage_config:
tsdb_shipper:
active_index_directory: /loki/tsdb-index
cache_location: /loki/tsdb-cache
aws:
bucketnames: <your-bucket-name>
region: <your-region>
Loki 的对象存储与索引完整配置可参考 Loki Storage 官方文档。
Alloy 以 DaemonSet 部署在每个节点上,通过 Kubernetes 服务发现定位本节点的 Pod,再通过 Kubernetes API 拉取容器日志(无需挂载节点日志目录、无需 root 权限),附加 namespace、pod、container 标签后推送到 Loki。该方式需要为 Alloy 的 ServiceAccount 授予 pods/log 的 get/list/watch 权限。核心配置由几个组件串联而成:
Alloy 采集 Kubernetes 容器日志的完整配置可参考 Grafana Alloy 官方文档。
5.3 链路支柱:Grafana Tempo 与 metrics_generator
Grafana Tempo 通过 OTLP(HTTP 4318)接收链路数据,写前日志写入 Amazon EBS,定期刷新到 Amazon S3。Tempo 的一项重要能力是内置的 metrics_generator:它从 span 数据中提取服务间调用关系(service-graphs)和每个 span 的延迟分布(span-metrics),生成 RED 指标后通过 remote_write 写入 VictoriaMetrics。
# distributor:显式开启 OTLP HTTP 接收器(Tempo 默认不开启,不写则不监听 4318、收不到 trace)
distributor:
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318
# metrics_generator:从 span 生成 RED 指标与服务拓扑,remote_write 到 VictoriaMetrics
metrics_generator:
storage:
remote_write:
- url: http://vminsert.observability.svc.cluster.local:8480/insert/0/prometheus/api/v1/write
overrides:
defaults:
metrics_generator:
processors: [service-graphs, span-metrics]
metrics_generator 的完整配置项可参考 Grafana Tempo 官方文档。
这意味着,即使应用本身没有专门埋点暴露服务调用指标,Grafana Tempo 也能从已有的 trace 数据反向计算出每个服务的调用速率、错误率、延迟分布以及服务间的调用拓扑。启用 span-metrics 与 service-graphs 两个 processor 后,生成的指标分别以 traces_spanmetrics_* 与 traces_service_graph_* 命名,可在 VictoriaMetrics 中直接用 PromQL 查询。
5.4 可视化环节:Grafana 与三向关联
三大支柱的数据就绪后,最后一步是在 Grafana 中配置三个数据源之间的关联,把“一键跳转”真正打通。三处关联配置的核心片段如下:
# VictoriaMetrics 数据源(指标,供 trace → metrics / 服务拓扑引用)
- name: VictoriaMetrics
type: prometheus # VictoriaMetrics 兼容 Prometheus 查询 API
uid: victoriametrics
url: http://vmselect.observability.svc.cluster.local:8481/select/0/prometheus
# Loki 数据源:Log → Trace
- name: Loki
type: loki
uid: loki
jsonData:
derivedFields:
- name: TraceID
matcherRegex: "\\[([0-9a-f]{32})-[0-9a-f]{16}\\]"
url: "$${__value.raw}"
datasourceUid: tempo
# Tempo 数据源:Trace → Log / Metrics / 服务拓扑
- name: Tempo
type: tempo
uid: tempo
jsonData:
tracesToLogsV2:
datasourceUid: loki
filterByTraceID: true
tracesToMetrics:
datasourceUid: victoriametrics
serviceMap:
datasourceUid: victoriametrics
数据源关联的完整配置可参考 Grafana 官方文档的 Tempo 数据源、Loki 数据源两节。
关联机制由三处配置实现:
- Log → Trace:Loki 数据源的
derivedFields通过正则匹配日志中的 trace_id,生成可点击链接跳转到 Grafana Tempo; - Trace → Log:Grafana Tempo 数据源的
tracesToLogsV2在查看 trace 详情时按 trace_id 过滤关联日志; - Trace → Metrics / 服务拓扑:
tracesToMetrics与serviceMap利用metrics_generator生成的指标展示服务拓扑。
需要注意 derivedFields 的正则要与应用实际的日志格式匹配:探针将 trace_id 注入日志的格式因语言与日志框架而异——有的是 trace_id=xxx 的键值对,有的是方括号形式(如 [<32位traceId>-<16位spanId>])。若正则与实际格式不符,日志中不会出现可点击的 TraceID 链接,且没有任何报错提示。
六、应用零代码接入:OpenTelemetry Operator 自动注入
对于运行在 Amazon EKS 上的存量微服务,接入可观测性最省力的方式是使用 OpenTelemetry Operator 的自动注入能力。它的核心价值是零改动:不修改业务代码、不重新构建镜像,只需为工作负载添加一个注解,Operator 便会通过准入 Webhook 在 Pod 启动时注入一个 init-container,将对应语言的 OpenTelemetry 探针挂载进应用容器。
自动注入依赖集群中的两个组件:OpenTelemetry Operator(执行注入的控制器)和它所依赖的 cert-manager(为 Operator 的准入 Webhook 签发证书)。二者按各自官方文档安装即可(cert-manager 安装、OpenTelemetry Operator),本文不展开。两者就绪后,定义一个 Instrumentation 自定义资源,描述注入哪种语言的探针以及遥测数据发往何处。下面以 Java 为例(其他语言仅探针配置段不同,见本章末的多语言对照),将探针的导出端点指向已部署的 Grafana Tempo,并开启 logback MDC 注入,使应用日志自动携带 trace_id:
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: java-instrumentation
namespace: <your-app-namespace>
spec:
exporter:
endpoint: http://tempo.observability.svc.cluster.local:4318 # 4318 = OTLP http/protobuf
propagators: [tracecontext, baggage]
sampler:
type: parentbased_traceidratio
argument: "1.0" # 演示用 100%;生产建议 0.1,控制 trace 量
java:
env:
- name: OTEL_EXPORTER_OTLP_PROTOCOL # 显式声明,避免旧版探针默认 gRPC 对 4318 发错协议
value: http/protobuf
- name: OTEL_TRACES_EXPORTER
value: otlp
- name: OTEL_METRICS_EXPORTER # 指标走 vmagent 抓取,这里关闭
value: none
- name: OTEL_LOGS_EXPORTER # 日志走 stdout + Alloy,这里关闭
value: none
- name: OTEL_INSTRUMENTATION_LOGBACK_MDC_ENABLED
value: "true" # 把 trace_id 注入应用日志,供 Loki 关联
随后,只需在工作负载的 Pod 模板上添加注解即可触发注入。注解值 "true" 表示使用同命名空间下的 Instrumentation;若 Instrumentation 位于其他命名空间,注解值需写成 <namespace>/<name>:
spec:
template:
metadata:
annotations:
instrumentation.opentelemetry.io/inject-java: "true"
Instrumentation 资源的完整字段与各语言的注入方式可参考 OpenTelemetry Operator 自动注入官方文档。
注入完成后,可通过 kubectl describe pod 看到名称以 opentelemetry-auto-instrumentation 开头的 init-container,确认探针已生效。应用产生的 trace 随即直接发往 Grafana Tempo,无需在代码中编写任何 OpenTelemetry SDK 初始化逻辑。
6.1 规模化与多语言
这种基于注解的接入方式天然适合规模化:注入逻辑由集群中的 OpenTelemetry Operator 统一承担,应用侧只需声明注解。当应用数量较多时,可以通过脚本批量为现有工作负载打注解,或在 GitOps、Helm 模板中统一声明,新上线的服务即可自动接入。需要说明的是,注解在 Pod 创建时由 Webhook 处理,已有 Pod 需滚动重启才会生效。
OpenTelemetry Operator 同样支持 Python、Node.js、.NET 等多种语言,机制与 Java 完全一致,仅注解与探针配置段不同:
| 语言 | 注入注解 |
| Java | instrumentation.opentelemetry.io/inject-java: "true" |
| Python | instrumentation.opentelemetry.io/inject-python: "true" |
| Node.js | instrumentation.opentelemetry.io/inject-nodejs: "true" |
| .NET | instrumentation.opentelemetry.io/inject-dotnet: "true" |
七、效果验证
本文的演示应用采用 Spring PetClinic 微服务版——一个基于 Spring Boot 的真实 Java 微服务应用。
链路追踪展示了完整的跨服务调用链:由 api-gateway 调用 customers-service、vets-service、visits-service 等后端服务。通过 api-gateway 触发一批请求(例如 /api/customer/owners/{ownerId} 经网关路由到 customers-service、/api/visit/owners/{ownerId}/pets/{petId}/visits 路由到 visits-service、/api/vet/vets 路由到 vets-service),即可在网关与各后端服务间形成完整调用链,随后在 Grafana 中验证三类数据的采集与关联。
在实测环境中,单条此类 trace 包含约 19 个 span,OpenTelemetry 探针自动追踪到了 HTTP 调用、Spring MVC 处理、Hibernate 会话乃至底层 SQL 查询——全部无需修改业务代码(见图 2)。
[图2 链路追踪] |
日志与链路关联是排障效率的关键。图 3 展示了在 Loki 中查询到的应用日志,由于 OpenTelemetry 探针已将 trace_id 注入日志,Grafana 的 derivedFields 配置将其渲染为可点击链接,点击即可跳转到 Grafana Tempo 查看对应的完整调用链。
[图3 日志查询] |
指标查询方面,在 Grafana 中选择 VictoriaMetrics 数据源,即可用 PromQL 查询应用通过 Spring Boot Actuator 暴露的 Micrometer 指标,例如 http_server_requests_seconds_count(HTTP 请求计数)或 jvm_memory_used_bytes(JVM 内存使用),以及 vmagent 采集的节点级 cAdvisor 容器指标。
服务拓扑由 metrics_generator 自动生成。图 4 展示了 Grafana 中的服务拓扑图,节点为各微服务,边表示服务间调用关系,并标注请求速率与延迟。这张图的数据完全来自对 trace 的反向计算,应用本身并未埋点暴露这些指标。
[图4 服务拓扑] |
数据落盘 Amazon S3。日志与链路的长期数据最终都写入 Amazon S3,下图可见数据已实际写入。图中 fake/ 是 Loki 在单租户模式(auth_enabled: false)下使用的默认租户目录,并非模拟数据;其下大量以分片 ID 命名的子目录,是 Loki 存放日志块(chunk)的结构——每个子目录里的文件即压缩后的日志块,印证了日志正被持续写入对象存储。
[图5 S3 日志存储] |
八、总结
本文完整实践了在 Amazon EKS 上开源自建可观测性数据栈的方案:以 VictoriaMetrics、Grafana Loki 与 Grafana Tempo 分别承载指标、日志与链路三大支柱,由 Grafana Alloy 与 vmagent 完成采集,日志与链路的长期数据存入 Amazon S3、指标数据存放在 Amazon EBS gp3,以充分利用云存储的高性价比优势;借助 OpenTelemetry Operator 的自动注入能力,存量微服务无需改动任何代码即可接入;最后通过 Grafana 的数据源关联配置打通 trace、log、metrics 的一键跳转,并由 Tempo 的 metrics_generator 自动生成服务拓扑,显著提升故障排查效率。
由于篇幅有限,各组件的高可用部署(如 Loki 的 SimpleScalable 模式、多副本与跨可用区调度)、生产环境的采样率与数据保留策略、以及多租户等高级特性未能展开,读者可参考文中引用的官方文档,在演示环境中验证三大支柱的关联效果后,再结合自身规模逐项调整。
此外,亚马逊云科技也提供了托管的可观测性服务,免去存储与组件的运维工作,并可与其他云服务原生集成。两条路径并非互斥,也可组合使用:例如借助采集器将链路数据同时发送到亚马逊云科技托管服务与自建的 Grafana Tempo,便于对比评估与平滑过渡。读者可结合团队能力与实际场景,选择或组合使用。
➡️ 下一步行动:
相关产品:
- Amazon S3 — 适用于 AI、分析和存档的几乎无限的安全对象存储
- Amazon EKS — 托管式 Kubernetes 服务
- Amazon EBS — 高性能数据块存储
- Amazon IAM — 身份管理和访问权限
- Amazon OpenSearch — 搜索和分析引擎
相关文章:
- 星合互娱借助 AWS DevOps Agent 构建多游戏智能运维体系
- Habby 游戏借助 AWS DevOps Agent 实现智能运维最佳实践
- Zenjoy 基于 Amazon Bedrock 和 EKS 构建 AIOps Agent:打通 Prometheus、ES 与夜莺的智能化告警实战
- 基于 AWS DevOps Agent 构建 AI 驱动的运维分析系统
- 在 Amazon EKS 上使用 NVIDIA GPU Operator 管理自定义 GPU 驱动与 CUDA 工作负载
九、参考资料
- Amazon EKS 官方文档
- OpenTelemetry Operator
- Grafana Tempo 文档
- Grafana Loki 文档
- Grafana Alloy 文档
- VictoriaMetrics 文档
- Spring PetClinic 微服务版
- Grafana 官方文档
*前述特定亚马逊云科技生成式人工智能相关的服务目前在亚马逊云科技海外区域可用。亚马逊云科技中国区域相关云服务由西云数据和光环新网运营,具体信息以中国区域官网为准。
本篇作者
AWS 架构师中心:云端创新的引领者探索 AWS 架构师中心,获取经实战验证的最佳实践与架构指南,助您高效构建安全、可靠的云上应用 |
![]() |

