在不同的 Kubernetes 集群之间实现暴露 Collector 进行跨集群通信
在不同的 Kubernetes 集群之间实现暴露 Collector 进行跨集群通信
本文将演示如何在不同的 Kubernetes 集群中建立安全通信以保障用户数据的传输。目前,在OpenTelemetry Collector中,暴露 Collector 需要进行一系列的配置步骤。通过本文章,我们将展示使用基本身份验证(Basic Authentication)的方法以简化安全设置,无需使用密钥管理或其他第三方服务。
注:本文不涵盖CRD和依赖安装的详细信息。
概述
在将 Collector 公开访问时,首要的问题是通过 TLS 进行用户数据的安全传输。然而,对服务器进行身份验证同样至关重要,以防止未经授权的服务发送数据。
OpenTelemetry Collector 支持多种身份验证方法,目前最常用的有:
- TLS 身份验证
- OpenID Connect(OIDC-Authentication)
- 基本身份验证(HTTP Basic Authentication)
本文将重点介绍基本身份验证(HTTP Basic Authentication),以简化定义安全设置的方法,无需密钥管理或其他第三方服务。对于有关 TLS 配置的更多信息,请参考以下文章:
- TLS 提供识别,身份验证,机密性和完整性
- openTelemetry Collector 中的 TLS 配置。
如果您有兴趣使用外部身份验证提供者,请参阅以下文章:
- Securing your OpenTelemetry Collector 作者 Juraci Paixão Kröhling 在本主题上说明了如何使用 OpenTelemetry Collector 进行安全配置,并如何将 Keycloak 配置为身份验证提供者。
基本身份验证
HTTP 基本身份验证机制非常简单。HTTP 用户代理(如 Web 浏览器)在每个请求中提供用户名和密码的组合。当建立连接时,通过Authorization
键在 HTTP 标头中包含传输的凭据。身份验证方法的值首先是basic
,然后是编码后的凭据。注意,凭据的形式是“用户名:密码”。
下面的示例中,dXNlci0xOjEyMzQK
是用户名=user-1
和密码=1234
的组合的编码值。要编码或解码 base64 值,可以使用以下命令:
# HTTP 标头键:值对
Authorization: Basic <credentials-base64-encoded>
# 例如: 用户: user-1, 密码: 1234
Authorization: Basic dXNlci0xOjEyMzQK
可以使用 base64 cli 工具 来创建自己的用户名和密码组合。
# 编码
$ echo "user-1:1234" | base64
dXNlci0xOjEyMzQK
# 解码
$ echo "dXNlci0xOjEyMzQK" | base64 -d
user-1:1234
数据流
下图展示了目标拓扑图。目标是通过专用的 Collector 将测试应用(test application)生成的跟踪传输到一个公开的集群。接收的 Collector 使用传输的基本身份验证凭据来检查发送者是否有权存储数据。最后,已传输的跟踪将存储在 Jaeger in-memory 中。
前提条件
界面和行为将来可能会发生变化。因此,在本设置中使用的版本如下所示:
- 带有 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 后端以外的所有组件都依赖于后续组件,我们首先部署后端。请参考以下 YAML 配置:
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
name: my-in-memory
接下来,我们使用 OpenTelemetryCollector
CRD 创建一个 OpenTelemetry Collector。其中最重要的条目是 mode
、image
和已配置的 basicauth 扩展。在下面的配置中,选择了 deployment
模式,以确保至少有一个 Collector Pod 可用来处理传入的信息。此外,还使用了默认的 Collector 镜像,但后面被contrib版本覆盖。这是因为核心版不包含 basicauth extension。此扩展配置了名称为basicauth/server
的 basicauth,并在 otlp/basicauth
中进行了注册。作为 otlp exporter 端点,配置了 Jaeger in-memory 服务。
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 processor 将识别元数据添加到 span 标签中。该 processor 可在 OpenTelemetry Collector contrib 版本中使用。首先,使用需要的权限创建一个 service account。如果您想了解有关 Kubernetes 元数据的更多信息,可以阅读本文 “Improved troubleshooting using K8s metadata"。
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
扩展中包含了用于标识自身以连接到远程 Collector 的 username
和 password
。使用 k8sattributes
processor 利用 Kubernetes 的 Kubernetes downward-api 提供更多的容器和节点的具体信息。未涵盖的信息包括集群的可用区和集群名称。为了能够在以后区分报告的跟踪,这些信息将在 resource
processor 的帮助下手动插入。此外,OTLP exporter 的端点也已给出了占位符值,您需要将其替换为远程集群的域名。
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'
测试
现在,在边缘集群中生成的跟踪应该已扩展为包含源数据。然后将它们传输到远程集群并存储在 Jaeger 后端中。Jaeger 提供了用于检查传输数据的用户界面。
一种简单的方法是通过端口转发到本地系统来访问 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 以获取最新消息。