Lesson 02 of 7 · Modules
OpenTelemetry Collector deep dive
Build telemetry pipelines with the OpenTelemetry Collector: receivers, processors, exporters and connectors, the processors every pipeline needs, agent and gateway deployment patterns on Kubernetes, the OpenTelemetry Operator, and how to see what the Collector itself is doing.
The pipeline model
receivers ──► processors ──► exporters
(otlp, (memory_limiter, (otlp → Tempo,
prometheus, k8sattributes, prometheusremotewrite → Prometheus/Mimir,
filelog…) filter, batch…) otlphttp → Loki…)
Each pipeline handles one signal (traces, metrics or logs). A component can be used in several pipelines; connectors act as an exporter for one pipeline and a receiver for another.
The Collector is a sorting office for letters. Receivers are the doors where letters arrive. Processors are the desks where letters are stamped with an address label, checked, and bundled. Exporters are the vans that take bundles to the right city. If the office gets too full, the doorman stops accepting letters for a while (memory_limiter) instead of letting the building collapse.
A working config
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317 # recent versions default to localhost; bind explicitly in pods
http:
endpoint: 0.0.0.0:4318
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 80
spike_limit_percentage: 25
k8sattributes:
extract:
metadata: [ k8s.namespace.name, k8s.pod.name, k8s.deployment.name, k8s.node.name ]
resource:
attributes:
- key: k8s.cluster.name
value: prod-eu-1
action: upsert
batch: {}
exporters:
otlp/tempo:
endpoint: tempo.observability.svc:4317
tls:
insecure: true # lab only; use TLS in production
prometheusremotewrite:
endpoint: http://prometheus.monitoring.svc:9090/api/v1/write
otlphttp/loki:
endpoint: http://loki-gateway.observability.svc/otlp
debug: {}
service:
pipelines:
traces:
receivers: [ otlp ]
processors: [ memory_limiter, k8sattributes, resource, batch ]
exporters: [ otlp/tempo ]
metrics:
receivers: [ otlp ]
processors: [ memory_limiter, k8sattributes, resource, batch ]
exporters: [ prometheusremotewrite ]
logs:
receivers: [ otlp ]
processors: [ memory_limiter, k8sattributes, resource, batch ]
exporters: [ otlphttp/loki ]
Notes:
- Prometheus accepts remote write only with
--web.enable-remote-write-receiver; recent Prometheus versions can also ingest OTLP directly (--web.enable-otlp-receiver). - Loki 3.x ingests OTLP natively at
/otlp; the older Loki-specific exporter is deprecated. k8sattributesneeds RBAC to read pods, namespaces and ReplicaSets.- Components live in different distributions: the core
otelcolhas few;otelcol-contribhas most (k8sattributes, tail_sampling, filelog…); you can also build your own with the OpenTelemetry Collector Builder.
Processors you'll use
| Processor | Job |
|---|---|
memory_limiter |
First: refuse data before OOM |
k8sattributes |
Add pod/namespace/deployment metadata by source IP or pod UID |
resource / attributes |
Add, change or delete attributes (cluster name, remove PII) |
filter |
Drop telemetry (health checks, noisy spans) with OTTL conditions |
transform |
Rewrite data with OTTL statements |
tail_sampling |
Keep interesting traces (lesson 03; gateway only) |
batch |
Group data before export for efficiency (recent versions also offer batching in exporters) |
Agents, gateways and the Operator
- Agent (DaemonSet): apps send OTLP to the node-local Collector (
$(NODE_IP):4317via the downward API); it also scrapes host and kubelet metrics and tails log files. - Gateway (Deployment + HPA): central processing, tail sampling, routing to tenants/backends, credentials for backends in one place.
- The OpenTelemetry Operator manages Collectors as
OpenTelemetryCollectorresources (modes: deployment, daemonset, statefulset, sidecar) and auto-instrumentation throughInstrumentationresources plus pod annotations such asinstrumentation.opentelemetry.io/inject-java: "true".
Watching the Collector
- Collector self-metrics (port 8888 by default): accepted, refused and failed-to-send items per component, queue sizes. Alert on refused and failed sends.
debugexporter prints data flowing through: invaluable for "why is this attribute missing?", but remove it afterwards (it's noisy).otelcol-contrib validate --configcatches config errors before a rollout.
Try it: a Collector pipeline on kind
- Install the OpenTelemetry Operator (it needs cert-manager; see its install docs) and create an
OpenTelemetryCollectorindeploymentmode with the config above, pointing exporters atdebugonly. - Send test traces with
telemetrygen(from the collector-contrib project):telemetrygen traces --otlp-insecure --otlp-endpoint <collector>:4317 --traces 10. - Read the Collector logs and see the spans printed by the debug exporter.
- Add
k8sattributesand send from a pod; confirm the pod metadata appears. - Add a
filterprocessor that drops spans withhttp.route == "/healthz"and verify they disappear.
Going deeper: Collector at scale
- Enable persistent queues (file storage extension +
sending_queue) on exporters that must survive restarts and backend outages. - For tail sampling across several gateway replicas, put a loadbalancing exporter in front that routes by trace ID, so all spans of a trace reach the same replica.
- Keep configs small and layered (agent: collect + enrich; gateway: policy), and version them in Git.
- Pin Collector versions: component names and defaults evolve between releases; read the changelog before upgrading.
Recap
- Receivers → processors → exporters, per signal pipeline; connectors link pipelines.
- Always memory_limiter first,
k8sattributesfor context,batchfor efficiency. - Agent (DaemonSet) + gateway (Deployment) patterns; the Operator manages Collectors and auto-instrumentation.
- Watch the Collector's own metrics; debug with the
debugexporter; validate configs before rollout.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.