Kubernetes-Logs an UptimeEye senden

Der schnellste Weg auf Kubernetes ist ein Log-Agent als DaemonSet: Er liest stdout/stderr jedes Containers direkt auf dem Node, reichert jede Zeile mit Pod-Metadaten an und schickt sie gebündelt an UptimeEye. An deinen Anwendungen änderst du nichts.

Das Beispiel nutzt Vector: Es braucht etwa 50 MB Speicher pro Node und puffert auf die Platte, solange das Netz weg ist. Mit Fluent Bit oder dem OpenTelemetry Collector funktioniert es genauso – die Alternativen findest du am Ende.

Endpunkt: https://logs.uptimeeye.com/v1/ingest/elasticsearch/_bulk

1.Ingest-Key erstellen und als Secret ablegen

Erstell unter API-Keys einen Key vom Typ „Log-Ingest“ und leg ihn in dem Namespace ab, in dem der Agent läuft.

shell
kubectl create namespace logging
kubectl -n logging create secret generic uptimeeye-logs \
  --from-literal=key=ue_ingest_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

2.Vector als DaemonSet installieren

Das Helm-Chart bringt eine kubernetes_logs-Source mit, die Container-Logs schon lesen und mit Pod-Metadaten verknüpfen kann. Der Remap-Schritt wählt die Felder aus, nach denen du filtern willst; der Sink spricht das Bulk-Protokoll von Elasticsearch, das UptimeEye annimmt.

values.yaml
role: Agent
# Mount the Secret as files; Vector reads the key via its secret backend
# (it does not expand ${ENV} placeholders in the config).
extraVolumes:
  - name: uptimeeye-logs
    secret: { secretName: uptimeeye-logs }
extraVolumeMounts:
  - name: uptimeeye-logs
    mountPath: /etc/vector/secrets
    readOnly: true
customConfig:
  data_dir: /vector-data-dir
  secret:
    k8s:
      type: directory
      path: /etc/vector/secrets
  sources:
    k8s_logs:
      type: kubernetes_logs
  transforms:
    enrich:
      type: remap
      inputs: [k8s_logs]
      source: |
        .service   = .kubernetes.container_name
        .namespace = .kubernetes.pod_namespace
        .pod       = .kubernetes.pod_name
        .host      = .kubernetes.pod_node_name
        .env       = "prod"
        # Applications that log JSON: lift their fields to the top level.
        parsed, err = parse_json(string!(.message))
        if err == null { . = merge(., parsed) }
        del(.kubernetes); del(.file); del(.source_type)
  sinks:
    uptimeeye:
      type: elasticsearch
      inputs: [enrich]
      endpoints: ["https://logs.uptimeeye.com/v1/ingest/elasticsearch/"]
      api_version: v8
      compression: gzip
      healthcheck: { enabled: false }
      request:
        headers:
          Authorization: "Bearer SECRET[k8s.key]"
      query:
        _msg_field: message
        _time_field: timestamp
        _stream_fields: namespace,service,env
      buffer: { type: disk, max_size: 268435488 }

3.Deployen

shell
helm repo add vector https://helm.vector.dev
helm upgrade --install vector vector/vector -n logging -f values.yaml
kubectl -n logging logs ds/vector --tail=20   # look for "Healthcheck disabled" and no 4xx

4.Suchen

Nach wenigen Sekunden erscheint jeder Pod unter seinem Containernamen. Probier namespace:=payments level:error oder öffne das Panel „Felder“ und klick einen Wert an.

Felder, die du bekommst

Diese Felder erscheinen im Panel „Felder“ und funktionieren in jedem Filter:

  • service (Containername)
  • namespace
  • pod
  • host (Node)
  • env
  • jedes JSON-Feld, das deine Anwendung loggt

Tipps

  • Beschränk die Stream-Felder auf eine Handvoll Felder mit wenigen Werten (namespace, service, env). pod ändert sich bei jedem Rollout – lass es ein normales Feld.
  • Namespaces mit viel Rauschen schließt du mit extra_namespace_label_selector oder einem filter-Transform aus (.namespace != "kube-system").
  • Alternative mit Fluent Bit: Nimm den kubernetes-Filter und den http-Output aus der Fluent-Bit-Anleitung mit URI /v1/ingest/jsonline?_msg_field=log&_stream_fields=kubernetes.namespace_name,kubernetes.container_name.
  • Alternative mit OpenTelemetry: der filelog-Receiver des Collectors plus der k8sattributes-Processor, exportiert mit otlphttp an /v1/ingest/otlp/v1/logs.

FAQ

Muss ich meine Anwendungen ändern?

Nein. Der Agent liest stdout/stderr. Loggen deine Apps schon JSON, macht der parse_json-Schritt aus ihren Keys durchsuchbare Felder; Klartextzeilen bleiben die Nachricht.

Wie viele Ressourcen braucht der Agent?

Vector als DaemonSet braucht meist 30–80 MB Speicher und wenige Prozent eines Cores pro Node. Der Plattenpuffer (256 MB im Beispiel) hält die Logs vor, solange UptimeEye nicht erreichbar ist.

Wo bekomme ich den Ingest-Key?

In der App unter API-Keys → Neuer API-Key → Typ „Log-Ingest“. Der Key beginnt mit ue_ingest_ und wird nur einmal angezeigt. Management-Keys (ue_live_) lehnt der Ingest-Endpoint ab.

Wie sehe ich, ob Logs ankommen?

Öffne Logs in der App, wähl den Zeitraum „15 Min.“ und such nach service:=<your service>. Neue Zeilen sind nach etwa einer Sekunde durchsuchbar; Live-Tail zeigt sie mit etwa 6 s Verzögerung.