Istio 可觀測性之日志,深入了解 Istio 通過 Envoy 來提供訪問日志功能

訪問日志提供了一種從單個(gè)工作負(fù)載實(shí)例的角度監(jiān)控和理解行為的方法,同樣訪問日志是我們在生產(chǎn)環(huán)境中必不可少的一種監(jiān)控手段,Istio 通過 Envoy 來提供訪問日志功能,Envoy Proxy 打印訪問信息到標(biāo)準(zhǔn)輸出,Envoy 容器的標(biāo)準(zhǔn)輸出能夠通過 kubectl logs 命令打印出來。
Istio 能夠以一組可配置的格式為服務(wù)流量生成訪問日志,使運(yùn)維人員可以完全控制日志記錄的方式、內(nèi)容、時(shí)間和地點(diǎn)。下面是一個(gè)典型的 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在現(xiàn)在的 Telemetry V2 版本的架構(gòu)中,訪問日志直接通過服務(wù)網(wǎng)格的數(shù)據(jù)平面 Envoy 上生成并上報(bào)給日志后端。根據(jù)后端日志采集方式的不同,會(huì)有不同的通道和方式。Envoy 可以通過控制臺或者文件輸出,由各種日志代理采集,也可以通過 gRPC 協(xié)議直接上報(bào)日志給標(biāo)準(zhǔn)的訪問日志服務(wù) ALS(Envoy Access Log Service),比如 Skywalking 就支持,一般流程如下所示
- Envoy 根據(jù)服務(wù)網(wǎng)格配置提取應(yīng)用的訪問信息。
 - 上報(bào)訪問日志,比如通過 gRPC 協(xié)議上報(bào)給 ALS 服務(wù)。
 - ALS 服務(wù) 對接后端,將日志寫到 Elasticsearch、Kafka 等后端服務(wù)中。
 
- 通過 Kibanba、Grafana 等工具從后端服務(wù)檢索日志。
 
開啟 Envoy 訪問日志
同樣的方式在 Istio 中我們可以通過 MeshConfig 和 Telemetry API 的方式來啟用訪問日志。如果想通過 MeshConfig 方式來配置,需要在安裝配置中添加以下字段(默認(rèn)已經(jīng)配置了):
spec:
  meshConfig:
    accessLogFile: /dev/stdout或者,在原來的 istioctl install 命令中添加相同的設(shè)置,例如:
istioctl install <flags-you-used-to-install-Istio> --set meshConfig.accessLogFile=/dev/stdout此外還可以通過設(shè)置 accessLogEncoding 為 JSON 或 TEXT 來配置日志的格式。另外還可以設(shè)置 accessLogFormat 來自定義訪問日志的格式,如果沒有指定 accessLogFormat 的話 Istio 將使用以下默認(rèn)的訪問日志格式:
[%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當(dāng)然我們還是強(qiáng)烈推薦使用 Telemetry API 來開啟或關(guān)閉訪問日志,如下所示:
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: mesh-default
  namespace: istio-system
spec:
  accessLogging:
    - providers:
        - name: envoy上面的示例使用默認(rèn)的 envoy 訪問日志提供程序,當(dāng)然我們也可以應(yīng)用于單獨(dú)的命名空間或單獨(dú)的工作負(fù)載,以在細(xì)粒度級別控制日志記錄。
Loki
接下來我們來將訪問日志發(fā)送到 Grafana Loki 進(jìn)行統(tǒng)一的日志管理,Loki 是一個(gè)水平可擴(kuò)展、高可用的多租戶日志聚合系統(tǒng)。
首先我們需要先確保 Loki 已經(jīng)安裝,我們這里同樣只是為了測試,直接使用下面的方式安裝即可,如果在生產(chǎn)環(huán)境中使用,則需要參考官方文檔進(jìn)行分布式部署。
kubectl apply -f samples/addons/loki.yaml -n istio-system由于 Istio 默認(rèn)并沒有直接支持 Loki 這個(gè) Provider,我們可以查看 MeshConfig 的 ExtensionProvider 字段,可以看到 Istio 默認(rèn)支持的 Provider 有:
字段  | 類型  | 描述  | 是否必需  | 
name  | string  | 必填。用于唯一標(biāo)識擴(kuò)展提供商的名稱。  | 否  | 
envoyExtAuthzHttp  | EnvoyExternalAuthorizationHttpProvider (oneof)  | 配置實(shí)現(xiàn)了 Envoy ext_authz 過濾器授權(quán)檢查服務(wù)的外部授權(quán)器,使用 HTTP API。  | 否  | 
envoyExtAuthzGrpc  | EnvoyExternalAuthorizationGrpcProvider (oneof)  | 配置實(shí)現(xiàn)了 Envoy ext_authz 過濾器授權(quán)檢查服務(wù)的外部授權(quán)器,使用 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 指標(biāo)提供商。  | 否  | 
envoyFileAccessLog  | EnvoyFileAccessLogProvider (oneof)  | 配置 Envoy 文件訪問日志提供商。  | 否  | 
envoyHttpAls  | EnvoyHttpGrpcV3LogProvider (oneof)  | 針對 HTTP 流量配置 Envoy 訪問日志服務(wù)提供商。  | 否  | 
envoyTcpAls  | EnvoyTcpGrpcV3LogProvider (oneof)  | 針對 TCP 流量配置 Envoy 訪問日志服務(wù)提供商。  | 否  | 
envoyOtelAls  | EnvoyOpenTelemetryLogProvider (oneof)  | 配置 Envoy Open Telemetry 訪問日志服務(wù)提供商。  | 否  | 
沒有 Loki 這個(gè) Provider,那么我們需要怎樣才能將日志發(fā)送到 Loki 中呢?這里我們可以使用 OpenTelemetry 來收集日志,然后再通過 OpenTelemetry Collector 來將日志發(fā)送到 Loki 中。

OpenTelemetry
OpenTelemetry(簡稱 OTel) 是一個(gè)開源的可觀測框架,用于生成、收集和描述應(yīng)用程序的觀測數(shù)據(jù)。它提供了一組 API、庫、Agent 和 Collector,用于捕獲分布式跟蹤和度量數(shù)據(jù),并將其發(fā)送到分析軟件、存儲(chǔ)庫或其他服務(wù),OTel 的目標(biāo)是提供一套標(biāo)準(zhǔn)化、與廠商無關(guān)的 SDK、API 和工具集,用于將數(shù)據(jù)攝取、轉(zhuǎn)換和發(fā)送到可觀測性后端(開源或商業(yè)廠商)。
OpenTelemetry Collector
OpenTelemetry Collector 提供了一個(gè)與廠商無關(guān)的實(shí)現(xiàn)方式,用于接收、處理和導(dǎo)出遙測數(shù)據(jù),它消除了運(yùn)行、操作和維護(hù)多個(gè)代理/收集器的需求。
事實(shí)上收集器也并不是必需的,有的時(shí)候我們可以直接將遙測數(shù)據(jù)發(fā)送到外部的可視化工具中,比如 Jaeger、Zipkin 等等,但是這樣的話我們就需要在每個(gè)應(yīng)用中都進(jìn)行配置,這樣的話就會(huì)導(dǎo)致配置非常繁瑣,而且也不利于統(tǒng)一管理,所以這里我們就可以使用 OpenTelemetry Collector 來解決這個(gè)問題。
而且 OpenTelemetry Collector 本身部署起來也非常靈活,可以將其部署為代理或網(wǎng)關(guān)。區(qū)別在于作為代理時(shí),收集器實(shí)例與應(yīng)用程序在同一主機(jī)上運(yùn)行(sidecar 容器、daemonset 等)。此外一個(gè)或多個(gè)收集器實(shí)例也可以作為獨(dú)立服務(wù)以每個(gè)集群、數(shù)據(jù)中心和地區(qū)的網(wǎng)關(guān)形式運(yùn)行。
一般來說建議新應(yīng)用選擇代理部署,現(xiàn)有應(yīng)用選擇網(wǎng)關(guān)部署的方式,如果是 Kubernetes 環(huán)境,當(dāng)然更建議部署為守護(hù)進(jìn)程(代理模式)的方式。
收集器由四個(gè)組件組成,通過管道(Pipeline)進(jìn)行啟用:
- 接收器(Receiver)將數(shù)據(jù)發(fā)送到收集器中,可以通過推送或拉取方式發(fā)送
 - 處理器(Processor)決定如何處理接收到的數(shù)據(jù)
 - 導(dǎo)出器(Exporter)決定將數(shù)據(jù)發(fā)送到哪里,可以通過拉取或推送方式完成,上面代碼中的 OTLPTraceExporter 就是一個(gè)導(dǎo)出器
 - 連接器(Connectors):連接器既是輸出者又是接收者。連接器連接兩個(gè)管道:它作為一個(gè)管道末端的導(dǎo)出器消耗數(shù)據(jù),并作為另一個(gè)管道開始處的接收器發(fā)出數(shù)據(jù)。它可以消耗和發(fā)出相同數(shù)據(jù)類型或不同數(shù)據(jù)類型的數(shù)據(jù)。
 

OTel Collector
當(dāng)然我們也可以基于社區(qū)的組件進(jìn)行自定義,以增強(qiáng)和擴(kuò)展收集器管道。例如我們可以創(chuàng)建一個(gè)專用的導(dǎo)出器來接收并攝取指標(biāo)、追蹤和日志。
OpenTelemetry Collector 部署
在了解了 OpenTelemetry 的相關(guān)概念后,接下來我們需要部署 OpenTelemetry Collector,同樣我們直接使用 Istio 提供的 samples 中的配置即可:
kubectl apply -f samples/open-telemetry/loki/otel.yaml -n istio-system該命令會(huì)部署一個(gè) 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]上面的配置中我們主要關(guān)注 exporters 字段,其中 loki 就是我們要將日志發(fā)送到的 Loki 服務(wù),endpoint 字段指定了 Loki 服務(wù)的地址,這里我們直接使用 Loki 的 Service 名稱即可,因?yàn)?Loki 服務(wù)暴露了 3100 端口,所以我們可以直接使用 http://loki.istio-system.svc:3100/loki/api/v1/push 來訪問 Loki 服務(wù)。而 receivers 字段表示接收器,這里配置的是 otlp,表示使用 OpenTelemetry 的 OTLP 標(biāo)準(zhǔn)協(xié)議來接收數(shù)據(jù)。processors 字段表示處理器,這里我們使用了 attributes 處理器,它的作用是向日志中添加一些自定義的屬性,比如 pod、namespace、cluster、mesh 等等,這樣我們在 Loki 中就可以通過這些屬性來進(jìn)行檢索了。最后需要注意的是必須要在 service.pipelines 中明確聲明要啟用的管道以及管道中使用的接收器、處理器和導(dǎo)出器,否則不會(huì)生效。
現(xiàn)在在 Istio 根命名空間中包含如下的一些工作負(fù)載:
$ 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 中添加一個(gè) OpenTelemetry 訪問日志服務(wù)的 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)%"在上面的配置中我們添加了一個(gè)名為 otel 的 Provider,該 Provider 是一個(gè) envoyOtelAls,表示使用 OpenTelemetry 的訪問日志服務(wù),對應(yīng)的后端服務(wù)為 opentelemetry-collector.istio-system.svc.cluster.local,端口為 4317,這里我們直接使用 OpenTelemetry Collector 的 Service 名稱即可。最后我們還配置了 logFormat,表示日志的格式,這里我們添加了一些自定義的屬性,比如 pod、namespace、cluster、mesh 等等,然后在 OpenTelemetry 采集器中會(huì)把這些屬性轉(zhuǎn)換為 Loki 的標(biāo)簽,這樣我們在 Loki 中就可以通過這些屬性來進(jìn)行檢索了。
直接使用 istioctl 命令來安裝配置該對象即可:
istioctl install -f iop.yaml -y到這里我們的準(zhǔn)備工作就完成了。
使用 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應(yīng)用該資源對象后,整個(gè)服務(wù)網(wǎng)格的日志就都會(huì)被上報(bào)到 OTel 采集器,然后在 Loki 中就可以看到日志了。
這里我們直接打開 Grafana 的 Loki Dashboard 即可:
istioctl dashboard grafana首先要在 Grafana 中添加 Loki 數(shù)據(jù)源:

Loki 數(shù)據(jù)源
然后接下來我們?nèi)ピL問 Productpage 應(yīng)用產(chǎn)生一些日志數(shù)據(jù),再切換回到 Grafana 中,切換到 Explore 頁面,然后選擇 Loki 數(shù)據(jù)源,就可以看到 Loki 中的日志了:

日志查詢
同樣的我們還可以使用 Telemetry API 來做一些更加細(xì)粒度的配置。
比如可以使用以下配置禁用 sleep 服務(wù)的訪問日志:
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 服務(wù)的入站訪問日志:
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 表達(dá)式過濾訪問日志。只有響應(yīng)碼大于等于 500 時(shí),才會(huì)顯示訪問日志,如下所示:
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比如只有響應(yīng)碼大于等于 400 或請求轉(zhuǎn)到 BlackHoleCluster 或 PassthroughCluster 時(shí),才顯示訪問日志:
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/。
 















 
 
 












 
 
 
 