访问日志提供了一种从单个工作负载实例的角度监控和理解行为的方法,同样访问日志是我们在生产环境中必不可少的一种监控手段,Istio 通过 Envoy 来提供访问日志功能,Envoy Proxy 打印访问信息到标准输出,Envoy 容器的标准输出能够通过 kubectl logs 命令打印出来。
Istio 能够以一组可配置的格式为服务流量生成访问日志,使运维人员可以完全控制日志记录的方式、内容、时间和地点。下面是一个典型的 Istio 访问日志示例:
[2023-12-04T06:17:42.719Z] "GET /productpage HTTP/1.1" 200 - via_upstream - "-" 0 5289 23 22 "10.244.0.0" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" "f3a98cd1-6970-42c0-9c86-d179b93aa779" "192.168.0.100:31896" "10.244.1.254:9080" inbound|9080|| 127.0.0.6:45629 10.244.1.254:9080 10.244.0.0:0 outbound_.9080_._.productpage.default.svc.cluster.local default
在现在的 Telemetry V2 版本的架构中,访问日志直接通过服务网格的数据平面 Envoy 上生成并上报给日志后端。根据后端日志采集方式的不同,会有不同的通道和方式。Envoy 可以通过控制台或者文件输出,由各种日志代理采集,也可以通过 gRPC 协议直接上报日志给标准的访问日志服务 ALS(Envoy Access Log Service),比如 Skywalking 就支持,一般流程如下所示
- Envoy 根据服务网格配置提取应用的访问信息。
- 上报访问日志,比如通过 gRPC 协议上报给 ALS 服务。
- ALS 服务 对接后端,将日志写到 Elasticsearch、Kafka 等后端服务中。
- 通过 Kibanba、Grafana 等工具从后端服务检索日志。
开启 Envoy 访问日志
同样的方式在 Istio 中我们可以通过 MeshConfig 和 Telemetry API 的方式来启用访问日志。如果想通过 MeshConfig 方式来配置,需要在安装配置中添加以下字段(默认已经配置了):
spec:
meshConfig:
accessLogFile: /dev/stdout
或者,在原来的 istioctl install 命令中添加相同的设置,例如:
istioctl install <flags-you-used-to-install-Istio> --set meshConfig.accessLogFile=/dev/stdout
此外还可以通过设置 accessLogEncoding 为 JSON 或 TEXT 来配置日志的格式。另外还可以设置 accessLogFormat 来自定义访问日志的格式,如果没有指定 accessLogFormat 的话 Istio 将使用以下默认的访问日志格式:
[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %RESPONSE_CODE_DETAILS% %CONNECTION_TERMINATION_DETAILS%
\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\" %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\"
\"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\" %UPSTREAM_CLUSTER% %UPSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_REMOTE_ADDRESS% %REQUESTED_SERVER_NAME% %ROUTE_NAME%\n
当然我们还是强烈推荐使用 Telemetry API 来开启或关闭访问日志,如下所示:
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: mesh-default
namespace: istio-system
spec:
accessLogging:
- providers:
- name: envoy
上面的示例使用默认的 envoy 访问日志提供程序,当然我们也可以应用于单独的命名空间或单独的工作负载,以在细粒度级别控制日志记录。
Loki
接下来我们来将访问日志发送到 Grafana Loki 进行统一的日志管理,Loki 是一个水平可扩展、高可用的多租户日志聚合系统。
首先我们需要先确保 Loki 已经安装,我们这里同样只是为了测试,直接使用下面的方式安装即可,如果在生产环境中使用,则需要参考官方文档进行分布式部署。
kubectl apply -f samples/addons/loki.yaml -n istio-system
由于 Istio 默认并没有直接支持 Loki 这个 Provider,我们可以查看 MeshConfig 的 ExtensionProvider 字段,可以看到 Istio 默认支持的 Provider 有:
字段 | 类型 | 描述 | 是否必需 |
name | string | 必填。用于唯一标识扩展提供商的名称。 | 否 |
envoyExtAuthzHttp | EnvoyExternalAuthorizationHttpProvider (oneof) | 配置实现了 Envoy ext_authz 过滤器授权检查服务的外部授权器,使用 HTTP API。 | 否 |
envoyExtAuthzGrpc | EnvoyExternalAuthorizationGrpcProvider (oneof) | 配置实现了 Envoy ext_authz 过滤器授权检查服务的外部授权器,使用 gRPC API。 | 否 |
zipkin | ZipkinTracingProvider (oneof) | 配置使用 Zipkin API 的跟踪提供商。 | 否 |
datadog | DatadogTracingProvider (oneof) | 配置 Datadog 跟踪提供商。 | 否 |
stackdriver | StackdriverProvider (oneof) | 配置 Stackdriver 提供商。 | 否 |
skywalking | SkyWalkingTracingProvider (oneof) | 配置 Apache SkyWalking 提供商。 | 否 |
opentelemetry | OpenTelemetryTracingProvider (oneof) | 配置 OpenTelemetry 跟踪提供商。 | 否 |
prometheus | PrometheusMetricsProvider (oneof) | 配置 Prometheus 指标提供商。 | 否 |
envoyFileAccessLog | EnvoyFileAccessLogProvider (oneof) | 配置 Envoy 文件访问日志提供商。 | 否 |
envoyHttpAls | EnvoyHttpGrpcV3LogProvider (oneof) | 针对 HTTP 流量配置 Envoy 访问日志服务提供商。 | 否 |
envoyTcpAls | EnvoyTcpGrpcV3LogProvider (oneof) | 针对 TCP 流量配置 Envoy 访问日志服务提供商。 | 否 |
envoyOtelAls | EnvoyOpenTelemetryLogProvider (oneof) | 配置 Envoy Open Telemetry 访问日志服务提供商。 | 否 |
没有 Loki 这个 Provider,那么我们需要怎样才能将日志发送到 Loki 中呢?这里我们可以使用 OpenTelemetry 来收集日志,然后再通过 OpenTelemetry Collector 来将日志发送到 Loki 中。
OpenTelemetry
OpenTelemetry(简称 OTel) 是一个开源的可观测框架,用于生成、收集和描述应用程序的观测数据。它提供了一组 API、库、Agent 和 Collector,用于捕获分布式跟踪和度量数据,并将其发送到分析软件、存储库或其他服务,OTel 的目标是提供一套标准化、与厂商无关的 SDK、API 和工具集,用于将数据摄取、转换和发送到可观测性后端(开源或商业厂商)。
OpenTelemetry Collector
OpenTelemetry Collector 提供了一个与厂商无关的实现方式,用于接收、处理和导出遥测数据,它消除了运行、操作和维护多个代理/收集器的需求。
事实上收集器也并不是必需的,有的时候我们可以直接将遥测数据发送到外部的可视化工具中,比如 Jaeger、Zipkin 等等,但是这样的话我们就需要在每个应用中都进行配置,这样的话就会导致配置非常繁琐,而且也不利于统一管理,所以这里我们就可以使用 OpenTelemetry Collector 来解决这个问题。
而且 OpenTelemetry Collector 本身部署起来也非常灵活,可以将其部署为代理或网关。区别在于作为代理时,收集器实例与应用程序在同一主机上运行(sidecar 容器、daemonset 等)。此外一个或多个收集器实例也可以作为独立服务以每个集群、数据中心和地区的网关形式运行。
一般来说建议新应用选择代理部署,现有应用选择网关部署的方式,如果是 Kubernetes 环境,当然更建议部署为守护进程(代理模式)的方式。
收集器由四个组件组成,通过管道(Pipeline)进行启用:
- 接收器(Receiver)将数据发送到收集器中,可以通过推送或拉取方式发送
- 处理器(Processor)决定如何处理接收到的数据
- 导出器(Exporter)决定将数据发送到哪里,可以通过拉取或推送方式完成,上面代码中的 OTLPTraceExporter 就是一个导出器
- 连接器(Connectors):连接器既是输出者又是接收者。连接器连接两个管道:它作为一个管道末端的导出器消耗数据,并作为另一个管道开始处的接收器发出数据。它可以消耗和发出相同数据类型或不同数据类型的数据。
OTel Collector
当然我们也可以基于社区的组件进行自定义,以增强和扩展收集器管道。例如我们可以创建一个专用的导出器来接收并摄取指标、追踪和日志。
OpenTelemetry Collector 部署
在了解了 OpenTelemetry 的相关概念后,接下来我们需要部署 OpenTelemetry Collector,同样我们直接使用 Istio 提供的 samples 中的配置即可:
kubectl apply -f samples/open-telemetry/loki/otel.yaml -n istio-system
该命令会部署一个 OpenTelemetry 采集器,其中比较重要的是该采集器的配置:
apiVersion: v1
kind: ConfigMap
metadata:
name: opentelemetry-collector-conf
labels:
app: opentelemetry-collector
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]
上面的配置中我们主要关注 exporters 字段,其中 loki 就是我们要将日志发送到的 Loki 服务,endpoint 字段指定了 Loki 服务的地址,这里我们直接使用 Loki 的 Service 名称即可,因为 Loki 服务暴露了 3100 端口,所以我们可以直接使用 http://loki.istio-system.svc:3100/loki/api/v1/push 来访问 Loki 服务。而 receivers 字段表示接收器,这里配置的是 otlp,表示使用 OpenTelemetry 的 OTLP 标准协议来接收数据。processors 字段表示处理器,这里我们使用了 attributes 处理器,它的作用是向日志中添加一些自定义的属性,比如 pod、namespace、cluster、mesh 等等,这样我们在 Loki 中就可以通过这些属性来进行检索了。最后需要注意的是必须要在 service.pipelines 中明确声明要启用的管道以及管道中使用的接收器、处理器和导出器,否则不会生效。
现在在 Istio 根命名空间中包含如下的一些工作负载:
$ kubectl get pods -n istio-system
NAME READY STATUS RESTARTS AGE
grafana-5f9b8c6c5d-jv65v 1/1 Running 16 (5h12m ago) 32d
istio-egressgateway-556f6f58f4-mqp5z 1/1 Running 0 4h26m
istio-ingressgateway-9c8b9b586-p2w67 1/1 Running 0 4h26m
istiod-644f5d55fc-dlktv 1/1 Running 0 4h26m
jaeger-db6bdfcb4-9s8lr 1/1 Running 0 3h47m
kiali-7c9d5f9f96-cp4mb 1/1 Running 18 (5h12m ago) 32d
loki-0 1/1 Running 0 32m
opentelemetry-collector-5ccc9c9c55-msg5x 1/1 Running 0 60s
prometheus-5d5d6d6fc-lfz87 2/2 Running 2 (5h12m ago) 2d19h
接下来我们就需要在 Istio 中添加一个 OpenTelemetry 访问日志服务的 Provider,添加如下配置:
# iop.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
profile: demo
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)%"
在上面的配置中我们添加了一个名为 otel 的 Provider,该 Provider 是一个 envoyOtelAls,表示使用 OpenTelemetry 的访问日志服务,对应的后端服务为 opentelemetry-collector.istio-system.svc.cluster.local,端口为 4317,这里我们直接使用 OpenTelemetry Collector 的 Service 名称即可。最后我们还配置了 logFormat,表示日志的格式,这里我们添加了一些自定义的属性,比如 pod、namespace、cluster、mesh 等等,然后在 OpenTelemetry 采集器中会把这些属性转换为 Loki 的标签,这样我们在 Loki 中就可以通过这些属性来进行检索了。
直接使用 istioctl 命令来安装配置该对象即可:
istioctl install -f iop.yaml -y
到这里我们的准备工作就完成了。
使用 Telemetry API 配置访问日志
接下来我们只需要通过 Telemetry API 来启用上面我们配置的日志 Provider 就可以开始收集日志了,如下所示:
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: mesh-logging-default
namespace: istio-system
spec:
accessLogging:
- providers:
- name: otel
应用该资源对象后,整个服务网格的日志就都会被上报到 OTel 采集器,然后在 Loki 中就可以看到日志了。
这里我们直接打开 Grafana 的 Loki Dashboard 即可:
istioctl dashboard grafana
首先要在 Grafana 中添加 Loki 数据源:
Loki 数据源
然后接下来我们去访问 Productpage 应用产生一些日志数据,再切换回到 Grafana 中,切换到 Explore 页面,然后选择 Loki 数据源,就可以看到 Loki 中的日志了:
日志查询
同样的我们还可以使用 Telemetry API 来做一些更加细粒度的配置。
比如可以使用以下配置禁用 sleep 服务的访问日志:
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: disable-sleep-logging
namespace: default
spec:
selector:
matchLabels:
app: sleep
accessLogging:
- providers:
- name: otel
disabled: true
还可以使用 match 字段来指定要过滤的流量,比如可以使用以下配置禁用 httpbin 服务的入站访问日志:
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: disable-httpbin-logging
spec:
selector:
matchLabels:
app: httpbin
accessLogging:
- providers:
- name: otel
match:
mode: SERVER # 入站模式
disabled: true # 禁用
此外我们可以通过 CEL 表达式过滤访问日志。只有响应码大于等于 500 时,才会显示访问日志,如下所示:
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: filter-sleep-logging
spec:
selector:
matchLabels:
app: sleep
accessLogging:
- providers:
- name: otel
filter:
expression: response.code >= 500
比如只有响应码大于等于 400 或请求转到 BlackHoleCluster 或 PassthroughCluster 时,才显示访问日志:
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: default-exception-logging
namespace: istio-system
spec:
accessLogging:
- providers:
- name: otel
filter:
expression: "response.code >= 400 || xds.cluster_name == 'BlackHoleCluster' || xds.cluster_name == 'PassthroughCluster' "
参考文档
- https://istio.io/latest/docs/tasks/observability/。
- https://opentelemetry.io/docs/。