Why a Collector in Kubernetes
You can send OTLP straight from a Kubernetes pod to Telemtra, but running the OpenTelemetry Collector inside the cluster gives you four things app-direct export does not: container log file collection, pod and node metric scraping, batching across many pods over a single egress connection, and a stable place to apply attribute filtering before data leaves the cluster.
The Collector also lets you change backends without restarting workloads. Apps export to a stable in-cluster endpoint (otelcol.observability:4318); the Collector handles the connection to Telemtra.
DaemonSet, sidecar, or gateway?
- DaemonSet. One Collector pod per node. Reads container log files from
/var/log/pods, scrapes the kubelet for node and pod metrics, accepts OTLP from local pods onlocalhost. Best default. - Sidecar. One Collector container injected into each application pod. Useful when an app should not even know an OTLP endpoint exists; the app sends to
localhost:4318and the sidecar handles everything. Higher per-pod cost. - Gateway. A Deployment behind a Service that receives OTLP from DaemonSets or sidecars and forwards to Telemtra. Useful for cross-cluster aggregation or strict egress policy.
Most teams start with the DaemonSet and add a gateway only when they have multiple clusters or need to centralize batching.
Where these manifests go in your project
You can apply the manifests directly with kubectl, but most teams keep them in the same Git repo as their application manifests so the Collector is versioned alongside the rest of the deploy. A typical layout:
my-platform/
├── apps/
│ └── checkout-api/ # your existing app manifests (unchanged)
│ ├── deployment.yaml
│ └── service.yaml
└── observability/ # NEW: Collector config lives here
├── namespace.yaml # observability namespace
├── secret.yaml # telemtra-api-key Secret
├── rbac.yaml # ServiceAccount + ClusterRole for kubeletstats
├── configmap.yaml # otelcol-config (the YAML from the next section)
└── daemonset.yaml # the DaemonSet itself How application pods are affected:
- With the DaemonSet topology (recommended): apps need no changes. Container logs are read from the node filesystem; OTLP-emitting apps target the node-local Collector at
http://$(NODE_IP):4318. Add this via the downward API in the app pod spec only if the app already emits OTLP. - With the sidecar topology: add a second container to the app's pod spec (shown below) and point the app at
http://localhost:4318.
Apply in order:
kubectl apply -f observability/namespace.yaml
kubectl apply -f observability/secret.yaml
kubectl apply -f observability/rbac.yaml
kubectl apply -f observability/configmap.yaml
kubectl apply -f observability/daemonset.yaml Or, with Kustomize/Helm/Argo CD, treat the observability/ directory as an independent application.
Store the Telemtra API key as a Secret
apiVersion: v1
kind: Namespace
metadata:
name: observability
---
apiVersion: v1
kind: Secret
metadata:
name: telemtra-api-key
namespace: observability
type: Opaque
stringData:
TELEMTRA_API_KEY: "tk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" DaemonSet manifest
apiVersion: v1
kind: ConfigMap
metadata:
name: otelcol-config
namespace: observability
data:
config.yaml: |
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
filelog:
include: [/var/log/pods/*/*/*.log]
start_at: end
include_file_path: true
operators:
- type: container
id: container-parser
hostmetrics:
collection_interval: 30s
scrapers:
cpu: {}
memory: {}
disk: {}
filesystem: {}
load: {}
network: {}
kubeletstats:
collection_interval: 30s
auth_type: serviceAccount
endpoint: "https://${env:NODE_NAME}:10250"
insecure_skip_verify: true
processors:
memory_limiter:
check_interval: 1s
limit_mib: 512
k8sattributes: {}
batch:
timeout: 5s
send_batch_size: 1024
exporters:
otlphttp/telemtra:
endpoint: https://ingest.telemtra.io
headers:
x-api-key: ${env:TELEMTRA_API_KEY}
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, k8sattributes, batch]
exporters: [otlphttp/telemtra]
metrics:
receivers: [otlp, hostmetrics, kubeletstats]
processors: [memory_limiter, k8sattributes, batch]
exporters: [otlphttp/telemtra]
logs:
receivers: [otlp, filelog]
processors: [memory_limiter, k8sattributes, batch]
exporters: [otlphttp/telemtra]
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: otelcol
namespace: observability
spec:
selector:
matchLabels: { app: otelcol }
template:
metadata:
labels: { app: otelcol }
spec:
serviceAccountName: otelcol
hostNetwork: true
containers:
- name: otelcol
image: otel/opentelemetry-collector-contrib:latest
args: ["--config=/conf/config.yaml"]
env:
- name: TELEMTRA_API_KEY
valueFrom:
secretKeyRef:
name: telemtra-api-key
key: TELEMTRA_API_KEY
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
volumeMounts:
- { name: config, mountPath: /conf }
- { name: varlogpods, mountPath: /var/log/pods, readOnly: true }
- { name: varlibdockercontainers, mountPath: /var/lib/docker/containers, readOnly: true }
volumes:
- { name: config, configMap: { name: otelcol-config } }
- { name: varlogpods, hostPath: { path: /var/log/pods } }
- { name: varlibdockercontainers, hostPath: { path: /var/lib/docker/containers } } That single manifest covers pod log collection, node metrics, kubelet pod metrics, and OTLP-from-app ingest, all forwarded to Telemtra with k8s.pod.name, k8s.namespace.name, and k8s.deployment.name attributes attached automatically.
Sidecar pattern
For pods that should not depend on an in-cluster Service or for high-volume apps that need their own buffer, run the Collector as a sidecar:
spec:
containers:
- name: app
image: my-app:1.0
env:
- { name: OTEL_EXPORTER_OTLP_ENDPOINT, value: "http://localhost:4318" }
- { name: OTEL_SERVICE_NAME, value: "checkout-api" }
- name: otelcol
image: otel/opentelemetry-collector-contrib:latest
args: ["--config=/conf/config.yaml"]
volumeMounts:
- { name: otel-config, mountPath: /conf } Gateway deployment
If you operate many clusters or want strict egress policy (one identity, one egress rule, one quota), run a gateway Collector behind a Service and point DaemonSets at it. The gateway holds the API key; node DaemonSets do not need it.
Container log collection
The filelog receiver with the container operator parses the container runtime's log format (CRI-O or containerd) and extracts k8s.pod.name, k8s.namespace.name, and k8s.container.name. Combined with k8sattributes, every log line lands in Telemtra fully attributed.
Pod and node metrics
hostmetrics covers the node (CPU, memory, disk, filesystem, network). kubeletstats covers per-pod metrics from the kubelet (container CPU and memory usage). For cluster-level metrics (deployment status, HPA state), add the k8s_cluster receiver to a single gateway Collector, not the DaemonSet.
OpenTelemetry Operator
For larger environments, install the OpenTelemetry Operator via Helm. It manages Collector deployments via OpenTelemetryCollector CRDs and supports auto-instrumentation injection: annotate a pod with instrumentation.opentelemetry.io/inject-python: "true" and the operator attaches the language SDK as an init container.
Helm chart
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm install otelcol open-telemetry/opentelemetry-collector \
--namespace observability --create-namespace \
--set mode=daemonset \
--set image.repository=otel/opentelemetry-collector-contrib \
--set-string config.exporters."otlphttp/telemtra".endpoint=https://ingest.telemtra.io \
--set-string config.exporters."otlphttp/telemtra".headers.x-api-key=${TELEMTRA_API_KEY} Troubleshooting
- No logs from filelog. Confirm
/var/log/podsis mounted into the Collector container and the path matches your container runtime (CRI-O uses a different layout from containerd). - kubeletstats 401. The Collector ServiceAccount needs
nodes/statsandnodes/metricsin its ClusterRole. - k8sattributes empty. The processor needs RBAC to list pods. Use the chart-provided ClusterRole or copy from the Operator docs.
- High egress cost. Increase
batch.timeoutto 10s and enable gzip on the exporter to reduce bytes.
Pair this with Data Shaper to drop noisy logs at ingest. For protocol details see What is OTLP? New to Telemtra? The quickstart includes a Kubernetes Secret + env block you can paste straight into a Deployment. Create a Telemtra workspace for an API key.