公开 Collector 以进行跨集群通信
博客文章在发布后不会更新。这篇文章已经发布一年多了,其内容可能已过时,部分链接可能无效。在依赖任何信息之前,请务必核实。
公开一个 OpenTelemetry Collector 目前需要一系列配置步骤。本文的目标是演示如何在两个不同 Kubernetes 集群中的 Collector 之间建立安全通信。
本文不涵盖 CRD 和依赖项安装的详细信息。
概述
当涉及到使 Collector 公开可访问时,首先想到的是通过 TLS 安全传输用户数据。然而,对服务器进行身份验证至少同样重要,以防止未经授权的服务发送数据。
OpenTelemetry Collector 支持不同的身份验证方法。最常用的可能是:
- TLS 身份验证
- OpenID Connect (OIDC 身份验证)
- 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。
在以下示例中,dXNlci0xOjEyMzQK 是 username=user-1 和 password=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 内存中。

先决条件
接口和行为将来可能会发生变化。因此,此设置中使用的版本会在括号中注明。
- 安装了 ingress-nginx-controller [v1.2.1] 的具有公共地址的 Kubernetes [v1.23.3] 集群。
- 一个 Kubernetes [v1.23.3] 边缘集群,用于创建测试集群。推荐使用 Kind。
- 在两端都安装了 OpenTelemetry Operator [v0.58.0]。
- 在您的公共集群上安装了 Jaeger Operator [v1.37.0]。
- 在您的公共集群上安装了 cert-manager [v1.9.1]。
远程集群配置
由于除 Jaeger 后端外的所有组件都依赖于后续组件,因此我们首先部署后端。
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
name: my-in-memory
下一步,我们使用 OpenTelemetryCollector CRD 创建一个 OpenTelemetry Collector。最重要的条目是 mode、image 和已配置的 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 配置。以下 ClusterIssuer 和 Ingress 条目公开了 otel-collector-app-collector 服务。请注意,您需要替换 email 和 host 字段的值。
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 扩展包含 username 和 password,用于向公开的远程 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-collector 的 daemonset。这确保了每个集群节点都有一个本地 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

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