OTel Collector on Kubernetes
The OpenTelemetry Collector Contrib distribution can be deployed on Kubernetes as a DaemonSet using the opentelemetry-collector Helm chart. It collects traces, metrics, logs, Kubernetes events, and infrastructure objects and forwards them to Kloudfuse over OTLP HTTP.
Prerequisites
-
Helm installed and configured against your cluster.
-
The external hostname of your Kloudfuse cluster.
-
If ingestion authentication is enabled, an API key — see Ingestion Authentication with API Key.
-
OTel Collector Contrib version 0.130.0 or later (required for
k8s_leader_electorextension used in system-level metrics).
Install the Collector
Add the OTel Helm repository and install:
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
helm upgrade --install opentelemetry-collector open-telemetry/opentelemetry-collector \
-f helm-values.yaml \
--namespace otel \
--create-namespace
Basic Configuration
The following helm-values.yaml configures the OTel Collector to receive OTLP data and export metrics to Kloudfuse.
Use https or http for the endpoint scheme depending on whether TLS is enabled on your Kloudfuse cluster:
image:
repository: "otel/opentelemetry-collector-contrib"
config:
exporters:
debug:
verbosity: basic
otlphttp:
tls:
insecure: true # remove if using https
metrics_endpoint: https://<kloudfuse-hostname>/ingester/otlp/metrics
headers:
Kf-Api-Key: <token> # omit if auth is not enabled
extensions:
health_check: {}
processors:
batch:
timeout: 10s
resourcedetection:
detectors: [env, eks, ec2, gcp, aks, azure]
override: false
timeout: 2s
receivers:
otlp:
protocols:
grpc:
endpoint: ${env:MY_POD_IP}:4317
http:
cors:
allowed_origins:
- http://*
- https://*
endpoint: 0.0.0.0:4318
service:
extensions: [health_check]
pipelines:
metrics:
receivers: [otlp]
processors: [batch, resourcedetection]
exporters: [otlphttp]
mode: daemonset
service:
enabled: true
ports:
metrics:
enabled: true
otlp:
enabled: true
otlp-http:
enabled: true
presets:
kubernetesAttributes:
enabled: true
resources: {}
The memory_ballast extension was removed in OTel Collector Contrib 0.97.0 and later.
Remove it from any existing configurations — it will cause a startup failure with unknown type: "memory_ballast".
|
When running multiple OTel Collector DaemonSets on the same cluster (for example, one for logs and one for metrics), the default hostPorts (4317, 4318, 8888, etc.) conflict because only one DaemonSet pod can bind a given hostPort per node.
Disable the ports that are not needed for each collector:
|
ports:
otlp:
enabled: false
otlp-http:
enabled: false
metrics:
enabled: false
Logs
To add a logs pipeline, set the logs_endpoint on the exporter and add a logs pipeline to the service:
config:
exporters:
otlphttp:
logs_endpoint: https://<kloudfuse-hostname>/ingester/otlp/v1/logs
metrics_endpoint: https://<kloudfuse-hostname>/ingester/otlp/metrics
traces_endpoint: https://<kloudfuse-hostname>/ingester/otlp/traces
service:
pipelines:
logs:
receivers: [otlp]
processors: [batch, k8sattributes, resourcedetection]
exporters: [otlphttp]
metrics:
receivers: [otlp]
processors: [batch, k8sattributes, resourcedetection]
exporters: [otlphttp]
traces:
receivers: [otlp]
processors: [batch, k8sattributes, resourcedetection]
exporters: [otlphttp]
Traces
To add a traces pipeline, set the traces_endpoint and add a traces pipeline:
config:
exporters:
otlphttp:
traces_endpoint: https://<kloudfuse-hostname>/ingester/otlp/traces
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch, k8sattributes, resourcedetection]
exporters: [otlphttp]
Kubernetes and System-Level Metrics
Kloudfuse APM Services correlate APM data with Kubernetes infrastructure metrics on the Infra tab of a service detail page.
Enable the hostmetrics, kubeletstats, and k8s_cluster receivers to collect node, pod, and cluster-level metrics alongside application telemetry.
The k8s_cluster receiver uses the k8s_leader_elector extension to prevent duplicate cluster metrics in multi-Collector deployments (requires OTel Collector Contrib 0.130.0+).
command:
extraArgs: [--feature-gates=receiver.kubeletstats.enableCPUUsageMetrics]
config:
extensions:
health_check: {}
k8s_leader_elector/k8scluster:
auth_type: serviceAccount
lease_name: k8sclusterlease
lease_namespace: default # namespace where the lease resource will be created
processors:
batch:
timeout: 10s
resource:
attributes:
- key: kf_metrics_agent (1)
value: "otlp"
action: upsert
resourcedetection:
detectors: [env, eks, ec2, gcp, aks, azure]
override: false
timeout: 2s
transform/k8spodphase: (2)
metric_statements:
- context: datapoint
statements:
- 'set(attributes["phase"], "Pending") where metric.name == "k8s.pod.phase" and value_int == 1'
- 'set(attributes["phase"], "Running") where metric.name == "k8s.pod.phase" and value_int == 2'
- 'set(attributes["phase"], "Succeeded") where metric.name == "k8s.pod.phase" and value_int == 3'
- 'set(attributes["phase"], "Failed") where metric.name == "k8s.pod.phase" and value_int == 4'
- 'set(attributes["phase"], "Unknown") where metric.name == "k8s.pod.phase" and value_int == 5'
receivers:
hostmetrics:
collection_interval: 30s
scrapers:
cpu:
metrics:
system.cpu.utilization:
enabled: true
memory:
metrics:
system.memory.utilization:
enabled: true
disk:
filesystem:
metrics:
system.filesystem.utilization:
enabled: true
kubeletstats:
collection_interval: 30s
auth_type: serviceAccount
endpoint: "https://${env:K8S_NODE_NAME}:10250"
insecure_skip_verify: true
node: '${env:K8S_NODE_NAME}'
collect_all_network_interfaces:
pod: true
node: true
extra_metadata_labels:
- k8s.volume.type
metric_groups: [node, pod, container, volume]
metrics:
k8s.container.cpu.node.utilization:
enabled: true
k8s.pod.cpu.node.utilization:
enabled: true
k8s.container.memory.node.utilization:
enabled: true
k8s.pod.memory.node.utilization:
enabled: true
k8s_cluster:
k8s_leader_elector: k8s_leader_elector/k8scluster
collection_interval: 30s
auth_type: serviceAccount
allocatable_types_to_report: [cpu, memory, pods]
metrics:
k8s.node.condition:
enabled: true
node_conditions_to_report: [Ready, MemoryPressure]
resource_attributes:
container.id:
enabled: false
k8s.container.status.last_terminated_reason:
enabled: true
service:
extensions: [health_check, k8s_leader_elector/k8scluster]
pipelines:
metrics:
receivers: [otlp, kubeletstats, hostmetrics, k8s_cluster]
processors: [resource, resourcedetection, transform/k8spodphase, batch]
exporters: [otlphttp]
| 1 | kf_metrics_agent: otlp enables correlation of host metrics with APM services in the Kloudfuse UI. |
| 2 | transform/k8spodphase converts the numeric pod phase value from k8s_cluster into a human-readable string label. |
Kubernetes Events
Kloudfuse ingests Kubernetes events through a dedicated logs pipeline using the k8s_events receiver.
| Run the Collector as a Deployment (not a DaemonSet) when collecting Kubernetes events. Running in DaemonSet mode results in duplicate events. This is a known upstream limitation — see GitHub issue #42266. |
The events pipeline uses a separate logs endpoint and a resource processor to tag events as OTel-sourced:
config:
exporters:
otlphttp/k8sevents:
logs_endpoint: https://<kloudfuse-hostname>/ingester/otlp/k8s_events (1)
receivers:
k8s_events: {}
processors:
resource/k8s_events:
attributes:
- key: kf_events_agent
value: "otlp"
action: upsert
service:
pipelines:
logs/k8s_events: (2)
receivers: [k8s_events]
processors: [resource/k8s_events]
exporters: [otlphttp/k8sevents]
| 1 | Kloudfuse treats data sent to /ingester/otlp/k8s_events as Kubernetes events, not log records. |
| 2 | This must be a separate pipeline from the main logs pipeline — do not add k8s_events to an existing pipeline. |
Grant the Collector service account read access to events:
rules:
- apiGroups: [""]
resources: [events]
verbs: ["get", "list", "watch"]
Verify Events Are Arriving
Kubernetes events collected via the OTel k8s_events receiver are stored in the Logs store, not the Events store.
Query them via the Loki API using the kf_events_agent label:
{kf_events_agent="otlp"}
To filter by namespace or event reason, combine labels:
{kf_events_agent="otlp", k8s_namespace_name="<namespace>"}
OTel Kubernetes events appear in Logs (tagged kf_events_agent=otlp), not in the Events explorer.
This differs from the Datadog Agent path, where Kubernetes events are stored in the Events store under source="kubernetes".
|
Kubernetes Infrastructure Objects
Kloudfuse can ingest Kubernetes infrastructure objects (pods, nodes, deployments, etc.) using the k8sobjects receiver.
Objects appear in the Kloudfuse UI under Infrastructure > Kubernetes.
Each data type requires its own pipeline. Do not reuse an existing logs pipeline for objects. Exporter names must be prefixed with otlphttp/.
|
| Kloudfuse drops objects that have not been updated within 15 minutes. To prevent stale data, configure a pull interval shorter than 15 minutes on each object type. |
Step 1: Configure Exporter
exporters:
otlphttp/k8sobjects:
logs_endpoint: https://<kloudfuse-hostname>/ingester/otlp/k8s_objects
Step 2: Configure Receiver
receivers:
k8sobjects:
objects:
- name: pods
interval: 5m
mode: pull
- name: deployments
- name: replicasets
- name: services
- name: nodes
- name: jobs
- name: cronjobs
- name: daemonsets
- name: statefulsets
- name: persistentvolumes
- name: persistentvolumeclaims
- name: roles
- name: rolebindings
- name: clusterroles
- name: clusterrolebindings
- name: serviceaccounts
- name: ingresses
- name: namespaces
Step 3: Configure Processors
processors:
resource:
attributes:
- key: kf_infra_agent (1)
value: "otlp"
action: upsert
resourcedetection:
detectors: [env, gcp] # replace with your environment: eks, aks, ec2, azure
timeout: 2s
override: false
k8sattributes:
extract:
metadata:
- k8s.namespace.name
- k8s.deployment.name
- k8s.statefulset.name
- k8s.daemonset.name
- k8s.cronjob.name
- k8s.job.name
- k8s.node.name
- k8s.pod.name
- k8s.pod.uid
- k8s.pod.start_time
- k8s.replicaset.name
| 1 | kf_infra_agent: otlp enables filtering by OTel-sourced objects in the Kloudfuse UI. |
Step 4: Configure Pipeline
service:
pipelines:
logs/k8sobjects:
receivers: [k8sobjects]
processors: [k8sattributes, resource, resourcedetection]
exporters: [otlphttp/k8sobjects]
Step 5: RBAC Permissions
Grant the Collector service account read access to all collected object types:
rules:
- apiGroups: [""]
resources: [namespaces, nodes, pods, services, persistentvolumeclaims, persistentvolumes, serviceaccounts]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: [daemonsets, deployments, replicasets, statefulsets]
verbs: ["get", "list", "watch"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: [clusterroles, clusterrolebindings, roles, rolebindings]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources: [ingresses]
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources: [jobs, cronjobs]
verbs: ["get", "list", "watch"]
| Missing RBAC rules can silently prevent object collection without visible errors in the Collector logs. |
Step 6: Leader Election
Prevent duplicate collection in multi-Collector deployments:
extensions:
leader_election: {}
service:
extensions: [leader_election]
Complete Example
extensions:
leader_election: {}
receivers:
k8sobjects:
objects:
- name: pods
interval: 5m
mode: pull
- name: deployments
- name: replicasets
- name: services
- name: nodes
- name: jobs
- name: cronjobs
- name: daemonsets
- name: statefulsets
- name: persistentvolumes
- name: persistentvolumeclaims
- name: roles
- name: rolebindings
- name: clusterroles
- name: clusterrolebindings
- name: serviceaccounts
- name: ingresses
- name: namespaces
processors:
resource:
attributes:
- key: kf_infra_agent
value: "otlp"
action: upsert
resourcedetection:
detectors: [env, gcp]
timeout: 2s
override: false
k8sattributes:
extract:
metadata:
- k8s.namespace.name
- k8s.pod.name
- k8s.pod.uid
- k8s.node.name
- k8s.deployment.name
- k8s.replicaset.name
- k8s.statefulset.name
- k8s.daemonset.name
- k8s.job.name
- k8s.cronjob.name
exporters:
otlphttp/k8sobjects:
logs_endpoint: https://<kloudfuse-hostname>/ingester/otlp/k8s_objects
service:
extensions: [leader_election]
pipelines:
logs/k8sobjects:
receivers: [k8sobjects]
processors: [k8sattributes, resource, resourcedetection]
exporters: [otlphttp/k8sobjects]
After restarting the Collector, open the Kloudfuse UI at Infrastructure > Kubernetes and confirm that pods, nodes, deployments, and namespaces appear.
Use the filter kf_infra_agent=otlp to view only OTel-sourced objects.