Blog
Einrichten eines lokalen Langfuse-Servers mit Kubernetes zur Verfolgung von Agentic-Systemen

Selbst-Hosting von Langfuse mit Kubernetes
Ein lokaler GPU-Cluster ist ideal für die Entwicklung sensibler LLM-Anwendungen. Wenn Sie an sensiblen Anwendungen arbeiten, möchten Sie nicht, dass die Protokollierung und Überwachung über das Internet an einen von einer dritten Partei verwalteten Server gesendet wird.
Langfuse ist ein großartiges Open-Source-Framework für die Verwaltung von Traces, Evals, Prompt-Management und Metriken zur Fehlersuche und Verbesserung Ihrer LLM-Anwendung. Dieser Blogbeitrag beschreibt, wie wir unseren selbst gehosteten Langfuse-Server für einen lokalen GPU-Cluster eingerichtet haben.
Unser Stapel
Bevor wir einsteigen, möchten wir Ihnen die Tools vorstellen, die wir für die Einrichtung eines lokalen Langfuse-Servers verwenden:
- Helm: Diagramme für die Kubernetes-Konfiguration
- Anpassen: So ändern Sie die Diagramme
- Geheimnis-Generator: Zum Erstellen und Verwalten von Geheimnissen
- Traefik: Eingehenden Verkehr umleiten
- Pihole: DNS-Server für Filterung auf DNS-Ebene
Konfiguration
Langfuse stellt in seinem Repo ein Helm-Diagramm und ein minimales Installationsbeispiel zur Verfügung. In diesem Abschnitt erfahren Sie, wie Sie diese anpassen, um mit dem oben erwähnten Stack zu arbeiten.
Anpassen
Wir beginnen mit dem Verweis auf die Steuerkarte in unserem kustomization.yml
:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: langfuse
helmCharts:
- name: langfuse
releaseName: langfuse
version: "1.2.5"
repo: https://langfuse.github.io/langfuse-k8s
namespace: langfuse
Namespace
Um einen Namensraum zu erstellen, wird unter dem Ordner resources/
eine Datei namens namespace.yml
erstellt:
apiVersion: v1
kind: Namespace
metadata:
name: langfuse
Diese Datei wird referenziert, indem Sie folgendes an die kustomization.yml
anhängen:
...
resources:
- resources/namespace.yml
Geheimnisse
Als nächstes legen wir obligatorische Geheimnisse für langfuse
und die Dienstleistungen langfuse
hängt ab von (postgresql
, redis
, clickhouse
und minio
) im kustomization.yml
:
...
helmCharts:
- name: langfuse
...
valuesInline:
langfuse:
...
salt:
secretKeyRef:
name: langfuse-salt
key: salt
nextauth:
secret:
secretKeyRef:
name: langfuse-nextauth
key: encryption-key
postgresql:
auth:
existingSecret: langfuse-postgresql
secretKeys:
userPasswordKey: postgres-password
adminPasswordKey: postgres-password
redis:
auth:
existingSecret: langfuse-redis
existingSecretPasswordKey: password
clickhouse:
auth:
existingSecret: langfuse-clickhouse
existingSecretKey: password
s3:
deploy: true
bucket: langfuse
endpoint: http://langfuse-s3:9000
forcePathStyle: true
auth:
existingSecret: langfuse-minio
rootUserSecretKey: user
rootPasswordSecretKey: password
...
Wir verwenden kubernetes-secret-generator, um Geheimnisse in resources/secrets.yml
zu verwalten:
apiVersion: v1
kind: Secret
metadata:
name: langfuse-postgresql
annotations:
secret-generator.v1.mittwald.de/autogenerate: postgres-password
secret-generator.v1.mittwald.de/encoding: hex
secret-generator.v1.mittwald.de/length: "32"
---
...
Dies wird für alle Geheimnisse wiederholt, auf die in kustomization.yml
verwiesen wird, wobei ein Benutzer für minio
hinzugefügt wird:
...
---
apiVersion: v1
kind: Secret
metadata:
name: langfuse-minio
annotations:
secret-generator.v1.mittwald.de/autogenerate: password
secret-generator.v1.mittwald.de/encoding: base64
secret-generator.v1.mittwald.de/length: "32"
data:
user: bWluaW8=
Die Datei secrets wird unter resources in der kustomization.yml
referenziert, ähnlich wie die Namespaces:
resources:
...
- resources/secrets.yml
Ingress konfigurieren - Traefik
Damit der lokale Datenverkehr unseren selbst gehosteten Langfuse-Server erreichen kann, wird die Ingress-Konfiguration an langfuse
in der kustomization.yml
übergeben:
langfuse:
ingress:
enabled: true
classname: traefik
hosts:
- host: langfuse.my-server.internal
paths:
- path: /
pathType: Prefix
...
Traefik konfiguriert den Ingress so, dass er von langfuse.my-server.internal
zu langfuse.langfuse.svc.cluster.local
führt.
Vollständiges Beispiel
Zusammengefasst ergibt dies die folgenden Dateien:
langfuse/
├── kustomization.yml
└── resources
├── namespace.yml
└── secrets.yml
kustomization.yml
:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: langfuse
helmCharts:
- name: langfuse
releaseName: langfuse
version: "1.2.5"
repo: https://langfuse.github.io/langfuse-k8s
namespace: langfuse
valuesInline:
langfuse:
ingress:
enabled: true
classname: traefik
hosts:
- host: langfuse.my-server.internal
paths:
- path: /
pathType: Prefix
salt:
secretKeyRef:
key: salt
name: langfuse-salt
nextauth:
secret:
secretKeyRef:
key: key
name: langfuse-nextauth
postgresql:
port: 5432
auth:
existingSecret: langfuse-postgresql
secretKeys:
userPasswordKey: postgresql-password
adminPasswordKey: postgresql-password
redis:
auth:
existingSecret: langfuse-redis
existingSecretPasswordKey: redis-password
clickhouse:
auth:
existingSecret: langfuse-clickhouse
existingSecretKey: clickhouse-password
s3:
deploy: true
bucket: langfuse
endpoint: http://langfuse-s3:9000
forcePathStyle: true
auth:
existingSecret: langfuse-minio
rootUserSecretKey: user
rootPasswordSecretKey: minio-password
resources:
- resources/namespace.yml
- resources/secrets.yml
resources/namespace.yml
:
apiVersion: v1
kind: Namespace
metadata:
name: langfuse
resources/secrets.yml
:
apiVersion: v1
kind: Secret
metadata:
name: langfuse-salt
annotations:
secret-generator.v1.mittwald.de/autogenerate: salt
secret-generator.v1.mittwald.de/encoding: base64
secret-generator.v1.mittwald.de/length: "32"
---
apiVersion: v1
kind: Secret
metadata:
name: langfuse-nextauth
annotations:
secret-generator.v1.mittwald.de/autogenerate: key
secret-generator.v1.mittwald.de/encoding: base64
secret-generator.v1.mittwald.de/length: "32"
---
apiVersion: v1
kind: Secret
metadata:
name: langfuse-postgresql
annotations:
secret-generator.v1.mittwald.de/autogenerate: postgresql-password
secret-generator.v1.mittwald.de/encoding: hex
secret-generator.v1.mittwald.de/length: "32"
---
apiVersion: v1
kind: Secret
metadata:
name: langfuse-redis
annotations:
secret-generator.v1.mittwald.de/autogenerate: redis-password
secret-generator.v1.mittwald.de/encoding: base64
secret-generator.v1.mittwald.de/length: "32"
---
apiVersion: v1
kind: Secret
metadata:
name: langfuse-clickhouse
annotations:
secret-generator.v1.mittwald.de/autogenerate: clickhouse-password
secret-generator.v1.mittwald.de/encoding: base64
secret-generator.v1.mittwald.de/length: "32"
---
apiVersion: v1
kind: Secret
metadata:
name: langfuse-minio
annotations:
secret-generator.v1.mittwald.de/autogenerate: minio-password
secret-generator.v1.mittwald.de/encoding: base64
secret-generator.v1.mittwald.de/length: "32"
data:
user: bWluaW8=
Zugang und Test
Dank Traefik kann auf Langfuse über das interne Netzwerk des Kubernetes-Clusters zugegriffen werden: http://langfuse.my-server.internal
App
Wenn Sie sich mit Langfuse verbinden, z.B. wenn Sie Traces von Ihrer Agentic-App protokollieren möchten, setzen Sie die env-Variable auf dieselbe URL:
LANGFUSE_HOST="http://langfuse.my-server.internal"
UI
In der Benutzeroberfläche von Langfuse können Sie eine Organisation, ein Projekt und API-Schlüssel erstellen. Um von Ihrem lokalen Gerät aus auf die Benutzeroberfläche zuzugreifen, nutzen wir einen vorhandenen DNS-Server
DNS-Eintrag
Der DNS-Eintrag wird zu kustomize.yml
von Pihole hinzugefügt:
helmCharts:
- name: pihole
releaseName: pihole
version: "v2.24.0"
repo: https://mojo2600.github.io/pihole-kubernetes/
valuesInline:
dnsmasq:
customDnsEntries:
...
- address=/langfuse.my-server.internal/100.74.16.73
...
Jetzt kann auf Langfuse von einem lokalen Gerät aus zugegriffen werden: http://langfuse.my-server.internal
. Nachdem Sie eine Organisation und ein Projekt erstellt haben, können Sie die API-Schlüssel (LANGFUSE_SECRET_KEY
, LANGFUSE_PUBLIC_KEY
) in der Benutzeroberfläche erstellen.
Test über Python
Um die Bereitstellung zu testen, starten Sie einen temporären Container:
kubectl run langfuse-test --image=python:3.12-slim-bookworm -n langfuse --rm -it -- /bin/bash
Innerhalb dieses Containers installieren Sie das Python-Paket langfuse
:
pip install langfuse
Exportieren Sie die Umgebungsvariablen:
export LANGFUSE_HOST=http://langfuse.my-server.internal
export LANGFUSE_SECRET_KEY=sk-lf-...
export LANGFUSE_PUBLIC_KEY=pk-lf-...
Führen Sie den folgenden Python-Code aus:
from langfuse import observe
@observe()
def fn():
pass
@observe()
def main():
fn()
main()
⚠️ Beachten Sie, dass es ab v3 einfach from langfuse
heißt, statt from langfuse.decorators
in v2.
Wenn alles erfolgreich verlaufen ist, sollten die Spuren jetzt in der Benutzeroberfläche erscheinen:
Erweiterungen
Um diese Einrichtung zu professionalisieren, kann der Langfuse-Server um folgende Funktionen erweitert werden:
- Verwenden Sie einen bestehenden Keyvault oder Sealed-Secrets, um Geheimnisse aufzubewahren.
- Einrichten der Verschlüsselung.
- Optimieren Sie die Einrichtung für Zuverlässigkeit und Betriebszeit.
- SSO einrichten.
Referenzen
Verfasst von

Jetze Schuurmans
Machine Learning Engineer
Jetze is a well-rounded Machine Learning Engineer, who is as comfortable solving Data Science use cases as he is productionizing them in the cloud. His expertise includes: AI4Science, MLOps, and GenAI. As a researcher, he has published papers on: Computer Vision and Natural Language Processing and Machine Learning in general.
Unsere Ideen
Weitere Blogs
Contact