Kubernetes observability met OpenTelemetry: de Collector deployen en workloads instrumenteren

Prometheus geeft je metrics. Fluent Bit verstuurt logs. Geen van beide produceert traces, en geen van beide spreekt een formaat dat elke backend begrijpt. In deze tutorial deploy je de OpenTelemetry Operator op Kubernetes, zet je twee Collectors op (DaemonSet voor node-niveau, Deployment voor clusterniveau), instrumenteer je workloads automatisch zonder applicatiecode aan te raken, en route je traces, metrics en logs naar de backends die je zelf kiest.

Inhoudsopgave

Wat je leert

Aan het eind van deze tutorial draait de OpenTelemetry Operator op je cluster, verzamelen twee Collectors de juiste signalen op het juiste niveau, en produceert minstens een workload traces zonder dat er ook maar een regel applicatiecode veranderd is. Je begrijpt waarom er twee Collectors zijn in plaats van een, welke receivers bij welke mode horen, en hoe je de output routeert naar Prometheus, Grafana Tempo of Jaeger.

Vereisten

Zorg dat je het volgende bij de hand hebt:

  • Een draaiend Kubernetes-cluster, versie 1.25 of nieuwer. Een managed cluster (GKE, EKS, AKS) of een lokaal kind/minikube-cluster werkt allebei. Deze tutorial is geschreven tegen OpenTelemetry Operator v0.148.0, uitgebracht in maart 2026.
  • Helm 3.9 of nieuwer, geconfigureerd voor je cluster. De Operator-chart vereist Helm 3.9+.
  • kubectl met cluster-admin-rechten. De Operator installeert CRDs en clusterwijde resources.
  • Een observability-backend om naartoe te sturen. In deze tutorial gebruik ik Prometheus en Grafana Tempo als voorbeeld; heb je al Prometheus draaien via kube-prometheus-stack, dan hergebruik je die en voeg je alleen Tempo toe voor traces.
  • Basiskennis van Kubernetes Deployments, DaemonSets en pod-annotaties. Is TLS-automatisering nieuw voor je, dan legt de cert-manager tutorial die voorwaarde uit.

Wat OpenTelemetry toevoegt aan je Kubernetes-stack

Als je al Prometheus hebt die metrics scrapet en Fluent Bit die logs verstuurt, is de eerlijke vraag wat OpenTelemetry er dan nog aan toevoegt. Het antwoord heeft drie delen.

Een derde signaaltype dat je waarschijnlijk nog niet hebt: distributed traces. Metrics vertellen je dat de checkout-API traag is. Logs vertellen je wat de checkout-API printte terwijl hij traag was. Traces laten zien wat het volledige requestpad was over de vijf services die de checkout-API aanriep, waar de tijd in elke hop bleef hangen, en welke hop de tail-latency veroorzaakte. Prometheus produceert geen traces. Fluent Bit ook niet. Zonder traces is latency-analyse in een service mesh gewoon gokken.

Een enkel wire-formaat. OpenTelemetry definieert het OTLP-protocol, een gedeelde encoding voor traces, metrics en logs over gRPC (poort 4317) en HTTP (poort 4318). Elke grote backend spreekt OTLP inmiddels native: Prometheus via OTLP-ingest, Grafana Tempo, Jaeger, Elasticsearch APM, Datadog, Honeycomb, New Relic. Je kiest de backend. En je hoeft nooit opnieuw te instrumenteren als je later wisselt.

Auto-instrumentatie zonder codewijzigingen. De OpenTelemetry Operator injecteert een init-container in geannoteerde pods die de language runtime voorziet van een OpenTelemetry-agent nog voordat je applicatie start. Java, Python, Node.js, .NET, Go, Apache HTTPD en Nginx zijn ondersteund. Je voegt een annotatie toe. Traces verschijnen.

Dat is waarom OpenTelemetry de moeite waard is zelfs als je al een werkende Prometheus-setup hebt. Het vult een gat in plaats van te vervangen wat werkt.

Architectuur: signalen, Collector, Operator

De architectuur heeft drie lagen.

Signalen. Traces, metrics en logs. Dat zijn de drie telemetriesignalen die OpenTelemetry definieert. Je applicatie stuurt ze uit, Collectors ontvangen, transformeren en exporteren ze.

Collectors. Een Collector is een standalone proces dat telemetrie ontvangt, door een pipeline van processors stuurt, en naar een of meer backends exporteert. Collectors schalen horizontaal en zijn backend-onafhankelijk. Op Kubernetes is de officiele aanbeveling om twee Collectors te draaien:

  • Een DaemonSet Collector op elke node voor node-lokale telemetrie (kubelet-stats, container-logs, host-metrics) en om OTLP te ontvangen van workloads op diezelfde node. "A daemonset is used to guarantee that this instance of the collector is installed on all nodes."
  • Een Deployment Collector met precies een replica voor clusterwijde telemetrie (cluster-scoped metrics, Kubernetes-events). "A deployment with exactly one replica ensures that we don't produce duplicate data."

Operator. De OpenTelemetry Operator is een Kubernetes-operator die twee CRDs reconciles: OpenTelemetryCollector (die een Collector-deployment beschrijft) en Instrumentation (die beschrijft hoe pods geauto-instrumenteerd worden). De Operator injecteert ook auto-instrumentatie init-containers in pods met de juiste annotatie.

Je kunt de Collector ook zonder de Operator draaien, met ruwe manifests. Je verliest dan wel de CRD-gedreven workflow, de auto-instrumentatie-injector en de target allocator voor Prometheus-metrics. Voor een productiecluster is de Operator de extra component waard.

Wat OpenTelemetry niet is

Drie misverstanden komen consistent voorbij.

OpenTelemetry vervangt Prometheus niet. OpenTelemetry definieert signalen, SDKs en een wire-formaat. Het bevat geen time-series database, geen querytaal zoals PromQL, en geen alerting engine. Je hebt nog steeds een metrics-backend nodig. Prometheus blijft daar een prima keuze voor; de OTLP-receiver in Prometheus 3.x accepteert metrics direct vanuit Collectors. OpenTelemetry zit voor Prometheus, niet in plaats ervan.

Auto-instrumentatie vereist geen codewijzigingen. Dat is nou net het hele punt. De Operator's automatische instrumentatie injecteert een init-container met de naam opentelemetry-auto-instrumentation in elke pod met de juiste annotatie. Die init-container kopieert de language-agent naar een gedeeld volume; je app-container laadt 'm via environment variables die de Operator zet. Je Dockerfile, je broncode, je CI-pijplijn: alles blijft ongewijzigd.

Een Collector-deployment is niet genoeg voor productie. Een enkele Deployment Collector kan geen kubelet-stats van elke node verzamelen (draait maar op een node) en kan geen /var/log/pods/*/*/*.log tailen over het hele cluster. Een enkele DaemonSet Collector zou weer cluster-scoped data dupliceren (een kopie per node) bij het scrapen van de Kubernetes API. De DaemonSet + Deployment-splitsing is geen optimalisatie. Het is een correctheidsvereiste.

cert-manager en de OpenTelemetry Operator installeren

De Operator gebruikt validating en mutating admission webhooks, en die hebben TLS nodig. De Helm-chart biedt drie manieren om die TLS te leveren: cert-manager (aanbevolen), zelf-gegenereerde self-signed certs uit de chart, of certs die je zelf aanlevert. Deze tutorial gebruikt cert-manager omdat dat de route is die meeschaalt naar andere operators die je later toevoegt.

Stap 1: installeer cert-manager

# cert-manager v1.20.2 via OCI-registry (aanbevolen door jetstack)
helm install cert-manager oci://quay.io/jetstack/charts/cert-manager \
  --version v1.20.2 \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true

De cert-manager installatiedocs beschrijven ook de legacy HTTP-repositorymethode als je omgeving OCI-registries blokkeert.

Verwachte output. helm list -n cert-manager toont cert-manager in deployed state, en kubectl get pods -n cert-manager toont drie pods running: de controller, de webhook en de cainjector.

Stap 2: voeg de OpenTelemetry Helm-repository toe

helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update

Stap 3: installeer de Operator

# Operator v0.148.0; overschrijft de default collector-image naar de
# Kubernetes-aware distributie (otelcol-k8s) die de k8sattributes-,
# k8scluster-, kubeletstats- en filelog-componenten bundelt.
helm install opentelemetry-operator open-telemetry/opentelemetry-operator \
  --namespace opentelemetry \
  --create-namespace \
  --set "manager.collectorImage.repository=ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-k8s" \
  --set admissionWebhooks.certManager.enabled=true

Die manager.collectorImage.repository-override is belangrijk: de default collector-image is de minimale otelcol-distributie en mist Kubernetes-specifieke receivers. De opentelemetry-collector-k8s-distributie bundelt de receivers die je hier wel nodig hebt.

Verwachte output. Binnen een minuut:

kubectl get pods -n opentelemetry

Toont een pod opentelemetry-operator-<hash> in Running state. kubectl get crd | grep opentelemetry toont opentelemetrycollectors.opentelemetry.io en instrumentations.opentelemetry.io als geinstalleerde CRDs.

Hangt de Operator-pod in ContainerCreating met een webhook-foutmelding, dan heeft cert-manager het certificaat nog niet uitgegeven. Wacht 30 seconden en check kubectl get certificate -n opentelemetry; zodra het certificaat op Ready=True staat, start de webhook.

Een DaemonSet Collector voor node-niveau signalen

De DaemonSet Collector draait op elke node en heeft vier taken:

  1. OTLP ontvangen van lokale workloads op poort 4317 (gRPC) en 4318 (HTTP). Door dit local-only te houden voorkom je onnodige netwerkhops.
  2. Kubelet-stats verzamelen (pod- en container-CPU/geheugen) via de kubeletstats-receiver.
  3. Container-logs tailen uit /var/log/pods/*/*/*.log via de filelog-receiver.
  4. Elk signaal verrijken met Kubernetes-metadata (podnaam, namespace, labels) via de k8sattributes-processor.

Pas het volgende manifest toe:

# otel-collector-daemonset.yaml
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
  name: otel-agent
  namespace: opentelemetry
spec:
  mode: daemonset
  # De k8s-distributie bevat de receivers die deze config nodig heeft.
  image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-k8s:0.120.0
  # Host-mounts voor log-tailing en kubelet-stats.
  volumeMounts:
    - name: varlogpods
      mountPath: /var/log/pods
      readOnly: true
    - name: varlibdockercontainers
      mountPath: /var/lib/docker/containers
      readOnly: true
  volumes:
    - name: varlogpods
      hostPath:
        path: /var/log/pods
    - name: varlibdockercontainers
      hostPath:
        path: /var/lib/docker/containers
  # Moet op elke node draaien, inclusief control plane.
  tolerations:
    - key: node-role.kubernetes.io/control-plane
      operator: Exists
      effect: NoSchedule
  # Nodig zodat kubeletstats tegen de lokale kubelet kan authenticeren.
  env:
    - name: K8S_NODE_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName
  config:
    receivers:
      # Applicaties sturen OTLP naar de lokale DaemonSet via localhost.
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318
      # Pod/container-metrics vanaf de lokale kubelet.
      kubeletstats:
        auth_type: serviceAccount
        collection_interval: 30s
        endpoint: https://${env:K8S_NODE_NAME}:10250
        insecure_skip_verify: true     # acceptabel voor kubelet on-node
      # Container stdout/stderr-logs.
      filelog:
        include:
          - /var/log/pods/*/*/*.log
        exclude:
          - /var/log/pods/opentelemetry_*/*/*.log   # voorkomt log-loops
        start_at: end
        include_file_path: true

    processors:
      # Verrijkt signalen met pod/namespace/label-metadata.
      k8sattributes:
        auth_type: serviceAccount
        passthrough: false
        extract:
          metadata:
            - k8s.namespace.name
            - k8s.pod.name
            - k8s.pod.uid
            - k8s.node.name
        pod_association:
          - sources:
              - from: resource_attribute
                name: k8s.pod.uid
          - sources:
              - from: connection
      batch: {}                                     # altijd batchen voor export

    exporters:
      # Stuur alles door naar de cluster-Collector (volgende sectie).
      otlp:
        endpoint: otel-gateway.opentelemetry.svc.cluster.local:4317
        tls:
          insecure: true                            # in-cluster verkeer

    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [k8sattributes, batch]
          exporters: [otlp]
        metrics:
          receivers: [otlp, kubeletstats]
          processors: [k8sattributes, batch]
          exporters: [otlp]
        logs:
          receivers: [otlp, filelog]
          processors: [k8sattributes, batch]
          exporters: [otlp]
kubectl apply -f otel-collector-daemonset.yaml

Verwachte output. kubectl get daemonset -n opentelemetry toont otel-agent-collector met DESIRED gelijk aan het aantal nodes in je cluster, en dezelfde waarde bij READY. Op elke node luistert nu een Collector op het host-netwerk voor OTLP op 4317/4318 en scrapet de lokale kubelet.

Draait de DaemonSet maar laat kubeletstats TLS-fouten zien in de logs, dan is insecure_skip_verify: true de pragmatische fix. Voor een strakke productie-setup mount je liever de kubelet CA-bundle; de kubeletstats-receiver docs laten beide paden zien.

Een Deployment Collector voor clusterniveau signalen

De Deployment Collector draait met precies een replica en behandelt signalen die alleen op clusterniveau zinvol zijn:

  1. Clusterwijde metrics (nodecondities, podfases, deployment-replicacounts) via de k8scluster-receiver.
  2. Kubernetes-events via de k8sobjects-receiver.
  3. Telemetrie ontvangen en doorsturen vanaf de DaemonSet Collectors naar de backends.
# otel-collector-deployment.yaml
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
  name: otel-gateway
  namespace: opentelemetry
spec:
  mode: deployment
  replicas: 1                         # cluster-scoped receivers vragen om exact een
  image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-k8s:0.120.0
  config:
    receivers:
      # DaemonSets pushen OTLP hierheen.
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318

      # Clusterwijde metrics (podfases, nodecondities, deploymentstatus).
      k8s_cluster:
        auth_type: serviceAccount
        collection_interval: 30s
        node_conditions_to_report: [Ready, MemoryPressure, DiskPressure, PIDPressure]

      # Kubernetes-events als logrecords.
      k8sobjects:
        auth_type: serviceAccount
        objects:
          - name: events
            mode: watch
            group: events.k8s.io

    processors:
      batch: {}

    exporters:
      # Traces -> Tempo.
      otlp/tempo:
        endpoint: tempo.observability.svc.cluster.local:4317
        tls:
          insecure: true
      # Metrics -> Prometheus via OTLP-ingest (Prometheus 3.x).
      otlphttp/prometheus:
        endpoint: http://kube-prom-stack-kube-prom-prometheus.monitoring.svc.cluster.local:9090/api/v1/otlp
        tls:
          insecure: true
      # Logs -> een Loki OTLP-endpoint, of ruil om voor je eigen log-backend.
      otlphttp/loki:
        endpoint: http://loki-gateway.logging.svc.cluster.local/otlp
        tls:
          insecure: true

    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [batch]
          exporters: [otlp/tempo]
        metrics:
          receivers: [otlp, k8s_cluster]
          processors: [batch]
          exporters: [otlphttp/prometheus]
        logs:
          receivers: [otlp, k8sobjects]
          processors: [batch]
          exporters: [otlphttp/loki]
kubectl apply -f otel-collector-deployment.yaml

Verwachte output. kubectl get deployment -n opentelemetry toont otel-gateway-collector met READY 1/1. De Operator heeft ook een Service aangemaakt met de naam otel-gateway-collector.opentelemetry.svc.cluster.local op poort 4317 en 4318, precies waar de DaemonSet's OTLP-exporter op target.

Workloads auto-instrumenteren met de Instrumentation CRD

De auto-instrumentatiefeature van de Operator injecteert een language-agent in pods met de juiste annotatie. Je configureert dat een keer per namespace met een Instrumentation-resource.

Stap 1: maak een Instrumentation-resource

# instrumentation.yaml
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: default
  namespace: production
spec:
  # Traces en metrics van geinstrumenteerde apps gaan hierheen.
  # Wijst naar de lokale DaemonSet Collector op dezelfde node.
  exporter:
    endpoint: http://$(K8S_NODE_IP):4318
  propagators:
    - tracecontext
    - baggage
    - b3
  sampler:
    type: parentbased_traceidratio
    argument: "0.25"                  # sample 25% van alle root traces
  # Language-defaults; overschrijf per taal waar nodig.
  java:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest
  python:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:latest
  nodejs:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:latest
  dotnet:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-dotnet:latest
kubectl apply -f instrumentation.yaml

De $(K8S_NODE_IP)-placeholder wordt door de Operator bij pod-start ingevuld met het node-IP, zodat elke pod OTLP naar de DaemonSet Collector op de eigen node stuurt. Dat scheelt cross-node verkeer voor high-throughput signalen.

Stap 2: annoteer je workloads

Voeg een language-specifieke annotatie toe aan de pod-template. Voor een Java-Deployment:

# order-api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-api
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-api
  template:
    metadata:
      labels:
        app: order-api
      annotations:
        # Een annotatie. Meer hoeft er niet aan je workload te veranderen.
        instrumentation.opentelemetry.io/inject-java: "true"
    spec:
      containers:
        - name: app
          image: registry.example.nl/order-api:1.14.0
          ports:
            - containerPort: 8080

De annotatiewaarden voor andere runtimes:

  • Python: instrumentation.opentelemetry.io/inject-python: "true"
  • Node.js: instrumentation.opentelemetry.io/inject-nodejs: "true"
  • .NET: instrumentation.opentelemetry.io/inject-dotnet: "true"
  • Go: instrumentation.opentelemetry.io/inject-go: "true" (eBPF-gebaseerd, nog werk in uitvoering vanaf Operator v0.148.0; vereist verhoogde rechten)
  • Apache HTTPD: instrumentation.opentelemetry.io/inject-apache-httpd: "true"
  • Nginx: instrumentation.opentelemetry.io/inject-nginx: "true"

Verwacht gedrag. Bij de volgende rollout van order-api toont kubectl describe pod op een nieuwe pod een init-container opentelemetry-auto-instrumentation-java en environment variables als OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME=order-api en JAVA_TOOL_OPTIONS=-javaagent:/otel-auto-instrumentation-java/javaagent.jar. De applicatiecontainer zelf blijft ongewijzigd.

Kanttekening over Go

Go auto-instrumentatie werkt fundamenteel anders. De Go-compiler produceert een statisch binary zonder runtime-agent-hook, dus injecteert de Operator een eBPF-gebaseerde sidecar die het binary vanaf kernelniveau volgt. Die sidecar heeft privileged: true en runAsUser: 0 nodig, en je moet instrumentation.opentelemetry.io/otel-go-auto-target-exe zetten op het absolute pad van je Go-binary binnen de container. Voor productie-Go-workloads is de OpenTelemetry SDK in de broncode opnemen meestal toch de schonere route.

Signalen routeren naar backends

Het Deployment Collector-voorbeeld hierboven laat al drie gangbare routes zien. Een korte naslag:

Traces naar Grafana Tempo. Installeer Tempo via z'n Helm-chart en wijs otlp/tempo op het OTLP-ingest-endpoint op poort 4317. Tempo slaat traces op in object storage en bevraagt ze via TraceQL in Grafana.

Traces naar Jaeger. Jaeger heeft sinds versie 1.35 native OTLP-ondersteuning. Vervang de Tempo-exporter door:

otlp/jaeger:
  endpoint: jaeger-collector.observability.svc.cluster.local:4317
  tls:
    insecure: true

Metrics naar Prometheus. Prometheus 3.x accepteert OTLP-metrics direct op /api/v1/otlp/v1/metrics. Zet de feature aan via --web.enable-otlp-receiver op de Prometheus-pod; gebruik je kube-prometheus-stack, zet dan prometheus.prometheusSpec.enableFeatures: [otlp-write-receiver] in de chart-values.

Metrics naar een bestaande Prometheus-only backend. Gebruik dan de prometheusremotewrite-exporter in plaats van otlphttp. Die spreekt het legacy Prometheus remote-write-protocol.

Logs naar Loki. Loki 3.0+ accepteert OTLP op /otlp/v1/logs. Wijs de otlphttp/loki-exporter op de Loki-gateway.

Logs naar Elasticsearch. Gebruik de elasticsearch-exporter (zit in de otelcol-contrib-distributie, niet in otelcol-k8s). Heb je Elasticsearch al als log-backend via Fluent Bit en de EFK-stack, dan is Fluent Bit voor logs houden en OpenTelemetry alleen voor traces en metrics gebruiken een prima splitsing.

De pipeline end-to-end verifieren

Voordat je 'em uitroept als af, stuur je signalen door de volledige pipeline.

Controleer de Collector-logs

# DaemonSet-kant
kubectl logs -n opentelemetry -l app.kubernetes.io/name=otel-agent-collector --tail=50

# Deployment-kant
kubectl logs -n opentelemetry -l app.kubernetes.io/name=otel-gateway-collector --tail=50

Zoek naar regels met Everything is ready. Begin running and processing data. Een connection refused aan de DaemonSet-kant wijst meestal op de Service-naam van de Deployment Collector; een TLS-fout op de backend-exporter wijst op certificate trust.

Genereer een trace

Deploy een geannoteerde workload en raak een HTTP-endpoint aan. Voor een snelle smoketest bevat de OpenTelemetry demo-applicatie een microservices-stack die traces, metrics en logs in realistische patronen produceert.

Bevestig dat traces in Tempo landen

Kies in Grafana de Tempo-datasource en zoek op spans met service.name = order-api (of welke servicenaam de Operator ook gezet heeft). Traces verschijnen binnen 30 seconden.

Bevestig dat metrics in Prometheus landen

Draai in de Prometheus-UI de query {__name__=~".+", job="otel-collector"}. Je moet een resultaatset met meer dan nul rijen zien (Status > Targets is hier niet relevant, want dit is push, geen pull).

Bevestig dat logs in Loki landen

Draai in Grafana Explore met de Loki-datasource de query {k8s_namespace_name="production"}. Logs met verrijkte Kubernetes-metadata verschijnen.

Checkpoint. Landen alle drie de signalen in hun respectievelijke backends en dragen ze podnaam, namespace en node-labels als resource-attributen, dan werkt de pipeline. De k8sattributes-processor is de component die die verrijking doet; ontbreken labels, check dan of zijn ClusterRole toestemming heeft om pods te listen.

Veelvoorkomende valkuilen

De default collector-image is niet de k8s-distributie. De simpele otelcol-image mist k8sattributes, k8scluster, kubeletstats en filelog. Override manager.collectorImage.repository altijd naar de opentelemetry-collector-k8s-image bij het installeren van de Operator.

Twee Collectors die dubbele data produceren. Zie je elke cluster-scoped metric dubbel, dan draait de k8s_cluster-receiver in zowel de DaemonSet als de Deployment Collector. Verplaats 'em naar alleen de Deployment, en houd in de DaemonSet alleen node-lokale receivers.

Webhook-fouten na Operator-installatie. Mislukt OpenTelemetryCollector applyen met failed to call webhook: x509 certificate signed by unknown authority, dan heeft cert-manager het webhook-certificaat nog niet uitgegeven. Check kubectl get certificate -n opentelemetry -o wide en wacht tot Ready=True. De Operator installeren voordat cert-manager helemaal op is gekomen is de gangbare oorzaak.

Log-loops van de filelog-receiver. De DaemonSet Collector tailt /var/log/pods/*/*/*.log, en daar zitten de Collector's eigen logs tussen. Zonder de exclude-pattern in de filelog-config wordt elke Collector-logregel een logrecord die de Collector zelf weer verwerkt en exporteert, wat meer logregels oplevert. De opentelemetry-collector Helm-chart documentatie roept dit expliciet uit.

Auto-instrumentatie-annotatie op de Deployment-spec in plaats van op de pod-template. De injector kijkt naar pod-metadata, niet naar Deployment-metadata. Zet de annotatie onder spec.template.metadata.annotations, niet onder de metadata.annotations van de Deployment zelf.

otlp en otlphttp door elkaar halen. Dat zijn verschillende exporters met verschillende config-vormen. otlp is gRPC; otlphttp is HTTP/protobuf. Een HTTP-endpoint in otlp zetten (of andersom) levert verwarrende connection-errors op.

Wat je hebt geleerd

Je hebt nu de OpenTelemetry Operator Collectors reconcileren op je cluster. Een DaemonSet Collector op elke node verzorgt node-lokale signalen en ontvangt OTLP van workloads op dezelfde node. Een Deployment Collector met een replica verzamelt cluster-scoped metrics en events, en stuurt alles door naar de backends die je kiest. Auto-instrumentatie injecteert een language-agent in pods via een enkele annotatie, zonder codewijzigingen. Traces, metrics en logs landen in Tempo, Prometheus en Loki met volledige Kubernetes-metadata.

Je weet ook waar de scherpe randjes zitten: de collector-image-override, de DaemonSet-vs-Deployment-splitsing als correctheidsvereiste in plaats van optimalisatie, de cert-manager-afhankelijkheid, en de log-loop-valkuil in de filelog-receiver.

Verder lezen

  • Voeg PrometheusRule-alerts toe op de metrics die je nu verzamelt. De Prometheus-tutorial behandelt rule-writing en Alertmanager-routing.
  • Rechtvaardigt je log-volume het, houd dan Fluent Bit voor stdout-logs en gebruik OpenTelemetry alleen voor traces en metrics. De Fluent Bit-tutorial loopt die setup door.
  • Hard de Operator-TLS uit met een echte cert-manager ClusterIssuer in plaats van de self-signed default; de cert-manager-tutorial bevat dat patroon.

Terugkerende server- of deploymentproblemen?

Ik help teams productie betrouwbaar maken met CI/CD, Kubernetes en cloud—zodat fixes blijven en deploys geen stress meer zijn.

Bekijk DevOps consultancy

Doorzoek deze site

Begin met typen om te zoeken, of blader door de kennisbank en blog.