Add Langfuse as a self-hosted eval and trace viewer

This commit is contained in:
2026-08-21 10:35:25 +07:00
parent 4490a1abf0
commit 53582b6030
12 changed files with 1129 additions and 280 deletions
+48
View File
@@ -0,0 +1,48 @@
# Langfuse (LLM observability / eval viewer) — reads OTel-style traces and
# Ragas eval scores, not part of the RAG chat path itself.
#
# Multi-source Application: source[0] is the upstream `langfuse/langfuse-k8s`
# chart (not vendored into this repo); source[1] is this repo, providing only
# the override values file via the `$values` ref. Same disaster-recovery
# property as the other two Applications here: applying this file recreates
# the Application's structure, no follow-up step needed since this release
# carries no inline secrets (see infra/helm/langfuse/values-production.yaml
# for why: the app's own SALT/ENCRYPTION_KEY/NEXTAUTH_SECRET are chart-
# auto-generated on first install, and the Postgres password is read from
# the medical-chatbot-data release's existing Secret by name, never
# duplicated here).
#
# Deliberately targets the `medical-chatbot-data` namespace, not its own —
# see the values file's header comment for why (Secrets don't cross
# namespaces, and this needs the Postgres one that already lives there).
# This Application does NOT own the PersistentVolumeClaims that release
# depends on (postgres/qdrant) — it only adds new resources (its own
# ClickHouse/Redis/web/worker) into the same namespace. `prune: true` below
# only prunes resources Langfuse's own chart previously created, standard
# ArgoCD behavior scoped to this Application's own tracked resources.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: langfuse
namespace: argocd
spec:
project: default
sources:
- repoURL: https://langfuse.github.io/langfuse-k8s
chart: langfuse
targetRevision: 2.0.0
helm:
valueFiles:
- $values/infra/helm/langfuse/values-production.yaml
- repoURL: https://github.com/BaoVu2k4/vsf-duocthu.git
targetRevision: master
ref: values
destination:
server: https://kubernetes.default.svc
namespace: medical-chatbot-data
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
+118
View File
@@ -0,0 +1,118 @@
# Production values for the `langfuse` ArgoCD Application (upstream
# `langfuse/langfuse-k8s` chart, not this repo's own medical-chatbot chart).
#
# Runs in the `medical-chatbot-data` namespace deliberately, not its own —
# that is the only namespace holding the K8s Secret with the shared Postgres
# password, and Secrets don't cross namespaces. Langfuse never reads that
# password directly: everything below points at the secret BY NAME/KEY
# (`medical-chatbot-data-medical-chatbot-runtime` / `postgres-password`), the
# same way infra/helm/medical-chatbot's own templates do it.
#
# Deliberately bundled rather than external: Redis and ClickHouse (personal-
# scale traffic, no case for a managed service here). Deliberately external:
# Postgres (reuse what already exists rather than a second instance) and S3
# (a real bucket, `duocthu-langfuse-blobs`, reached via the k3s node's IAM
# instance role — no static access keys anywhere in this file or the cluster,
# same pattern as ai-service's Bedrock access).
#
# SALT / ENCRYPTION_KEY / NEXTAUTH_SECRET are deliberately absent: the chart
# auto-generates and persists them in a release-managed Secret on first
# install when left unset. No manual secret entry needed for this file.
langfuse:
resources:
requests: { cpu: 100m, memory: 256Mi }
limits: { cpu: "1", memory: 512Mi }
ingress:
enabled: true
className: traefik
hosts:
- host: langfuse.realvuxbaro.me
paths:
- path: /
pathType: ImplementationSpecific
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.entrypoints: websecure
tls:
enabled: true
secretName: langfuse-tls
nextauth:
url: https://langfuse.realvuxbaro.me
postgresql:
deploy: false
host: medical-chatbot-data-medical-chatbot-postgres.medical-chatbot-data.svc.cluster.local
port: 5432
auth:
username: duoc_thu
existingSecret: medical-chatbot-data-medical-chatbot-runtime
secretKeys:
userPasswordKey: postgres-password
database: langfuse
redis:
deploy: true
resources:
requests: { cpu: 50m, memory: 64Mi }
limits: { cpu: 250m, memory: 256Mi }
clickhouse:
deploy: true
cluster:
enabled: false
storage:
size: 20Gi
s3:
deploy: false
storageProvider: s3
bucket: duocthu-langfuse-blobs
region: us-east-1
endpoint: https://s3.us-east-1.amazonaws.com
forcePathStyle: false
# Both left empty on purpose: falls back to the AWS SDK default credential
# chain, which picks up the node's IAM instance role automatically.
accessKeyId:
value: ""
secretAccessKey:
value: ""
# One-off Job that creates the `langfuse` database on the existing Postgres
# instance before Langfuse's own migration runs. Idempotent (checks first).
# Never touches infra/helm/medical-chatbot's own templates or its release.
extraManifests:
- apiVersion: batch/v1
kind: Job
metadata:
name: langfuse-db-init
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
backoffLimit: 3
template:
spec:
restartPolicy: Never
containers:
- name: create-db
image: postgres:16-alpine
env:
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: medical-chatbot-data-medical-chatbot-runtime
key: postgres-password
command: ["sh", "-c"]
args:
- |
set -e
HOST=medical-chatbot-data-medical-chatbot-postgres.medical-chatbot-data.svc.cluster.local
EXISTS=$(psql "postgresql://duoc_thu@$HOST:5432/duoc_thu" -tAc \
"SELECT 1 FROM pg_database WHERE datname = 'langfuse'")
if [ "$EXISTS" != "1" ]; then
psql "postgresql://duoc_thu@$HOST:5432/duoc_thu" -c "CREATE DATABASE langfuse OWNER duoc_thu"
fi