Navigational sextant by Lars Plöger via Pixabay
OpenTelemetry 收集器[1]是一个二进制程序,通常作为代理部署在运行业务应用程序的主机上,但是现在越来越多的应用程序运行在像 Kubernetes 这样的容器平台上。
但是在 Kubernetes 上运行 OpenTelemetry 收集器需要什么呢?
OpenTelemetry 收集器项目附带了一个示例文件以供 Kubernetes 使用,但是该示例文件的目的是作为一个指南,而不是作为一个可用于生产的解决方案。收集器是如此通用,以至于几乎不可能实现一刀切的部署配置。
收集器的裸金属部署很容易规划和执行:它是作为主机上的守护进程运行的单个二进制文件。然而,对于 Kubernetes,我们有几个选择:
最常见的情况是,你可以混合使用 Deployment 和 Sidecar:Deployment 具有高度弹性,可以通过水平 Pod Autoscale 自动伸缩,而 Sidecar 允许你的应用程序将遥测数据卸载到运行在同一“主机”中的进程。Sidecar 更有可能拥有特定于业务应用程序的定制配置(即处理器),而 Deployment 通常是更通用的配置。
DaemonSet 最常用于单租户、多云部署,在这种情况下,需要确保来自应用程序的遥测数据在到达公共 internet 之前由同一个节点中的一个进程进行预处理。
当收集器实例的副本数量不会频繁更改,并且你正在使用可以从稳定的主机名列表(例如负载平衡导出器[5])中受益的处理器时,应该使用 StatefulSet。
为部署设计好拓扑之后,就可以开始编制清单了!对于本例,我们将使用经典的 Deployment+Sidecar 模式。
无论采用哪种部署模式,你都可能需要为收集器提供一个配置文件。因为这必须是一个实际的文件,我们将为它创建一个 ConfigMap,像这样:
---
apiVersion: v1
kind: ConfigMap
metadata:
name: collector-config
data:
collector.yaml: |
receivers:
otlp:
protocols:
grpc:
processors:
exporters:
logging:
service:
pipelines:
traces:
receivers: [otlp]
processors: []
exporters: [logging]
---
apiVersion: v1
kind: ConfigMap
metadata:
name: agent-config
data:
agent.yaml: |
receivers:
otlp:
protocols:
grpc:
processors:
exporters:
otlp:
endpoint: "opentelemetrycollector.default.svc.cluster.local:4317"
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: []
exporters: [otlp]
接下来,我们可以创建我们的 Deployment。最简单的解决方案是:
apiVersion: apps/v1
kind: Deployment
metadata:
name: opentelemetrycollector
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: opentelemetrycollector
template:
metadata:
labels:
app.kubernetes.io/name: opentelemetrycollector
spec:
containers:
- name: otelcol
args:
- --config=/conf/collector.yaml
image: otel/opentelemetry-collector:0.18.0
volumeMounts:
- mountPath: /conf
name: collector-config
volumes:
- configMap:
items:
- key: collector.yaml
path: collector.yaml
name: collector-config
name: collector-config
我们现在有了一个启动并运行的收集器实例,但是它仍然不能接收数据:为此,我们需要一个Service[6]来暴露我们在配置中声明的 OTLP 端口。以下是满足该需求的服务定义:
apiVersion: v1
kind: Service
metadata:
name: opentelemetrycollector
spec:
ports:
- name: grpc-otlp
port: 4317
protocol: TCP
targetPort: 4317
selector:
app.kubernetes.io/name: opentelemetrycollector
type: ClusterIP
现在,我们已经为收集器准备好了所有部件,现在是时候启动一个生成跟踪并将其发送到收集器的应用程序了。为此,我们将创建一个 Deployment,其中包含一个应用程序[7],该应用程序为接收到的每个 HTTP 请求创建跟踪。这个应用程序被打包在一个名为quay.io/jpkroehling/generate-span-java:0.1.0[8]的容器镜像中,我们可以使用如下 Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: myapp
template:
metadata:
labels:
app.kubernetes.io/name: myapp
spec:
containers:
- name: myapp
image: quay.io/jpkroehling/generate-span-java:0.1.0
- name: agent
image: otel/opentelemetry-collector:0.18.0
args:
- --config=/conf/agent.yaml
volumeMounts:
- mountPath: /conf
name: agent-config
volumes:
- configMap:
items:
- key: agent.yaml
path: agent.yaml
name: agent-config
name: agent-config
当 Deployment 就绪,我们就可以调用它的/orders 端点,它将为我们生成一些跨度。进行测试的最简单方法是执行端口转发,这样对 localhost:8080/orders 的调用就会落在 Kubernetes 集群中的应用程序上:
$ kubectl port-forward deployment/myapp 8080
Forwarding from 127.0.0.1:8080 -> 8080
现在让我们为我们的收集器跟踪日志:一旦我们调用了我们的服务,收集器中的 loggingexport 将确保在日志中记录这个事件。
$ kubectl logs deployments/opentelemetrycollector -f
...
2021-01-22T12:59:53.561Z info service/service.go:267 Everything is ready. Begin running and processing data.
最后,让我们生成一些跨度:
$ curl localhost:8080/order
Created
在收集器的日志中,我们现在应该看到如下内容:
2021-01-22T13:26:49.320Z INFO loggingexporter/logging_exporter.go:313 TracesExporter {"#spans": 2}
2021-01-22T13:26:49.430Z INFO loggingexporter/logging_exporter.go:313 TracesExporter {"#spans": 1}
2021-01-22T13:26:50.422Z INFO loggingexporter/logging_exporter.go:313 TracesExporter {"#spans": 4}
2021-01-22T13:26:50.571Z INFO loggingexporter/logging_exporter.go:313 TracesExporter {"#spans": 1}
这证实了在我们的应用程序中生成的跨已经通过 sidecar 代理转到收集器。在真实的设置中,我们将配置收集器以将我们的 span 导出到真实的后端,如 Jaeger 或 Zipkin。
到目前为止,你可能已经意识到在 Kubernetes 上部署一个简单的 OpenTelemetry 收集器实例并不是那么困难。但第二天的问题该怎样,比如版本升级?或者如何使 Service 与 ConfigMap 保持同步,以便配置中定义的所有端口都通过服务自动暴露?自动将 sidecar 注入到业务部署中不是很好吗?这些都是OpenTelemetry Operator [9]可以完成的任务。
如果你更喜欢 Helm Charts,也有一个实验性的版本[10]可以安装收集器。
OpenTelemetry 收集器是一个非常灵活和轻量级的进程,使混合和匹配策略成为可能,允许根据你非常特定的需求构建收集器链。在 Kubernetes 中部署收集器的单个实例并不是一项复杂的任务,尽管维护这些实例可能会让你考虑使用像 Helm Charts 或 Operator 这样的工具。
感谢 Gary Brown。
[1]
OpenTelemetry 收集器: https://github.com/open-telemetry/opentelemetry-collector
[2]
Deployment: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
[3]
DaemonSet: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/
[4]
StatefulSet: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/
[5]
负载平衡导出器: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/93d82a1c1aad3bfaa8694519d75b1d75c1de9a8f/exporter/loadbalancingexporter/README.md#trace-id-aware-load-balancing-exporter
[6]
Service: https://kubernetes.io/docs/concepts/services-networking/service/
[7]
一个应用程序: https://github.com/observatorium/opentelemetry-generate-span-java
[8]
quay.io/jpkroehling/generate-span-java:0.1.0: https://quay.io/repository/jpkroehling/generate-span-java
[9]
OpenTelemetry Operator : https://github.com/open-telemetry/opentelemetry-operator/
[10]
实验性的版本: https://github.com/open-telemetry/opentelemetry-helm-charts