1 Home
opunix edited this page 2026-08-16 01:33:29 +02:00

otel-collector — the standard Extract stage

The otel-collector is the front door for every observability signal in the lab. It is an upstream OpenTelemetry Collector (contrib) — config only, no lab-authored code — that receives signals, enriches the resource, and lands immutable OTLP-JSON artifacts in S3 (s3://semantix/land/<kind>/…). It never emits RDF: landing artifacts is the seam. This is seam #1 of the one ingestion contract — see the platform wiki "Runbook — onboard a signal source".

signals ──▶ otel-collector ──▶ s3://semantix/land/<kind>/…  ──▶ OTLP→RDF transform ──▶ loader ──▶ qlever
 (this repo: Extract)          (immutable OTLP-JSON)            (triplify, next)

What it does today

Receiver k8sobjects (Kubernetes events, mode: watch)
Processors resourcedetectionresource (stamps simon.cluster=lab, simon.signal=k8s-event) → batch
Exporter awss3 — marshaler otlp_json, bucket semantix, prefix land/k8s-events, partitioned year=/month=/day=/hour=
Extensions health_check (:13133)
Self-telemetry Prometheus :8888 (otelcol_* series)

Each flushed batch becomes one object: land/k8s-events/year=YYYY/month=MM/day=DD/hour=HH/logs_<n>.json.

Watch mode emits only NEW events. A quiet cluster lands nothing — that is correct, not a fault. To smoke-test, generate an event (kubectl -n pipelines run evgen --image=busybox --restart=Never --command -- sh -c 'echo hi') and watch an object appear.

S3 credentials (Hetzner)

Uses the lab S3 creds only — AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY come from the mover-s3 Secret (Hetzner bucket semantix, region fsn1, endpoint https://fsn1.your-objectstorage.com, path-style). The awss3exporter was verified against Hetzner — OTLP-JSON lands with no checksum problem. Do not wire business creds here.

Ops (fleet standard — same as the sibling services)

  • Health: http://otel-collector.pipelines.svc:13133/ → Gatus (pipeline group) + readiness probe.
  • Metrics: http://otel-collector.pipelines.svc:8888/metrics → Prometheus job otel-collector.
  • Dashboard: Grafana "OTel Collector — signal landing (Extract)" (uid otel-collector).
  • Catalog: registered in service-catalog → Backstage (type: service, system: observability).

Key metrics: otelcol_receiver_accepted_log_records (in), otelcol_exporter_sent_log_records (landed to S3), otelcol_receiver_refused_log_records + _failed_log_records (errors), otelcol_processor_batch_batch_send_size_bucket (upload batch size).

Deploy

kubectl -n pipelines create configmap otel-collector-config \
  --from-file=config.yaml=config/config.yaml --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f deploy/otel-collector.yaml

The image is the pinned upstream contrib digest (Kyverno require-signed-sbom only gates forgejo.…/* refs, so upstream admits as-is — same precedent as grafana/prometheus).

Onboarding a new signal (the whole point)

New source = a receiver block + an enrichment rule in config/config.yaml, not a new service. Add a filelog/syslog/otlp/hostmetrics receiver, stamp simon.signal=<kind>, point a new pipeline at an awss3 exporter with s3_prefix: land/<kind>. Then the shared OTLP→RDF transform + loader pick it up. Full procedure: platform wiki "Runbook — onboard a signal source".

Anchor rule (non-negotiable)

Every landed signal must resolve its resource to a stable CMDB IRI downstream (NetBox device, k8s object, or Backstage service). Enrich enough here (k8sattributes, resourcedetection, hostname/IP) that the transform can map it. No anchor, no join.