菜单

Administrator
发布于 2026-04-22 / 1 阅读
0

Langfuse Kubernetes Helm 部署与离线化实践

概述

本文整理了一套 Langfuse 基于 Kubernetes 与 Helm 的部署方法,覆盖以下主题:

  • Helm Chart 自定义配置方式
  • 中国区镜像拉取加速思路
  • 内置 PostgreSQL、Redis、ClickHouse、MinIO 的配置逻辑
  • hostPath + PV/PVC 持久化方案
  • NodePort 暴露方式
  • 基于节点主机名的调度约束
  • 常见启动报错排查方法
  • 无海外网络环境下的离线部署思路

内容基于单机或小规模测试环境展开。若用于生产环境,建议将数据库、对象存储与分析存储迁移为独立托管服务或可靠的分布式存储方案。

Helm Chart 的自定义方式

Langfuse 官方提供 Helm Chart 进行 Kubernetes 部署,主要自定义入口为 values.yaml

常见配置分类如下:

  • langfuse.*:应用自身配置,例如 nextauthsaltencryptionKeyingressresources
  • postgresql.*:内置 PostgreSQL 或外部 PostgreSQL 连接配置
  • redis.*:内置 Valkey/Redis 或外部 Redis 配置
  • clickhouse.*:内置或外部 ClickHouse 配置
  • s3.*:内置 MinIO 或外部对象存储配置
  • global.*:全局镜像、存储类等通用配置

基础安装命令如下:

helm repo add langfuse https://langfuse.github.io/langfuse-k8s
helm repo update
helm install langfuse langfuse/langfuse -n langfuse -f values.yaml

升级命令如下:

helm upgrade langfuse langfuse/langfuse -n langfuse -f values.yaml

若使用本地离线 Chart 目录,可改为:

helm upgrade --install langfuse . -n langfuse -f values.yaml

镜像源地址与中国区拉取加速

Langfuse Helm Chart 涉及多组镜像:

  • langfuse/langfuse
  • langfuse/langfuse-worker
  • bitnamilegacy/postgresql
  • bitnamilegacy/valkey
  • bitnamilegacy/clickhouse
  • bitnamilegacy/minio
  • bitnamilegacy/zookeeper(部分场景会出现)

推荐方案为:先将镜像同步到内网 Harbor、企业 ACR、TCR 或其他私有镜像仓库,再在 values.yaml 中覆盖镜像仓库地址。

示例:

langfuse:
  web:
    image:
      repository: harbor.example.local/library/langfuse
      tag: "3.169.0"
  worker:
    image:
      repository: harbor.example.local/library/langfuse-worker
      tag: "3.169.0"

postgresql:
  image:
    repository: harbor.example.local/library/postgresql

redis:
  image:
    repository: harbor.example.local/library/valkey

clickhouse:
  image:
    repository: harbor.example.local/library/clickhouse
  zookeeper:
    image:
      repository: harbor.example.local/library/zookeeper

s3:
  image:
    repository: harbor.example.local/library/minio

如私有仓库需要认证,可创建 imagePullSecrets 并在 values.yaml 中引用。

values.yaml 中账号密码的作用

values.yaml 中的账号密码主要分为两类:

内置依赖组件的启动参数

当以下配置为 true 时,Helm Chart 会一并部署对应组件:

postgresql:
  deploy: true
redis:
  deploy: true
clickhouse:
  deploy: true
s3:
  deploy: true

此时配置的用户名、密码会被用于:

  • 创建内置 PostgreSQL 用户与数据库
  • 初始化内置 Valkey/Redis 密码
  • 初始化内置 ClickHouse 用户密码
  • 初始化内置 MinIO root 用户与密码
  • 向 Langfuse Web 与 Worker 注入连接信息

外部依赖组件的连接参数

deploy: false 时,相关账号密码不再用于创建组件,而是用于连接外部服务。

例如:

  • postgresql.auth.password:外部 PostgreSQL 密码
  • redis.auth.password:外部 Redis 密码
  • s3.accessKeyId / s3.secretAccessKey:外部对象存储访问密钥

Langfuse 自身的重要密钥

以下配置不是数据库密码,而是 Langfuse 应用自身的关键安全参数:

  • langfuse.salt
  • langfuse.encryptionKey
  • langfuse.nextauth.secret

其中 encryptionKey 要求最严格,必须是 64 位十六进制字符串,生成方式如下:

openssl rand -hex 32

若格式不正确,webworker 会直接启动失败,并出现类似以下报错:

ENCRYPTION_KEY must be 256 bits, 64 string characters in hex format

无 PVC 场景下的持久化方案

若集群中没有可直接使用的动态存储类,且部署场景为单机或测试环境,可采用 hostPath + PV/PVC 方案。

这一方案的核心逻辑不是在 Pod 里直接写 hostPath,而是:

  1. 创建 PersistentVolume
  2. 使用 hostPath 作为 PV 后端目录
  3. 创建 PersistentVolumeClaim
  4. values.yaml 中通过 existingClaim 引用 PVC

示例:PostgreSQL 的 PV/PVC

apiVersion: v1
kind: PersistentVolume
metadata:
  name: langfuse-postgres-pv
spec:
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual-hostpath
  hostPath:
    path: /data/langfuse/postgresql
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: langfuse-postgres-pvc
  namespace: langfuse
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: manual-hostpath
  resources:
    requests:
      storage: 20Gi
  volumeName: langfuse-postgres-pv

在 values.yaml 中引用 PVC

postgresql:
  primary:
    persistence:
      enabled: true
      existingClaim: langfuse-postgres-pvc

Redis、ClickHouse、MinIO 的写法与此类似。

hostPath 的局限性

hostPath 的本质是节点本地磁盘目录,因此存在明显限制:

  • Pod 在同一节点重建,数据仍可保留
  • Pod 漂移到其他节点,本地数据不会随 Pod 迁移
  • 多节点环境下,若未限制调度,极易出现“卷已绑定,但数据不在当前节点”的问题

因此,hostPath 仅适用于:

  • 单机 Kubernetes
  • 测试环境
  • 明确固定状态组件到指定节点的场景

若需支持跨节点迁移,应使用网络存储、云盘 CSI、NFS、Longhorn、Ceph 等方案。

NodePort 部署方式

若不使用 Ingress,可将 Langfuse Web 服务改为 NodePort 暴露。

示例:

langfuse:
  nextauth:
    url: http://192.168.1.150:30080
    secret:
      value: "replace-with-a-random-secret"
  salt:
    value: "replace-with-a-random-salt"
  encryptionKey:
    value: "replace-with-64-hex-string"
  web:
    service:
      type: NodePort
      port: 3000
      externalPort: 3000
      nodePort: 30080

部署后可通过以下地址访问:

http://<节点IP>:30080

基于主机名的节点调度约束

由于 hostPath 与节点本地磁盘绑定,状态组件必须通过调度约束固定在同一台节点上。

推荐使用 nodeAffinitymatchExpressions + In 方式,按 kubernetes.io/hostname 约束。

示例:

postgresql:
  primary:
    affinity:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
            - matchExpressions:
                - key: kubernetes.io/hostname
                  operator: In
                  values:
                    - node1

Redis、ClickHouse、MinIO 均建议增加类似约束。

特点如下:

  • 目标节点可用时,Pod 固定落在指定节点
  • 目标节点不可用时,Pod 会保持 Pending
  • 与其错误漂移到其他节点挂错数据盘,Pending 更安全

首次访问时的登录行为

Langfuse 页面中出现的登录界面并不是来自数据库或对象存储密码。

values.yaml 中配置的以下密码:

  • PostgreSQL 密码
  • Redis 密码
  • ClickHouse 密码
  • MinIO root 密码

仅用于后端组件初始化或连接,不用于 Web 登录。

Langfuse 默认不会预置管理员账号,首次使用通常需要通过页面注册创建账号。

若页面不允许注册,应检查:

langfuse:
  features:
    signUpDisabled: false

常见报错与排查方法

1. Redis/Valkey 启动失败

典型日志:

Can't open or create append-only dir appendonlydir: Permission denied

这类问题通常与 hostPath 目录权限有关。Bitnami Valkey 镜像默认以非 root 用户运行,宿主机目录若无写权限,容器会直接退出。

修复方法示例:

mkdir -p /data/langfuse/redis
chown -R 1001:1001 /data/langfuse/redis
chmod -R 755 /data/langfuse/redis
kubectl delete pod -n langfuse langfuse-redis-primary-0

测试环境中,为快速验证,也可临时使用:

chmod -R 777 /data/langfuse

2. Worker/Web 因 ENCRYPTION_KEY 格式错误启动失败

典型日志:

ENCRYPTION_KEY must be 256 bits, 64 string characters in hex format

修复方式:重新生成合法密钥,并更新 values.yaml

openssl rand -hex 32

3. Zookeeper Pod 长期 Pending

典型事件:

pod has unbound immediate PersistentVolumeClaims

这通常说明:

  • Zookeeper 仍被 Chart 拉起
  • 对应 PVC 未准备完成
  • zookeeper.enabled 未真正关闭

在单机、单副本 ClickHouse 场景下,通常无需额外运行 Zookeeper,应重点检查 values.yaml 是否真正关闭相关组件。

一个可工作的核心 values 结构

以下结构适用于单机测试环境的基本思路:

global:
  security:
    allowInsecureImages: true

langfuse:
  nextauth:
    url: http://192.168.1.150:30080
    secret:
      value: "replace-with-base64-secret"
  salt:
    value: "replace-with-base64-salt"
  encryptionKey:
    value: "replace-with-64-hex-string"
  web:
    service:
      type: NodePort
      port: 3000
      externalPort: 3000
      nodePort: 30080

postgresql:
  deploy: true
  auth:
    username: langfuse
    password: "replace-with-postgres-password"
    database: langfuse
  primary:
    affinity:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
            - matchExpressions:
                - key: kubernetes.io/hostname
                  operator: In
                  values:
                    - node1
    persistence:
      enabled: true
      existingClaim: langfuse-postgres-pvc

redis:
  deploy: true
  auth:
    username: langfuse
    password: "replace-with-redis-password"
    database: 0
  primary:
    affinity:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
            - matchExpressions:
                - key: kubernetes.io/hostname
                  operator: In
                  values:
                    - node1
    extraFlags:
      - --maxmemory-policy noeviction
    persistence:
      enabled: true
      existingClaim: langfuse-redis-pvc

clickhouse:
  deploy: true
  auth:
    username: langfuse
    password: "replace-with-clickhouse-password"
  shards: 1
  replicaCount: 1
  clusterEnabled: false
  keeper:
    enabled: false
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                  - node1
  persistence:
    enabled: true
    existingClaim: langfuse-clickhouse-pvc

s3:
  deploy: true
  auth:
    rootUser: minio
    rootPassword: "replace-with-minio-password"
  defaultBuckets: langfuse
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                  - node1
  persistence:
    enabled: true
    existingClaim: langfuse-minio-pvc

部署命令顺序

完整部署流程建议如下:

kubectl create namespace langfuse
kubectl apply -f storage.yaml
kubectl get pv
kubectl get pvc -n langfuse
helm upgrade --install langfuse . -n langfuse -f values.yaml
kubectl get pods -n langfuse -o wide

若需查看渲染结果,可先执行:

helm template langfuse . -n langfuse -f values.yaml

无海外网络环境下的离线部署方案

无海外网络权限时,Helm 与镜像拉取需分开处理。

1. 离线准备 Helm Chart

在可访问外网的中转环境执行:

helm repo add langfuse https://langfuse.github.io/langfuse-k8s
helm repo update
helm pull langfuse/langfuse --untar
helm dependency build ./langfuse
tar czf langfuse-chart-offline.tar.gz langfuse

langfuse-chart-offline.tar.gz 传入内网环境后解压。

若解压目录中已存在以下内容,通常说明 Chart 依赖已经准备齐全:

  • Chart.yaml
  • Chart.lock
  • charts/
  • templates/

此时不必再次执行错误的命令:

helm dependency build langfuse

若当前目录已经是 Chart 根目录,正确写法应为:

helm dependency build .

或者直接安装:

helm upgrade --install langfuse . -n langfuse -f values.yaml

2. 离线准备镜像

即使本地 Chart 已经完整,若 Kubernetes 节点无法访问海外镜像仓库,Pod 仍然无法启动。因此必须提前准备镜像。

推荐方式如下:

  1. 在外网中转环境拉取所需镜像
  2. 同步到内网 Harbor 或其他私有镜像仓库
  3. values.yaml 中将镜像仓库地址全部替换为内网地址

需要重点处理的镜像通常包括:

  • langfuse/langfuse
  • langfuse/langfuse-worker
  • bitnamilegacy/postgresql
  • bitnamilegacy/valkey
  • bitnamilegacy/clickhouse
  • bitnamilegacy/minio
  • bitnamilegacy/zookeeper(若仍有该组件)

若集群已成功安装一次,可通过以下命令反查实际镜像列表:

kubectl get pods -n langfuse -o jsonpath="{..image}" | tr ' ' '\n' | sort -u

3. 离线机器上如何部署本地 Helm Chart

离线机器即使没有配置 Helm 仓库,也可以完成部署。前提是以下内容已经准备完成:

  • Helm 二进制可正常使用
  • 本地 Chart 目录或已打包的 .tgz 文件可用
  • values.yaml 已准备完成
  • Kubernetes 节点能够获取所需镜像

方式一:直接使用本地 Chart 目录部署

若离线环境中已有完整 Chart 目录,例如:

langfuse/
  Chart.yaml
  Chart.lock
  charts/
  templates/
  values.yaml

则可直接执行:

helm upgrade --install langfuse ./langfuse -n langfuse -f ./langfuse/values.yaml

这种方式不会访问任何远程 Helm 仓库。

方式二:使用打包后的 tgz 文件部署

在有网环境中完成打包:

helm dependency build ./langfuse
helm package ./langfuse

打包后会得到类似文件:

langfuse-1.5.27.tgz

.tgz 文件与 values.yaml 一并传入离线环境后,可直接安装:

helm upgrade --install langfuse ./langfuse-1.5.27.tgz -n langfuse -f values.yaml

同样不会依赖 Helm 仓库。

Chart 依赖的正确前提

离线安装能否成功,关键在于 Chart 依赖是否已被提前打入本地包中。需要满足以下任一条件:

  1. Chart 目录中已存在 charts/ 子目录,并且其中包含依赖包
  2. 在有网环境中已经执行过 helm dependency build ./langfuse

若当前目录已经是 Chart 根目录,则不应执行错误命令:

helm dependency build langfuse

正确写法应为:

helm dependency build .

或者:

helm dependency build ./langfuse

推荐的离线交付内容

更适合内网传递的交付物通常包括:

  • langfuse-<version>.tgz
  • values.yaml
  • storage.yaml

离线机器上的部署命令示例如下:

kubectl create namespace langfuse
kubectl apply -f storage.yaml
helm upgrade --install langfuse ./langfuse-1.5.27.tgz -n langfuse -f values.yaml

如何确认安装过程不依赖外网

可先在离线机器执行模板渲染:

helm template langfuse ./langfuse-1.5.27.tgz -n langfuse -f values.yaml

若模板渲染成功,说明 Chart 本体及其依赖已经准备完成。

需要注意的是,Chart 本地化仅解决 Helm 仓库问题,并不自动解决镜像拉取问题。若节点无法访问外部镜像仓库,仍需提前完成以下任一方案:

  • 将镜像同步到内网 Harbor 或其他私有镜像仓库
  • 将镜像直接导入各节点运行时

总结

Langfuse 的 Helm 部署并不复杂,难点主要集中在三处:

  1. 状态组件的持久化设计
  2. hostPath 场景下的节点固定与权限处理
  3. 无海外网络环境下的镜像与 Chart 离线准备

在单机测试环境中,hostPath + PV/PVC + hostname affinity + NodePort 是一套可行方案。该方案部署简单、排错路径清晰,适合快速验证 Langfuse 的功能链路。

在生产环境中,更合理的做法是:

  • 采用独立的 PostgreSQL、Redis、对象存储与分析存储
  • 使用稳定的网络存储或云盘 CSI
  • 使用企业内网镜像仓库统一管理镜像分发
  • 通过 Secret 管理敏感配置,而非明文写入 values.yaml

至此,一套从在线部署到离线迁移的 Langfuse Kubernetes Helm 实践路径已基本完整。