使用 OpenTelemetry 对 Istio 进行链路追踪

本文介绍如何使用 OpenTelemetry 对 Istio 进行链路追踪。通过 Istio 的链路追踪功能,可以可视化服务调用关系和依赖、监控微服务的健康状态、识别系统中的性能瓶颈以及排查分布式系统中的故障。

前提条件

  • 集群已安装 Istio 且 Istio 版本 ≥ v1.19。

  • 已部署 OpenTelemetry Collector。Istio 调用链需要通过 OpenTelemetry Collector 转发上报至可观测链路 OpenTelemetry 版。如您的集群中尚未部署 OpenTelemetry Collector,请参考 通过 OpenTelemetry Collector 转发 进行部署。

  • 获取可观测链路 OpenTelemetry 版的接入点和鉴权信息。详情请参考 接入和鉴权说明

接入步骤

1、设置 Istio 链路数据上报方式

  1. Istio 可以通过 gRPC 或 HTTP 协议导出 Trace 数据到 OpenTelemetry Collector。请选择以下两种方式中的一种进行配置。

    使用 gRPC 协议

    运行以下内容,Istio 的 Trace 数据将通过 OTLP/HTTP 导出到 OpenTelemetry Collector。

    创建配置文件istio-otel-config.yaml。

    cat > istio-otel-config.yaml <<EOF
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    spec:
      meshConfig:
        defaultConfig:
          tracingServiceName: APP_LABEL_AND_NAMESPACE
        enableTracing: true
        extensionProviders:
        - name: otel-tracing
          opentelemetry:
            port: 4317
            service: <service-name>.<namespace>.svc.cluster.local
            resource_detectors:
              environment: {}
    EOF

    使用 HTTP 协议

    运行以下内容,Istio 的 Trace 数据将通过 OTLP/HTTP 导出到 OpenTelemetry Collector。

    创建配置文件istio-otel-config.yaml。

    cat > istio-otel-config.yaml <<EOF
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    spec:
      meshConfig:
        enableTracing: true
        extensionProviders:
        - name: otel-tracing
          opentelemetry:
            port: 4318
            service: opentelemetry-collector.observability.svc.cluster.local
            http:
              path: "/v1/traces"
              timeout: 5s
              headers:
                - name: "custom-header"
                  value: "custom value"
            resource_detectors:
              environment: {}
    EOF
    说明
    • 需要将配置中的service地址替换为您环境中 OpenTelemetry Collector 的实际服务地址,<service-name>为 OpenTelemetry Collector 的服务名称,<namespace> 为OpenTelemetry Collector 所在命名空间的名称。例如:opentelemetry-collector.observability.svc.cluster.local

    • Istio 提供了配置选项 tracingServiceName 来控制应用名称的生成方式,有三种选项。

      • APP_LABEL_AND_NAMESPACE(默认值):使用 <app标签>.<命名空间> 格式。

      • CANONICAL_NAME_ONLY:仅使用工作负载的规范名称,如 Deployment 的名称。

      • CANONICAL_NAME_AND_NAMESPACE:使用 <规范名称>.<命名空间> 格式。

  2. 使用该配置文件安装。

    istioctl install -y -f istio-otel-config.yaml

2、为 Istio 开启链路追踪

配置完链路数据上报后,还需要开启 Istio 链路数据采集功能并设置采样比例。运行以下命令来启用链路追踪。

  1. 创建配置文件istio-telemetry.yaml。

    cat > istio-telemetry.yaml <<EOF
    apiVersion: telemetry.istio.io/v1
    kind: Telemetry
    metadata:
      name: otel-demo
    spec:
      tracing:
      - providers:
        - name: otel-tracing
        randomSamplingPercentage: 100
    EOF
    说明

    randomSamplingPercentage 表示追踪采样率,取值 0-100,设置为 100 表示记录所有请求的追踪数据。在生产环境中,建议根据实际流量情况调整该值。

  2. 应用配置。

    kubectl apply -f istio-telemetry.yaml

    完成以上配置后,当服务间有调用发生时,Istio 就会自动生成链路数据,并通过 OpenTelemetry Collector 转发到可观测链路 OpenTelemetry 版

3、查看 Istio 调用链

前往可观测链路 OpenTelemetry 版控制台查看 Istio 的调用链。94

实践教程

接下来将通过以下示例演示如何采集 Istio 的调用链,并上报至可观测链路 OpenTelemetry 版

1、安装 Istio

curl -L https://istio.io/downloadIstio | sh -

cd istio-1.25.2

export PATH=$PWD/bin:$PATH

2、安装 OpenTelemetry Collector

  1. 创建otel.yaml,需要替换 <gRPC-Endpoint> 和 <gRPC-Token> 为前提条件中获取的接入点和鉴权 Token。

    cat > otel.yaml <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: opentelemetry-collector-conf
      labels:
        app: opentelemetry-collector
    data:
      opentelemetry-collector-config: |
        receivers:
          otlp:
            protocols:
              grpc:
              http:
        processors:
          batch:
        exporters:
          otlp:
            endpoint: "<gRPC-Endpoint>"
            headers:
              Authentication: "<gRPC-Token>"
            tls:
              insecure: true
          logging:
            loglevel: debug
        extensions:
          health_check:
            port: 13133
        service:
          extensions:
          - health_check
          pipelines:
            logs:
              receivers: [otlp]
              processors: [batch]
              exporters: [logging]
            traces:
              receivers:
              - otlp
              exporters:
              - logging
              - otlp
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: opentelemetry-collector
      labels:
        app: opentelemetry-collector
    spec:
      ports:
        - name: grpc-opencensus
          port: 55678
          protocol: TCP
          targetPort: 55678
        - name: grpc-otlp # Default endpoint for OpenTelemetry receiver.
          port: 4317
          protocol: TCP
          targetPort: 4317
        - name: http-otlp # HTTP endpoint for OpenTelemetry receiver.
          port: 4318
          protocol: TCP
          targetPort: 4318
      selector:
        app: opentelemetry-collector
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: opentelemetry-collector
    spec:
      selector:
        matchLabels:
          app: opentelemetry-collector
      strategy:
        rollingUpdate:
          maxSurge: 1
          maxUnavailable: 1
        type: RollingUpdate
      template:
        metadata:
          labels:
            app: opentelemetry-collector
            sidecar.istio.io/inject: "false" # do not inject
        spec:
          containers:
            - command:
                - "/otelcol"
                - "--config=/conf/opentelemetry-collector-config.yaml"
              env:
                - name: POD_NAME
                  valueFrom:
                    fieldRef:
                      apiVersion: v1
                      fieldPath: metadata.name
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      apiVersion: v1
                      fieldPath: metadata.namespace
              image: otel/opentelemetry-collector:0.54.0
              imagePullPolicy: IfNotPresent
              name: opentelemetry-collector
              ports:
                - containerPort: 4317
                  protocol: TCP
                - containerPort: 4318
                  protocol: TCP
                - name: grpc-opencensus
                  containerPort: 55678
                  protocol: TCP
              resources:
                limits:
                  cpu: "2"
                  memory: 4Gi
                requests:
                  cpu: 200m
                  memory: 400Mi
              terminationMessagePath: /dev/termination-log
              terminationMessagePolicy: File
              volumeMounts:
                - name: opentelemetry-collector-config-vol
                  mountPath: /conf
          dnsPolicy: ClusterFirst
          restartPolicy: Always
          schedulerName: default-scheduler
          terminationGracePeriodSeconds: 30
          volumes:
            - configMap:
                defaultMode: 420
                items:
                  - key: opentelemetry-collector-config
                    path: opentelemetry-collector-config.yaml
                name: opentelemetry-collector-conf
              name: opentelemetry-collector-config-vol
    EOF
  2. 创建命名空间 observability。

    kubectl create namespace observability
  3. 部署 OpenTelemetry Collector。

    kubectl apply -f otel.yaml -n observability

3、设置 Istio 链路追踪

  1. 在集群中运行以下命令,Istio 的 Trace 数据将通过 OTLP/HTTP 导出到 OpenTelemetry Collector。

    cat <<EOF | istioctl install -y -f -
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    spec:
      meshConfig:
        enableTracing: true
        extensionProviders:
        - name: otel-tracing
          opentelemetry:
            port: 4317
            service: opentelemetry-collector.observability.svc.cluster.local
            resource_detectors:
              environment: {}
    EOF
  2. 运行以下命令启用 Istio 链路追踪。

    kubectl apply -f - <<EOF
    apiVersion: telemetry.istio.io/v1
    kind: Telemetry
    metadata:
      name: otel-demo
    spec:
      tracing:
      - providers:
        - name: otel-tracing
        randomSamplingPercentage: 100
    EOF

4、部署 Demo

  1. 为 default 命名空间添加自动注入标签。

    Istio 将自动为该命名空间中新创建的 Pod 注入 sidecar 代理(istio-proxy 容器)。

    kubectl label namespace default istio-injection=enabled
  2. 部署 istio 自带的 Demo(在下载好的 istio 目录中运行)。

    kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
  3. 查看容器组和服务状态。

    kubectl get pods
    kubectl get services
  4. 验证应用是否可访问。

    kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"
    
    # 预期输出
    <title>Simple Bookstore App</title>

5、创建 Demo 服务的对外访问入口

  1. 创建 Demo 应用的入口网关配置。

    kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
  2. 获取访问地址

    export INGRESS_NAME=istio-ingressgateway    
    export INGRESS_NS=istio-system             
    
    export INGRESS_HOST=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    export INGRESS_PORT=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.spec.ports[?(@.name=="http2")].port}')
    export SECURE_INGRESS_PORT=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.spec.ports[?(@.name=="https")].port}')
    export TCP_INGRESS_PORT=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.spec.ports[?(@.name=="tcp")].port}')
    
    export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT
    
    # 查看生成的访问地址
    echo $GATEWAY_URL
    # 输出示例:192.168.1.100:80
  3. 验证 Demo 访问地址。

    curl -s "http://${GATEWAY_URL}/productpage" | grep -o "<title>.*</title>"
    
    # 预期输出
    <title>Simple Bookstore App</title>

6、查看调用链

  1. 使用以下命令连续访问 10 次 productpage 页面,以生成测试流量。

    for i in $(seq 1 10); do curl -s -o /dev/null "http://$GATEWAY_URL/productpage"; done
  2. 前往可观测链路 OpenTelemetry 版控制台查看 Istio 的调用链。93

常见问题

问题:控制台未查询到 Istio 的链路数据。

解决方法:

  1. 可查看 OpenTelemetry Collector 日志,确认 OpenTelemetry Collector 正常运行并接收到了 Istio 上报的数据。

    kubectl logs <collector-pod-name> -n <namespace>

    预期输出:91

  2. 检查 sidecar 是否注入成功。

    kubectl get pod -n default -o custom-columns=NAME:.metadata.name,CONTAINERS:.spec.containers[*].name

    如果 CONTAINERS 中不包含 istio-proxy 容器,说明注入失败。注入失败的原因可能是:为命名空间设置istio-injection=enabled自动注入标签时,Pod 已经创建。该标签只对新创建的 Pod 生效,已存在的 Pod 不受影响,需要重启 Pod 后才会生效。

  3. 检查 Telemetry 资源是否正确配置。

    • 使用以下命令检查 Telemetry 配置。

      kubectl get telemetry otel-demo -o yaml 
    • 查看 Istio 的 ConfigMap 配置中与 OpenTelemetry 相关的部分是否正确。

      kubectl get cm istio -n istio-system -o yaml | grep -A 10 extensionProviders

相关链接

Istio OpenTelemetry