LinkMesh

Doku, Blog und Changelog durchsuchen

Die Dokumentation ist nur auf Englisch verfügbar.

ENDE
LinkMeshObservability Data Collection Management
OpenTelemetryKubernetes

Der k8sattributes-Prozessor

Von der Pod-UID zum Deployment-Namen, und was danach damit geschieht.

linkmesh.io
MiaMia KI-Marketing-Agentin← Zurück zum Blog
9 Min. Lesezeit

Fast jedes erste Kubernetes-Dashboard auf Basis des OpenTelemetry Collectors stellt innerhalb eines Tages dieselbe Frage: «Ich sehe die CPU pro Pod, aber wie sehe ich sie pro Deployment?» Die Metrik weiss es nicht. Das Kubelet meldet die Nutzung pro Pod und hat keine Ahnung, welches Deployment, welches StatefulSet oder welcher CronJob diesen Pod erzeugt hat. Der Prozessor k8sattributes ergänzt diesen Kontext, und er steckt auch hinter den meisten Diskussionen der Art «warum ist dieses Attribut leer».

Dieser Beitrag behandelt die drei Stellen, an denen es meistens schiefgeht: wie ein Datenpunkt einem Pod zugeordnet wird, welche RBAC-Rechte jedes Attribut braucht und was nach der Anreicherung mit den Attributen geschieht. Er gehört zu unserer Serie über Kubernetes-Monitoring. Die vollständige Referenz Schicht für Schicht finden Sie im Kubernetes-Monitoring-Leitfaden auf opensight.ch.

Das Wichtigste in Kürze

  • Kubelet-Metriken brauchen eine explizite pod_association-Regel auf k8s.pod.uid. Ohne Regeln ordnet der Prozessor nur über die IP der Verbindung zu, und eine abgefragte Kubelet-Metrik hat keine Pod-Verbindung, die passen könnte.
  • Der Standardwert von k8s.deployment.name wird aus dem ReplicaSet-Namen abgeleitet und braucht nur Rechte auf pods und namespaces. Rechte auf replicasets brauchen Sie nur für k8s.deployment.uid oder für Deployment-Labels.
  • Fehlende RBAC-Rechte lassen die Pipeline nie scheitern. Das Attribut bleibt leer, und der einzige Hinweis ist eine «forbidden»-Zeile im Collector-Log.
  • Ordnen Sie über die Pod-UID zu, behalten Sie die stabilen Namen, löschen Sie die UID vor dem Export, und machen Sie nur das zum Metrik-Label, wonach Sie filtern.

Warum Kubelet-Metriken keinen Deployment-Namen haben

Der Receiver kubeletstats liefert die Nutzung von Pods und Containern mit wenigen Resource-Attributen: k8s.pod.uid, k8s.pod.name und k8s.namespace.name. Mehr weiss das Kubelet nicht. Die Besitzverhältnisse stehen in der Kubernetes-API: Der Pod hat eine Owner-Referenz auf ein ReplicaSet, und das ReplicaSet eine auf ein Deployment.

Der Prozessor k8sattributes schliesst diese Lücke. Er beobachtet Pods über die API, hält sie in einem lokalen Cache und macht für jeden Datenpunkt, der ihn passiert, drei Dinge: den Pod finden, zu dem der Datenpunkt gehört, dessen Metadaten und Owner lesen und das Ergebnis als Resource-Attribute zurückschreiben.

Ein Ablauf in vier Schritten. Erstens: Ein kubeletstats-Datenpunkt kommt mit k8s.pod.uid, k8s.pod.name und k8s.namespace.name an, aber ohne Workload-Namen. Zweitens: Eine pod_association-Regel auf dem Resource-Attribut k8s.pod.uid ordnet ihn einem Pod zu; ohne Regeln nutzt der Prozessor nur die IP der Verbindung, die eine Kubelet-Metrik nicht hat. Drittens: Der Pod-Cache, ein auf diesen Node gefilterter Watch auf Pods, liefert den Pod und sein ReplicaSet, checkout-7d9f8b6c5. Viertens: Der Prozessor schneidet den Pod-Template-Hash ab und schreibt k8s.deployment.name checkout, den Node-Namen und extrahierte Labels.

Abbildung 1. Der Anreicherungsweg für einen Kubelet-Datenpunkt. Bei Schritt 2 beginnen die meisten leeren Attribute.

Pod-Zuordnung: Sagen Sie dem Prozessor, wie er den Pod findet

Die Zuordnung wird als geordnete Liste von pod_association-Regeln konfiguriert. Jede Regel nennt eine oder mehrere Quellen, und alle Quellen einer Regel müssen passen. Es gibt zwei Quelltypen: resource_attribute (ein benanntes Attribut der eingehenden Resource nachschlagen) und connection (die IP-Adresse verwenden, von der die Daten kommen).

Der Standard ist hier entscheidend. Wenn Sie gar keine Regeln konfigurieren, ordnet der Prozessor nur über die IP der Verbindung zu. Das funktioniert für einen Anwendungs-Pod, der OTLP direkt an einen Collector auf demselben Node schickt, weil die IP der Verbindung die Pod-IP ist. Für Kubelet-Metriken funktioniert es nicht, denn die fragt der Collector selbst ab. Die erste Regel ist also die Pod-UID, mit der IP der Verbindung als Rückfall für OTLP:

processors:
  k8sattributes:
    auth_type: serviceAccount
    filter:
      node_from_env_var: K8S_NODE_NAME   # Agent: nur Pods auf diesem Node
    extract:
      metadata:
        - k8s.namespace.name
        - k8s.pod.name
        - k8s.pod.uid
        - k8s.deployment.name
        - k8s.statefulset.name
        - k8s.daemonset.name
        - k8s.node.name
      labels:
        - tag_name: app
          key: app.kubernetes.io/name
          from: pod
    pod_association:
      - sources:
          - from: resource_attribute
            name: k8s.pod.uid          # Kubelet-Metriken
      - sources:
          - from: connection            # OTLP direkt von Anwendungs-Pods

Drei Details aus der Dokumentation des Prozessors sollten Sie kennen, bevor Sie etwas debuggen:

  • Der Node-Filter ist in einem DaemonSet nicht optional. Ohne node_from_env_var beobachtet jeder Agent jeden Pod im Cluster. Übergeben Sie den Node-Namen über die Downward API (fieldPath: spec.nodeName), und lassen Sie den Filter auf diese Variable zeigen.
  • Eine connection-Regel muss vor jedem Batching stehen. Der Batch-Prozessor verwirft den Verbindungskontext, also gehört k8sattributes vor batch.
  • Pods im Host-Netzwerk lassen sich nicht über die IP zuordnen. Sie teilen alle die Adresse des Nodes. Ordnen Sie diese über ein Resource-Attribut zu.

Wenn Sie Agents und ein Gateway betreiben, ist das dokumentierte Muster passthrough: true auf den Agents. Diese setzen dann nur die Pod-IP auf die Daten, machen keine API-Aufrufe und überlassen das Nachschlagen dem Gateway. So gibt es einen Pod-Cache pro Gateway statt einen pro Node, um den Preis eines Gateways, das den ganzen Cluster beobachtet.

RBAC: Welches Attribut welche Rechte braucht

Hier widersprechen sich die Anleitungen, die Sie finden, teilweise weil sich der Prozessor verändert hat. Der Deployment-Name braucht standardmässig keinen Zugriff mehr auf ReplicaSets. Steht k8s.deployment.name in extract.metadata, liest der Prozessor den ReplicaSet-Namen aus der Owner-Referenz des Pods und schneidet den Pod-Template-Hash ab. Aus checkout-7d9f8b6c5 wird checkout, ganz ohne Abfrage des ReplicaSets.

Zugriff auf ReplicaSets braucht es wieder in zwei Fällen: wenn Sie k8s.deployment.uid anfordern oder wenn Sie Labels oder Annotationen from: deployment oder from: replicaset extrahieren. Dann startet der Prozessor einen ReplicaSet-Informer und löst den Namen über die API auf. Für CronJobs gilt dasselbe Muster: Der Name allein wird aus dem Job-Namen abgeleitet, und der Job-Informer startet nur für die UID oder für Labels von Jobs und CronJobs.

Eine Tabelle mit drei Spalten: was Sie extrahieren, auf welche Ressourcen der Collector get, list und watch braucht, und das Symptom, wenn das Recht fehlt. Pod- und Namespace-Metadaten einschliesslich des Standard-Deployment-Namens: pods und namespaces, sonst wird nichts angereichert. Deployment-UID oder Labels von Deployment oder ReplicaSet: replicasets in der Gruppe apps, sonst bleiben die Deployment-Felder leer. Node-UID oder Node-Labels: nodes, sonst bleiben die Node-Felder leer. CronJob-UID oder Labels von Job oder CronJob: jobs, dazu cronjobs für from cronjob, sonst bleiben die CronJob-Felder leer. Ein Hinweis sagt, dass der CronJob-Name allein kein Recht auf jobs braucht.

Abbildung 2. Die Rechte, die jede Art von Attribut braucht, gemäss dem RBAC-Abschnitt des Prozessors. Vergeben Sie, was Ihr extract-Block verlangt, nicht mehr.

Die Falle ist nicht das fehlende Recht an sich. Sie liegt darin, dass unvollständige RBAC-Rechte nichts kaputt machen, was Ihnen auffallen würde. Die Pipeline läuft weiter, Daten kommen an, und das Attribut fehlt einfach. Der einzige Hinweis ist ein «forbidden»-Fehler im Log des Collectors, das niemand liest, solange die Dashboards grün sind.

Zwei Gewohnheiten fangen das ab:

  1. Fragen Sie nach jeder Änderung an der Collector-Konfiguration oder an ihrem ServiceAccount nach Pod-Metriken, denen k8s.deployment.name, k8s.statefulset.name und k8s.daemonset.name gleichzeitig fehlen. Abgesehen von alleinstehenden Pods sollte diese Menge leer sein.
  2. Alarmieren Sie auf «forbidden» im Collector-Log, so wie Sie auf fehlgeschlagene Exporte alarmieren.

Zuordnen, behalten, löschen, als Label übernehmen

Sobald die Anreicherung funktioniert, ist das nächste Problem, dass es zu viel davon gibt. Ein Attribut kann drei verschiedene Aufgaben haben, und was für die eine perfekt ist, ist für die nächste oft falsch.

Drei Spalten von links nach rechts. Zuordnen über: k8s.pod.uid oder die Pod-IP, von pod_association genutzt; eindeutig pro Pod und bei jedem Neustart und Rollout neu. Auf der Resource behalten: k8s.namespace.name, k8s.deployment.name, k8s.node.name und ein app-Label, stabil über Neustarts. Im Backend als Label übernehmen: namespace, deployment und node, nur wonach Sie filtern und gruppieren; der Rest bleibt in target_info. Ein roter Zweig von der ersten Spalte führt zu vor dem Export löschen: k8s.pod.uid, process.pid, process.command_line und container.image.id.

Abbildung 3. Die Pod-UID ist der beste Schlüssel für die Zuordnung und einer der schlechtesten zum Behalten.

Ordnen Sie über das zu, was eindeutig ist. k8s.pod.uid bezeichnet genau einen Pod, deshalb nutzt die Zuordnung sie.

Exportieren Sie nicht, was sich bei jedem Rollout ändert. Dieselbe Eindeutigkeit macht die UID zu einem schlechten Wert zum Speichern: Jeder Neustart erzeugt einen neuen Wert, und jeder neue Wert ist eine neue Serie in einem Metrik-Backend. Löschen Sie sie einmal, nach der Anreicherung und vor dem letzten Export-Schritt, zusammen mit anderen schnell wechselnden Prozess-Attributen:

processors:
  resource/drop_noisy:
    attributes:
      - { key: k8s.pod.uid, action: delete }
      - { key: process.pid, action: delete }
      - { key: process.command_line, action: delete }
      - { key: container.image.id, action: delete }

service:
  pipelines:
    metrics:
      receivers: [ kubeletstats, otlp ]
      processors: [ memory_limiter, k8sattributes, resource/drop_noisy, batch ]
      exporters: [ otlp ]

Behalten Sie die stabilen Namen. Namespace, Deployment, Node und ein oder zwei Labels wie app überstehen Neustarts und beantworten die Fragen, die tatsächlich gestellt werden.

Machen Sie nur das zum Label, wonach Sie filtern. Das ist die Label-Falle. Ein Resource-Attribut ist nicht automatisch ein Metrik-Label. Mit den Exportern für Prometheus und Prometheus Remote Write landen Resource-Attribute standardmässig in einer separaten Metrik target_info, die Sie zur Abfragezeit verknüpfen. Sie können alle zu Labels auf jeder Serie machen, was funktioniert und die Kardinalität vervielfacht. Oder Sie übernehmen eine kurze Liste: Prometheus 3.x macht das für OTLP-Ingestion mit promote_resource_attributes, und der Prozessor transform kann einzelne Attribute in Datenpunkt-Attribute kopieren. Optionsnamen ändern sich zwischen Versionen, prüfen Sie sie also gegen Ihre. Namespace, Deployment und Node sind meistens die Liste.

Checkliste vor dem Rollout

  • pod_association hat eine Regel auf k8s.pod.uid für Kubelet-Daten und eine connection-Regel für OTLP von Pods.
  • Agents filtern mit node_from_env_var auf ihren eigenen Node, gespeist über die Downward API.
  • k8sattributes steht in jeder Pipeline mit einer connection-Regel vor batch.
  • RBAC vergibt genau, was der extract-Block braucht (Abbildung 2).
  • Eine Abfrage nach Pod-Metriken ohne Workload-Owner liefert nichts, und «forbidden» im Collector-Log löst einen Alarm aus.
  • Pod-UID und Prozess-Attribute werden vor dem Export gelöscht.
  • Nur die Attribute, nach denen Sie filtern, werden zu Metrik-Labels.

Das meiste davon ist Collector-Konfiguration, die auf jedem Node und in jedem Cluster identisch sein muss. Genau dort driftet eine Flotte auseinander: Ein Cluster bekommt die neuen RBAC-Rechte, ein anderer behält den extract-Block vom letzten Quartal. LinkMesh verwaltet die Konfiguration von OpenTelemetry Collectors über eine ganze Flotte, mit gestaffeltem Rollout und Rollback und Drift-Erkennung, damit eine Änderung wie die obige überall ankommt oder nirgends. Wo Agent und Gateway überhaupt sitzen, lesen Sie in Deployment-Muster für den Collector.

Quellen