This page installs the community OpenTelemetry Collector Contrib using Kubernetes manifests, configures it to scrape Linkerd proxy and control plane metrics, and verifies that data is flowing to New Relic. Expect this to take about 15 minutes.
Compatibility and requirements
Supported
- Kubernetes cluster (EKS, GKE, AKS, or self-managed) with
kubectlconfigured against it - Linkerd installed and healthy (
linkerd checkpasses) - Manifest-managed cluster configuration (no Helm)
You need
A New Relic ingest license key
Network connectivity to New Relic OTLP endpoints
Injection enabled on the namespaces you want to observe. If it isn't, annotate and restart them:
bash$kubectl annotate namespace <YOUR_NAMESPACE> linkerd.io/inject=enabled$kubectl rollout restart deployment -n <YOUR_NAMESPACE>(Optional) kube-state-metrics running in your cluster, if you want Kubernetes workload entities correlated with Linkerd metrics. If you already have the New Relic Kubernetes integration installed, this is already handled
Install
Copy the manifest below into a file named
otel-collector.yaml.apiVersion: v1kind: ServiceAccountmetadata: { name: my-opentelemetry-collector, namespace: newrelic }---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: { name: my-opentelemetry-collector }rules:- apiGroups: [""]resources: [pods, nodes, nodes/proxy, endpoints, services, namespaces]verbs: [get, list, watch]- apiGroups: ["apps"]resources: [replicasets, deployments, daemonsets, statefulsets]verbs: [get, list, watch]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: { name: my-opentelemetry-collector }roleRef: { apiGroup: rbac.authorization.k8s.io, kind: ClusterRole, name: my-opentelemetry-collector }subjects:- { kind: ServiceAccount, name: my-opentelemetry-collector, namespace: newrelic }---apiVersion: v1kind: ConfigMapmetadata: { name: my-opentelemetry-collector-config, namespace: newrelic }data:config: |receivers:prometheus:config:scrape_configs:- job_name: 'linkerd-controller'kubernetes_sd_configs:- role: podnamespaces: { names: ['linkerd', 'linkerd-viz'] }relabel_configs:- { source_labels: [__meta_kubernetes_pod_container_port_name], action: keep, regex: admin-http }- { source_labels: [__meta_kubernetes_pod_container_name], target_label: component }- { source_labels: [__meta_kubernetes_namespace], target_label: namespace }- { source_labels: [__meta_kubernetes_pod_name], target_label: pod }- job_name: 'linkerd-proxy'kubernetes_sd_configs: [{ role: pod }]relabel_configs:- { source_labels: [__meta_kubernetes_pod_container_name, __meta_kubernetes_pod_container_port_name, __meta_kubernetes_pod_label_linkerd_io_control_plane_ns], action: keep, regex: ^linkerd-proxy;linkerd-admin;linkerd$ }- { source_labels: [__meta_kubernetes_namespace], target_label: namespace }- { source_labels: [__meta_kubernetes_pod_name], target_label: pod }- { source_labels: [__meta_kubernetes_pod_label_linkerd_io_control_plane_ns], target_label: linkerd_control_plane_ns }- { source_labels: [__meta_kubernetes_pod_label_linkerd_io_control_plane_component], target_label: linkerd_control_plane_component }processors:memory_limiter:check_interval: 1slimit_percentage: <MEMORY_LIMIT_PERCENTAGE>spike_limit_percentage: <MEMORY_SPIKE_LIMIT_PERCENTAGE>resource/strip_service:attributes:- { key: service.name, action: delete }- { key: service.instance.id, action: delete }resource/cluster:attributes:- key: k8s.cluster.namevalue: "<YOUR_CLUSTER_NAME>"action: inserttransform/k8s:metric_statements:- context: datapointstatements:- set(attributes["k8s.namespace.name"], attributes["namespace"]) where attributes["namespace"] != nil- set(attributes["k8s.pod.name"], attributes["pod"]) where attributes["pod"] != nil- delete_key(attributes, "instance")transform/deployment:metric_statements:- context: datapointstatements:- set(attributes["k8s.deployment.name"], attributes["k8s.pod.name"]) where attributes["k8s.deployment.name"] == nil and attributes["k8s.pod.name"] != nil- replace_pattern(attributes["k8s.deployment.name"], "-[a-z0-9]+-[a-z0-9]+$", "") where attributes["k8s.deployment.name"] == attributes["k8s.pod.name"]transform/metadata_nullify:metric_statements:- context: metricstatements:- set(description, "")- set(unit, "")batch: {}exporters:otlp_http/newrelic:endpoint: "https://otlp.nr-data.net:4318"headers:api-key: "<YOUR_NR_INGEST_LICENSE_KEY>"service:pipelines:metrics:receivers: [prometheus]processors: [memory_limiter, resource/strip_service, resource/cluster, transform/k8s, transform/deployment, transform/metadata_nullify, batch]exporters: [otlp_http/newrelic]---apiVersion: apps/v1kind: Deploymentmetadata: { name: my-opentelemetry-collector, namespace: newrelic }spec:replicas: 1selector: { matchLabels: { app: my-opentelemetry-collector } }template:metadata:labels: { app: my-opentelemetry-collector }spec:serviceAccountName: my-opentelemetry-collectorsecurityContext:runAsNonRoot: truerunAsUser: 10001containers:- name: opentelemetry-collectorimage: otel/opentelemetry-collector-contrib:0.160.0args: ["--config=/conf/config.yaml"]securityContext:allowPrivilegeEscalation: falsereadOnlyRootFilesystem: truecapabilities: { drop: [ALL] }resources:limits: { cpu: <CPU_LIMIT>, memory: <MEMORY_LIMIT> }volumeMounts:- { name: config, mountPath: /conf }volumes:- name: configconfigMap:name: my-opentelemetry-collector-configitems:- { key: config, path: config.yaml }---apiVersion: v1kind: Servicemetadata: { name: my-opentelemetry-collector, namespace: newrelic }spec:selector: { app: my-opentelemetry-collector }ports:- { name: otlp, port: 4317, targetPort: 4317, protocol: TCP, appProtocol: grpc }- { name: otlp-http, port: 4318, targetPort: 4318, protocol: TCP }팁
This manifest doesn't filter low-value proxy metrics by default. If you want the same filtering NRDOT applies, add a
filter/drop_unusedprocessor. See the NRDOT with Helm page for the filter list.
Configure
Fill in the placeholders in
otel-collector.yamlbefore applying:Field Location What to set resource/clusterprocessor Your Kubernetes cluster name otlp_http/newrelicexporter Your New Relic ingest license key memory_limiterprocessor Memory limit and spike percentages, for example 80and25resources.limitsDeployment container CPU and memory limits for the collector pod Apply the manifest.
bash$kubectl apply -f otel-collector.yaml$kubectl rollout status deployment/my-opentelemetry-collector -n newrelic
Find your data
- Go to one.newrelic.com > All capabilities > All entities.
- Search for your cluster name.
- Select your Linkerd entity to open the built-in dashboard.
The built-in dashboard covers request rate, latency p50/p95/p99, success rate, TCP connections, mTLS certificate status, control plane health, and meshed pod inventory. For detailed information, refer to Find Linkerd data documentation.
Related articles
Linkerd distributed tracing with OpenTelemetry
Enable proxy trace export and instrument your application pods to correlate mesh spans with APM traces.
Collect Linkerd proxy logs
Add a second collector release to collect linkerd-proxy sidecar logs.
Metrics reference
Full list of Linkerd metrics and resource attributes collected by the OTel Collector.