公开 Collector 以进行跨集群通信

博客文章在发布后不会更新。这篇文章已经发布一年多了,其内容可能已过时,部分链接可能无效。在依赖任何信息之前,请务必核实。

公开一个 OpenTelemetry Collector 目前需要一系列配置步骤。本文的目标是演示如何在两个不同 Kubernetes 集群中的 Collector 之间建立安全通信

本文不涵盖 CRD 和依赖项安装的详细信息。

概述

当涉及到使 Collector 公开可访问时,首先想到的是通过 TLS 安全传输用户数据。然而,对服务器进行身份验证至少同样重要,以防止未经授权的服务发送数据。

OpenTelemetry Collector 支持不同的身份验证方法。最常用的可能是:

  1. TLS 身份验证
  2. OpenID Connect (OIDC 身份验证)
  3. HTTP 基本身份验证

本文为了简单起见,将重点放在HTTP 基本身份验证上。它旨在展示如何在没有密钥管理或其他第三方服务的情况下运行安全设置。

有关 TLS 配置的更多信息,请参阅文章 TLS 如何提供识别、身份验证、保密性和完整性以及 Collector 在 GitHub 上的 TLS-Config 说明。

如果您有兴趣使用外部身份验证提供商,我建议您阅读 Juraci Paixão Kröhling 在此主题上的文章 Securing your OpenTelemetry Collector。他解释了如何使用 OIDC-Authenticator 扩展来保护 OpenTelemetry Collector,以及如何将 Keycloak 配置为身份验证提供商。

基本身份验证

HTTP 基本身份验证机制相当简单。HTTP 用户代理(例如 Web 浏览器)会在每次请求时提供用户名和密码组合。在建立连接时,传输的凭据会通过 Authorization 密钥包含在 HTTP 标头中。作为值,身份验证方法 basic 先出现,然后是编码的凭据。请注意,凭据格式为 username:password

在以下示例中,dXNlci0xOjEyMzQKusername=user-1password=1234 组合的编码。请注意,可以使用

# HTTP Header key: value pair
Authorization: Basic <credentials-base64-encoded>

# example: user: user-1 password: 1234
Authorization: Basic dXNlci0xOjEyMzQK

您可以使用 base64 命令行工具轻松创建自己的用户名密码组合。

# encode
$ echo "user-1:1234" | base64
dXNlci0xOjEyMzQK

# decode
$ echo "dXNlci0xOjEyMzQK" | base64 -d
user-1:1234

数据流

以下图表说明了目标拓扑。目标是通过专用的 Collector 将由 测试应用程序生成的跟踪传输到公共集群。接收 Collector 使用传输的 ‘Basic’ HTTP 身份验证凭据来检查发件人是否有权存储数据。最后,传输的跟踪将存储在 Jaeger 内存中

overview diagram

先决条件

接口和行为将来可能会发生变化。因此,此设置中使用的版本会在括号中注明。

远程集群配置

由于除 Jaeger 后端外的所有组件都依赖于后续组件,因此我们首先部署后端。

apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: my-in-memory

下一步,我们使用 OpenTelemetryCollector CRD 创建一个 OpenTelemetry Collector。最重要的条目是 modeimage 和已配置的 basicauth 扩展。在下面的清单中,选择了 deployment 模式以确保至少有一个 Collector pod 可用于处理传入信息。此外,默认 Collector 镜像已被 contrib 版本覆盖。这是必需的,因为 核心版本不包含 basicauth 扩展。此扩展已配置为名为 basicauth/server,并在 otlp/basicauth 中注册。作为 otlp 导出器端点,配置了 Jaeger 内存服务。

apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: otel-collector-app
spec:
  mode: deployment
  image: otel/opentelemetry-collector-contrib:0.58.0
  config: |
    extensions:
      basicauth/server:
        htpasswd:
          inline: |
            <REPLACE: your backend credentials, e.g.: "user-1:1234">

    receivers:
      otlp/basicauth:
        protocols:
          grpc:
            auth:
              authenticator: basicauth/server

    exporters:
      otlp/jaeger:
        endpoint: my-in-memory-collector:4317
        tls:
          insecure: true
          insecure_skip_verify: true

    service:
      extensions: [basicauth/server]
      pipelines:
        traces:
          receivers: [otlp/basicauth]
          exporters: [otlp/jaeger]

安装成功后,Jaeger 后端和 OpenTelemetry Collector 的 Pod 应该在选定的命名空间中创建。

NAME                                            READY   STATUS    RESTARTS   AGE
my-in-memory-6c5f5f87c5-rnp99                   1/1     Running   0          4m
otel-collector-app-collector-55cccf4b7d-llczt   1/1     Running   0          3m

此外,以下服务应该可用

NAME                                      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                    AGE
my-in-memory-agent                        ClusterIP   None            <none>        5775/UDP,5778/TCP,6831/UDP,6832/UDP                         7m
my-in-memory-collector                    ClusterIP   10.245.43.185   <none>        9411/TCP,14250/TCP,14267/TCP,14268/TCP,4317/TCP,4318/TCP    7m
my-in-memory-collector-headless           ClusterIP   None            <none>        9411/TCP,14250/TCP,14267/TCP,14268/TCP,4317/TCP,4318/TCP    7m
my-in-memory-query                        ClusterIP   10.245.91.239   <none>        16686/TCP,16685/TCP                                         7m
otel-collector-app-collector              ClusterIP   10.245.5.134    <none>        4317/TCP                                                    5m
otel-collector-app-collector-headless     ClusterIP   None            <none>        4317/TCP                                                    5m
otel-collector-app-collector-monitoring   ClusterIP   10.245.116.38   <none>        8888/TCP                                                    5m

最后,cert-manager 被配置为自动从 Let’s Encrypt 请求 TLS 证书,并使其可用于 Ingress TLS 配置。以下 ClusterIssuerIngress 条目公开了 otel-collector-app-collector 服务。请注意,您需要替换 emailhost 字段的值。

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
  namespace: cert-manager
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your-email-address-here@example.com # REPLACE
    privateKeySecretRef:
      name: letsencrypt
    solvers:
      - http01:
          ingress:
            class: nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-otel
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/backend-protocol: GRPC
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  tls:
    - hosts:
        - your-host # REPLACE your domain endpoint, e.g., traces@example.com
      secretName: letsencrypt
  rules:
    - host: your-host # REPLACE your domain endpoint, e.g., traces@example.com
      http:
        paths:
          - pathType: Prefix
            path: '/'
            backend:
              service:
                name: otel-collector-app-collector
                port:
                  number: 4317

边缘集群配置

为了能够确定传输跟踪的来源,通过 k8sattributes 处理器的标识元数据扩展了 span 标签。它在 OpenTelemetry Collector contrib 版本中可用。下一步,我们创建一个具有必要权限的服务帐户。如果您想了解更多关于 K8s 元数据的信息,可以阅读这篇帖子“使用 K8s 元数据改进故障排除”。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: attributes-role
rules:
  - apiGroups:
      - ''
    resources:
      - pods
    verbs:
      - get
      - list
      - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: attributes-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: attributes-role
subjects:
  - kind: ServiceAccount
    name: attributes-account
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: attributes-account

让我们快速看一下最重要的边缘 Collector 设置。daemonset 用作部署模式,以确保每个节点都有一个 Collector 实例。basicauth 扩展包含 usernamepassword,用于向公开的远程 Collector 进行身份验证。k8sattributes 处理器通过 Kubernetes Kubernetes downward-api 提供更多容器和节点特定的信息。未涵盖的是集群可用区和集群名称。为了能够稍后识别报告的 spans,它们在 resource 处理器的帮助下被手动插入。最后,OTLP 导出器端点也已给出占位符值,必须用您的远程集群域名替换。

apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: otel-collector-app
spec:
  mode: daemonset
  image: otel/opentelemetry-collector-contrib:0.58.0
  serviceAccount: attributes-account
  env:
    - name: KUBE_NODE_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: spec.nodeName

  config: |
    extensions:
      basicauth/client:
        client_auth: # credentials must be consistent with those of the receiving collector.
          username: <REPLACE: your basicauth username, e.g.: "user-1">
          password: <REPLACE: your basicauth password, e.g.: "1234">

    receivers:
      otlp:
        protocols:
          grpc:

    processors:
      resource:
        attributes:
        - key: cloud.availability_zone
          value: <REPLACE: your availability zone, e.g.: "eu-west-1">
          action: insert
        - key: k8s.cluster.name
          value: <REPLACE: your cluster name, e.g.: "edge-cluster-1">
          action: insert
      k8sattributes:
        filter:
          node_from_env_var: KUBE_NODE_NAME

    exporters:
      otlp:
        endpoint: "<REPLACE: your domain endpoint, e.g.: "traces.example.com:443">"
        auth:
          authenticator: basicauth/client
      logging:

    service:
      extensions: [basicauth/client]
      pipelines:
        traces:
          receivers: [otlp]
          processors: [k8sattributes]
          exporters: [otlp,logging]

安装成功后,应该已经创建了一个名为 otel-collector-app-collectordaemonset。这确保了每个集群节点都有一个本地 Collector 实例正在运行。

部署跟踪生成器以生成测试数据

apiVersion: apps/v1
kind: Deployment
metadata:
  name: trace-gen
spec:
  selector:
    matchLabels:
      app: trace-gen
  template:
    metadata:
      labels:
        app: trace-gen
    spec:
      containers:
        - name: trace-gen
          image: ghcr.io/frzifus/jaeger-otel-test:latest
          args:
            [
              '-otel.agent.host=otel-collector-app-collector',
              '-otel.agent.port=4317',
            ]
          env:
            - name: OTEL_SERVICE_NAME
              value: 'local-test-service'

测试

现在,边缘集群中生成的 span 应该会扩展源元数据。然后将它们传输到远程集群并存储在 Jaeger 后端中。Jaeger 本身提供了一个 UI 来检查传输的数据。

访问 UI 的简单方法是通过端口转发到您的本地系统。

$ kubectl port-forward deployments/my-in-memory 16686
Forwarding from 127.0.0.1:16686 -> 16686
jaeger-ui on remote cluster

结论

IngressClusterIssuer 和客户端/服务器端的 OpenTelemetryCollector 等配置必须手动配置。根据安装的 Kubernetes 组件,配置差异很大。总的来说,配置非常容易出错。将来,应该通过 OpenTelemetry Operator 简化 Collector 的公开。如果您对开发感兴趣,可以关注 GitHub issue #902 以保持更新。

参考