Istio OpenTelemetry ALS 访问日志接入 Loki:Envoy Access Log 经 OTLP 写入 Grafana 的完整实践
【免费下载链接】istioConnect, secure, control, and observe services.项目地址: https://gitcode.com/GitHub_Trending/is/istio
导读
本文基于 Istio 官方仓库 samples/open-telemetry/loki 示例,完整演示如何把 Istio 数据面(Envoy Sidecar)产生的访问日志通过OpenTelemetry ALS(Access Log Service,gRPC 访问日志服务)协议实时发送给 OpenTelemetry Collector,再由 Collector 转发写入Grafana Loki,最终在 Grafana 中实现日志检索。读完本文,你将掌握 extensionProviders(envoyOtelAls)声明、otel-collector 管道配置、Telemetry 访问日志策略下发,以及完整的验证与清理流程,可直接在本仓库基础上复制运行。
数据链路概览:
Envoy 访问日志 --OTLP/gRPC(4317)--> otel-collector --loki exporter--> Loki(3100) --数据源--> Grafana
一、原理先行:Istio 的 OpenTelemetry ALS 是什么
Envoy 除了能把访问日志写到本地文件 /dev/stdout,还内置了 gRPC 类型的访问日志服务(ALS):sidecar 将每条请求日志打包后按流式 gRPC 推送到外部 collector。Istio 对 ALS 的支持有两类 provider:
envoyHttpAls/envoyTcpAls:走 Envoy 原生 HTTP/TCP gRPC ALS 协议;envoyOtelAls:走 Envoy 的 OpenTelemetry 访问日志扩展(即envoy.access_loggers.open_telemetry),日志被编码为 OTLP Logs 后推送,可与生态中的 OTel Collector 无缝衔接。
在 Istio 源码 pilot/pkg/model/telemetry_logging.go 中可以看到该 provider 被处理的关键路径:
- 常量
OtelEnvoyALSName = "envoy.access_loggers.open_telemetry"与默认日志名OtelEnvoyAccessLogFriendlyName = "otel_envoy_accesslog"(telemetry_logging.go 定义); openTelemetryLog()(telemetry_logging.go)根据 MeshConfig 中的 provider 定义生成 Envoy 的 OpenTelemetryAccessLogConfig,其中通过clusterLookupFn(即同文件LookupCluster())把service:port解析为 Envoy 可用的上游 cluster(BuildSubsetKey构建 outbound cluster);buildOpenTelemetryAccessLogConfig()中DisableBuiltinLabels: true,并把logFormat.labels转换为 OTLPKeyValueList写入日志 Attributes;FilterStateObjectsToLog默认携带envoyWasmStateToLog中的上游/下游对端元数据(upstream_peer、downstream_peer及 wasm 相关 key),这正是 mTLS 场景下获取对端 workload 信息的基础。
因此在正式动手前,可以把“每个提供 OpenTelemetry 日志接入的第三方后端”理解成:先用 MeshConfig 声明一个被 Istio 认可的 extensionProvider,再用 Telemetry API 让某个作用域下的访问日志引用它。
二、安装 Istio:通过 IstioOperator 声明 envoyOtelAls Provider
示例使用官方提供的 IOP(IstioOperator)清单安装 Istio,并把 OpenTelemetry ALS provider 写入网格配置:
istioctl install -f iop.yaml -yiop.yaml 完整内容如下:
apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: meshConfig: extensionProviders: - name: otel envoyOtelAls: service: opentelemetry-collector.istio-system.svc.cluster.local port: 4317 logFormat: labels: pod: "%ENVIRONMENT(POD_NAME)%" namespace: "%ENVIRONMENT(POD_NAMESPACE)%" cluster: "%ENVIRONMENT(ISTIO_META_CLUSTER_ID)%" mesh: "%ENVIRONMENT(ISTIO_META_MESH_ID)%"逐字段说明:
| 字段 | 取值 | 作用 |
|---|---|---|
extensionProviders[].name | otel | provider 的全局唯一名称,Telemetry 资源通过该名称引用 |
envoyOtelAls.service | opentelemetry-collector.istio-system.svc.cluster.local | 接收 OTLP 日志的后端服务全限定名。Istio 侧LookupCluster()会先在服务注册表(ServiceIndex)中解析该名字,找不到对应 Service 时 ALS 不会被正确配置,并会在 pilot 日志中记录could not find cluster ...错误(见 telemetry_logging.go) |
envoyOtelAls.port | 4317 | 后端 OTLP gRPC 接收端口 |
logFormat.labels | 一组静态键 + Envoy 命令操作符 | 每个 key 都会成为写入 OTLP Attributes 的标签;value 使用 Envoy 的%ENVIRONMENT(VAR)%命令操作符读取 istio-proxy 容器环境变量 |
关于上面 4 个 label 的环境变量来源:
POD_NAME、POD_NAMESPACE:sidecar 注入器为 istio-proxy 容器注入的运行时信息;ISTIO_META_CLUSTER_ID、ISTIO_META_MESH_ID:Istio 注入时写入 istio-proxy 的网格元数据环境变量,分别标识集群与网格。
这些 label 会在后续 collector 配置中被挑选为 Loki 的流标签(见第四节),实现“按 Pod / 命名空间 / 集群 / 网格维度切分与检索日志”。同类 provider 的合法性校验(如 service 是否可解析、字段约束)由 pilot 在推送 xDS 前通过 pkg/config/validation/agent/extensionprovider.go 中的ValidateExtensionProviderEnvoyOtelAls()完成。
三、Setup Loki:安装 Loki 后端
仓库在 addons 目录提供了可直接使用的 Loki 清单(由官方 Loki Helm Chart 渲染而来,版本 3.6.11):
kubectl apply -f samples/addons/loki.yaml -n istio-system对 samples/addons/loki.yaml 中与本实验强相关的要点:
- 以单实例 StatefulSet模式运行(
-target=all),镜像docker.io/grafana/loki:3.6.11; - 对外 Service 名为
loki,监听:- HTTP
3100(对外提供/loki/api/v1/push写入接口与查询接口,Service 端口名http-metrics); - gRPC
9095(内部组件通信);
- HTTP
- 数据以
filesystem对象存储写入/var/loki,并通过 volumeClaimTemplates 申请 10Gi PVC; - 额外附带
loki-memberlist(无头服务,7946)与loki-headless等配套 Service,便于后续扩展为分布式模式。
安装后,collector 将通过http://loki.istio-system.svc:3100/loki/api/v1/push推送日志(详见下一节配置)。
四、Setup otel-collector:部署 OpenTelemetry Collector 并串联到 Loki
创建一个otel-collector后端,配置了 OTLP 接收器、属性处理与 Loki 导出器:
kubectl apply -f otel.yaml -n istio-systemsamples/open-telemetry/loki/otel.yaml 在单个文件中声明了 ConfigMap、Service、Deployment 三类资源。先看核心的 collector 配置(ConfigMap 的data.opentelemetry-collector-config):
receivers: otlp: protocols: grpc: http: processors: batch: attributes: actions: - action: insert key: loki.attribute.labels value: pod, namespace,cluster,mesh exporters: loki: endpoint: "http://loki.istio-system.svc:3100/loki/api/v1/push" logging: loglevel: debug extensions: health_check: service: extensions: - health_check pipelines: logs: receivers: [otlp] processors: [attributes] exporters: [loki, logging]配置语义拆解:
- receivers.otlp:同时开启 gRPC(4317)与 HTTP 两种 OTLP 协议接收,Envoy 的 ALS 通过 gRPC 4317 上报;
- processors.attributes:通过
action: insert插入特殊 keyloki.attribute.labels,其值为逗号分隔的pod, namespace,cluster,mesh。这是 OTel Loki exporter 的约定:只有被列进loki.attribute.labels的日志属性,才会被提升为 Loki 的 label(索引字段),其余属性只作为非索引的日志内容。这里列出的四个 key 与第二节 iop.yaml 中logFormat.labels的四个 key(pod/namespace/cluster/mesh)一一对应,从而让每条访问日志都携带可索引的 Pod、命名空间、集群与网格维度; - exporters.loki:指向 Loki 单实例的 HTTP Push API
http://loki.istio-system.svc:3100/loki/api/v1/push; - exporters.logging:
loglevel: debug表示同时把每条日志原样打印到 collector 的 stdout,便于无需接入 Grafana 时快速排障(这也是 README 中“查看 ALS 输出”的直接手段); - service.pipelines.logs:把
otlp → attributes → loki/logging串成一条完整 logs 管道;health_check扩展为存活探针提供端点。
Service 与 Deployment 部分的关键点:
spec: ports: - name: grpc-opencensus port: 55678 - name: grpc-otlp # Default endpoint for OpenTelemetry receiver. port: 4317 selector: app: opentelemetry-collectorspec: template: metadata: labels: app: opentelemetry-collector sidecar.istio.io/inject: "false" # do not inject spec: containers: - command: - "/otelcol-contrib" - "--config=/conf/opentelemetry-collector-config.yaml" env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace image: docker.io/otel/opentelemetry-collector-contrib:0.73.0- Service 暴露
4317(OTLP gRPC)与55678两个端口;其中grpc-otlp: 4317正是 iop.yaml 中envoyOtelAls.port指向的接收端点; - Deployment 镜像使用
otel/opentelemetry-collector-contrib:0.73.0(contrib 发行版才内置 loki exporter 等丰富组件); - 通过
sidecar.istio.io/inject: "false"明确禁止给 collector 自身注入 sidecar,避免“访问日志后端自己也在被采集”的循环与干扰; - 通过 fieldRef 注入
POD_NAME、POD_NAMESPACE环境变量;配置以 ConfigMap 卷挂载到/conf/opentelemetry-collector-config.yaml。
补充:仓库中的姊妹示例 samples/open-telemetry/als/README.md 演示了最小化的纯 stdout 版本——collector 只包含
otlp → batch → logging管道,不接 Loki;当你想先验证 ALS 通道本身是否打通时,可先跑那个精简版。
五、Apply Telemetry API:把访问日志路由到 otel Provider
Istio 的访问日志通过telemetry.istio.io/v1的Telemetry资源按作用域(mesh / namespace / workload)启用。示例添加一条全局策略,让网格内所有 sidecar 把访问日志发送到前面声明的otelprovider:
kubectl apply -f telemetry.yamltelemetry.yaml 完整内容:
apiVersion: telemetry.istio.io/v1 kind: Telemetry metadata: name: mesh-logging namespace: istio-system spec: accessLogging: - providers: - name: otel要点:
- 资源位于istio-system(根命名空间)且未限定
selector,因此这是一条全网格生效的访问日志策略; spec.accessLogging[].providers[].name: otel引用的正是 iop.yaml 中注册的 extensionProvider 名称;Istio 生成 xDS 时,会按 pilot/pkg/model/telemetry_logging.go 中telemetryAccessLog()的 switch 逻辑把该 provider 展开为 Envoy 的envoy.access_loggers.open_telemetry过滤器,填入 cluster(collector 服务)与 logName(默认otel_envoy_accesslog)等信息。
六、验证 ALS 输出:发起流量并观察日志
6.1 启动示例服务
按 httpbin 示例说明 启动httpbin与fortio两个服务。若命名空间已启用自动注入,直接 apply 清单即可:
kubectl apply -f httpbin.yaml # 位于 samples/httpbin/ kubectl apply -f samples/httpbin/sample-client/fortio-deploy.yaml否则需先为 istio-proxy 手动注入,例如:
kubectl apply -f <(istioctl kube-inject -f httpbin.yaml) kubectl apply -f <(istioctl kube-inject -f samples/httpbin/sample-client/fortio-deploy.yaml)6.2 从 fortio 请求 httpbin
kubectl exec -it deploy/fortio -- fortio curl httpbin:8000/ip此时该请求经过 fortio 与 httpbin 两侧的 Envoy,两侧的访问日志都会通过 ALS 上报给 otel-collector。
6.3 查看 collector 的 stdout 输出
由于 collector 配置了logging/loglevel: debug导出器,日志会同时打印到其标准输出:
kubectl logs -l app=opentelemetry-collector -n istio-system --tail=-1正常时你会看到类似 OTLP LogRecord 的结构化输出,其中包含请求方法、路径、响应码以及前面配置的pod、namespace、cluster、mesh属性。若看不到任何日志,可按以下顺序排查:
- 确认
istioctl install -f iop.yaml后 provider 已注册:istioctl manifest generate -f iop.yaml或查看 istiod 日志; - 确认 Telemetry 资源已生效(
kubectl get telemetry -n istio-system); - 确认 otel-collector 与 Loki 的 Service 均能解析(
kubectl get svc -n istio-system); - 查看 istiod 日志中是否存在
could not find cluster for open telemetry provider类错误——这对应源码中LookupCluster()失败的分支。
6.4 通过 Grafana 查询 Loki
如需在 Grafana 中检索日志,先部署 addons 中的 Grafana:
kubectl apply -f samples/addons/grafana.yaml -n istio-system istioctl dashboard grafana然后在 Grafana 中把 Loki 添加为数据源(地址为http://loki.istio-system.svc:3100),即可使用 LogQL 按pod、namespace等标签过滤、检索这些访问日志(Grafana 与 Loki 数据源的对接用法可查阅 Grafana 官方数据源文档)。
七、清理
按以下顺序删除示例资源并卸载 Istio:
kubectl delete -f otel.yaml -n istio-system kubectl delete telemetry mesh-logging -n istio-system istioctl uninstall --purge -y如需连同 Loki、Grafana 一并清理,可补充:
kubectl delete -f samples/addons/loki.yaml -n istio-system kubectl delete -f samples/addons/grafana.yaml -n istio-system kubectl delete -f httpbin.yaml kubectl delete -f samples/httpbin/sample-client/fortio-deploy.yaml八、小结与仓库导航
本实验展示了 Istio 可观测性中“访问日志外送”的一条标准链路:MeshConfig 声明 extensionProvider(envoyOtelAls)→ Telemetry 按作用域开启 accessLogging → Envoy 以 OTLP/gRPC 上报 → otel-collector 加工 → Loki 存储 → Grafana 查询。其中“按 Pod/命名空间/集群/网格给日志打标签”的做法,对多集群、多租户网格的日志归类与排障尤其实用。
建议结合仓库源码继续深入的关键文件:
- 示例清单:iop.yaml、otel.yaml、telemetry.yaml;
- Loki 后端清单:samples/addons/loki.yaml;
- 流量生成样例:samples/httpbin/README.md 与 samples/httpbin/sample-client/fortio-deploy.yaml;
- 核心实现:pilot/pkg/model/telemetry_logging.go(
openTelemetryLog/buildOpenTelemetryAccessLogConfig/LookupCluster); - Provider 校验:pkg/config/validation/agent/extensionprovider.go(
ValidateExtensionProviderEnvoyOtelAls); - 精简对照版:samples/open-telemetry/als/README.md。
掌握本文链路后,把exporters.loki.endpoint换成其他 Loki 网关、或把 collector 换成自建接收端,即可平滑迁移到生产级日志平台。
【免费下载链接】istioConnect, secure, control, and observe services.项目地址: https://gitcode.com/GitHub_Trending/is/istio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考