Skip to content

3.8 部署结构:从 Docker Compose 到 K8s

学习目标

完成本节后,你将能够:

  1. 从部署视角复述 Langfuse 的运行组件:webworker、Postgres、ClickHouse、Redis/Valkey、S3/blob。
  2. 说清 Docker Compose 本地栈和 Kubernetes Helm 栈之间的对象映射。
  3. 理解为什么生产部署不能只把 compose 翻译成几个 Pod,还要补迁移、扩缩容、备份和状态组件治理。

3.8.1 先给结论:部署结构不是代码目录结构

从源码目录看,Langfuse 主要是:

text
web/ + worker/ + packages/shared/

从部署视角看,它是:

text
Langfuse Web + Langfuse Worker
+ Postgres
+ ClickHouse
+ Redis / Valkey
+ S3 / Blob Storage

也就是说,部署时真正要回答的不是“哪些目录跑起来”,而是:

问题对应部署组件
谁接浏览器、SDK、Public API 和 ingestion 请求?langfuse-web
谁消费异步任务、批量写入、跑后台作业?langfuse-worker
组织、项目、用户、dataset、prompt、配置放哪里?Postgres
trace、observation、score、events 的分析查询放哪里?ClickHouse
ingestion job、缓存、锁、限流、queue metrics 放哪里?Redis / Valkey
raw event、大文件、media、export 放哪里?S3 / Blob Storage,local 用 MinIO

本地 Docker Compose 把这些组件压到一台机器和一个 Docker network 里。Kubernetes 则把它们拆成 Deployment、Service、Ingress、Stateful dependency、Secret、HPA/PDB/KEDA 等对象。

3.8.2 Local Docker Compose 是什么结构

完整本地自托管入口是 docker-compose.yml。它起六类服务:

compose service镜像或角色部署职责
langfuse-weblangfuse/langfuse:3UI、tRPC、Public API、ingestion HTTP 入口。
langfuse-workerlangfuse/langfuse-worker:3BullMQ consumer、batch writer、eval/export/delete 等后台任务。
postgresPostgreSQL事务型产品状态。
clickhouseClickHouse Servertracing/eval/score/events 分析状态。
redisRedis队列、缓存、锁、限流。
minioS3-compatible object storageraw event、media、batch export。

本地 compose 拓扑可以这样读:

读这张图时注意两点:

  1. webworker 都需要连接 Postgres、ClickHouse、Redis、S3,但职责不同。web 是同步入口,worker 是异步执行器。
  2. ingestion 高吞吐路径不是 web -> ClickHouse 直写,而是 web -> S3 raw event + Redis job -> worker -> ClickHouse

开发测试时还有一个更轻的 docker-compose.dev.yml。它通常只起 Postgres、ClickHouse、Redis、MinIO,webworker 由源码命令启动。这更适合本地改代码,因为你可以用 pnpm run dev:webpnpm run dev:worker 看日志和热更新。

3.8.3 Compose 里的连接契约

Compose 的核心不是 depends_on,而是环境变量连接契约。

契约示例变量谁使用
PostgresDATABASE_URLwebworker
ClickHouse HTTP 查询CLICKHOUSE_URLwebworker
ClickHouse migrationCLICKHOUSE_MIGRATION_URL主要由 web entrypoint 跑迁移时使用
RedisREDIS_HOSTREDIS_PORTREDIS_AUTHwebworker
S3 raw eventLANGFUSE_S3_EVENT_UPLOAD_*web 写,worker
S3 mediaLANGFUSE_S3_MEDIA_UPLOAD_*web 生成上传/下载 URL,产品功能读取
S3 exportLANGFUSE_S3_BATCH_EXPORT_*worker 写 export,web 提供下载入口

这些变量在 compose 和 K8s 之间保持同一套语义。K8s Helm chart 做的事情,本质上是把这些连接参数从 docker-compose.yml 的 env block,搬到 values.yaml、Secret 和模板 helper 里。

3.8.4 K8s Helm 是什么结构

langfuse-k8s chart 的应用层是:

text
Deployment/langfuse-web
Service/langfuse-web
Ingress/langfuse-web 可选

Deployment/langfuse-worker

Secret / ServiceAccount / HPA / KEDA ScaledObject / PDB / VPA 可选

数据层默认由 chart dependency 拉起:

text
PostgreSQL subchart
ClickHouse subchart
Valkey subchart,作为 Redis 兼容层
MinIO subchart,作为 S3 兼容层

生产环境也可以把这些 dependency 关掉,改接外部托管服务:

yaml
postgresql:
  deploy: false
  host: "<managed-postgres-host>"

clickhouse:
  deploy: false
  host: "<managed-clickhouse-host>"

redis:
  deploy: false
  host: "<managed-redis-host>"

s3:
  deploy: false
  bucket: "<bucket-name>"

从 K8s 资源视角看:

这张图和 compose 图看起来很像,但语义不一样:

维度Docker ComposeKubernetes
应用进程一个 service 通常一个容器实例Deployment 管理多个 Pod 副本
服务发现Docker network service nameKubernetes Service DNS
对外入口host port,例如 3000:3000Ingress / LoadBalancer / port-forward
配置env block / .envvalues.yaml、Secret、ConfigMap、envFrom
健康检查compose healthchecklivenessProbe / readinessProbe
扩缩容手动改 replicas 或另起容器HPA / KEDA / VPA
状态持久化local volumePVC、托管数据库、对象存储
发布docker compose pull && upHelm upgrade / rollout

3.8.5 Helm values 如何映射到应用环境变量

Chart 的 values.yaml 并不是应用代码的新配置系统。它只是把部署层参数翻译成应用容器认识的环境变量。

对应关系是:

Helm values生成的应用配置说明
postgresql.hostpostgresql.auth.*DATABASE_HOSTDATABASE_USERNAMEDATABASE_PASSWORDDATABASE_NAMEentrypoint 会拼出 DATABASE_URL
postgresql.directUrlDIRECT_URL用于 migration,可绕开 pooler。
postgresql.migration.autoMigrateLANGFUSE_AUTO_POSTGRES_MIGRATION_DISABLED控制是否自动跑 Prisma migration。
clickhouse.hostclickhouse.httpPortCLICKHOUSE_URL应用读写 ClickHouse HTTP endpoint。
clickhouse.migration.urlCLICKHOUSE_MIGRATION_URLClickHouse migration 使用 native/TCP endpoint。
clickhouse.clusterEnabledCLICKHOUSE_CLUSTER_ENABLED控制 migration 是否 ON CLUSTER
redis.hostredis.auth.*REDIS_HOST / REDIS_CONNECTION_STRING 相关 env连接 BullMQ 和缓存。
s3.eventUpload.*LANGFUSE_S3_EVENT_UPLOAD_*ingestion raw event bucket。
s3.mediaUpload.*LANGFUSE_S3_MEDIA_UPLOAD_*media upload bucket。
s3.batchExport.*LANGFUSE_S3_BATCH_EXPORT_*batch export bucket。

所以读 Helm chart 时,不要把 values.yaml 当成业务抽象。真正的业务抽象仍在 webworkerpackages/sharedvalues.yaml 是部署层把外部基础设施接入这些抽象。

3.8.6 迁移:Compose 方便,K8s 要小心

web 镜像的 entrypoint 会做两类迁移:

  1. Postgres:执行 cleanup SQL,再跑 Prisma migration。
  2. ClickHouse:执行 packages/shared/clickhouse/scripts/up.sh

这在 compose 里很方便,因为通常只有一个 langfuse-web 实例。

在 K8s 里,如果 langfuse-web 有多个副本,滚动发布时就可能出现多个新 Pod 同时启动、同时尝试迁移。迁移脚本一般会尽量幂等,但生产上仍然应该把“应用启动”和“schema 变更”分开治理。

推荐心智模型:

对初学者来说,这里最重要的不是记住某个 Helm hook 写法,而是理解边界:

阶段应该做什么不应该混在一起的原因
migration改 Postgres / ClickHouse schema需要单次、有序、可回滚或可人工介入。
web rollout替换同步入口实例应该快速、可并行、可健康检查。
worker rollout替换后台 consumer还要考虑旧 job payload、新旧版本兼容和重试。

如果要做更稳的生产部署,可以关闭自动迁移,把迁移放到单独 Job 或发布流水线里,再启动多副本 web/worker。

3.8.7 从能跑到生产,还差什么

K8s 不是“换个容器编排器”就自动生产可用。差距主要在下面几类治理能力。

1. 副本和高可用

最低限度:

组件POC更接近生产
web1 replica至少 2 replicas,配 readinessProbe 和 PDB。
worker1 replica按 CPU 或 queue length 扩容。
Postgreschart standalone 或外部单实例托管 Postgres 或有备份/故障恢复的 HA。
ClickHouse单节点也能跑单 shard 多 replica,或 ClickHouse Cloud/BYOC。
Redis/Valkeystandalone高负载时用 cluster/sentinel/托管实例。
S3/blobMinIO托管对象存储或有备份和生命周期策略的对象存储。

2. 扩缩容指标要按链路拆

webworker 的瓶颈不一样:

部署对象主要压力常见扩容信号
web UI/APIHTTP 并发、ingestion payload upload、API auth/cacheCPU、内存、请求延迟、5xx、S3 socket backlog。
workerBullMQ job 消费、S3 下载、ClickHouse batch insert、eval/exportCPU、queue length、job wait time、failed/stalled count。
ClickHouseinsert、merge、filter/query、dashboard/API readsquery latency、merge backlog、disk、CPU、memory。
Redisqueue operations、cache lookup、locks、sharded queueCPU、memory、queue length、command latency。

高 ingestion 负载时,可以把 langfuse-web 拆成两个 Deployment:

这不是源码层的新服务,而是部署层把同一个 langfuse-web 镜像按流量类型复制一份,用 Ingress path routing 隔离 UI 和 ingestion 压力。

3. 状态组件要有备份和生命周期策略

Compose 的 local volume 只适合本地实验。生产要分别回答:

状态组件必须回答的问题
Postgres备份频率、恢复演练、连接池、migration 用户权限、UTC 时区。
ClickHousereplica 数、PVC 大小、磁盘扩容、TTL、系统日志表、读写隔离。
Redis/Valkeynoeviction、CPU、memory、queue shard、持久化需求。
S3/blobbucket 权限、生命周期、media 不应随意过期、跨 VPC endpoint、presigned URL 可达性。

Langfuse 的“事实数据”不在一个地方。丢 Postgres 会丢产品控制面,丢 ClickHouse 会丢 tracing 分析事实,丢 S3 raw event 会影响重放、media 和 export,丢 Redis 会影响队列和短期缓存。

4. Secret 和配置要脱离 values 明文

教程里可以为了演示把密码写进 values.yaml,但生产不要这样做。

更稳的方式:

  • SALTENCRYPTION_KEYNEXTAUTH_SECRET 使用 Kubernetes Secret 或 External Secrets。
  • 数据库、Redis、S3 密码使用 secret reference。
  • 应用公共配置和敏感配置分开管理。
  • 不把真实 secret commit 到 GitOps 仓库。

5. 网络边界要明确

Compose 里靠 127.0.0.1 限制端口暴露。K8s 里要换成:

边界K8s 对应做法
用户只访问 webIngress 只暴露 langfuse-web
数据库不暴露公网ClusterIP / private endpoint / security group。
Pod 间访问受控NetworkPolicy。
S3 endpoint 可达VPC endpoint、DNS、presigned URL 外部 endpoint。
管理控制面限制 Helm/Kubectl 权限和 namespace 范围。

3.8.8 最小 K8s 学习路径

如果你只是为了理解架构,不要一上来陷入所有 Helm values。建议按这个顺序读:

  1. charts/langfuse/Chart.yaml:看 dependency,确认哪些状态组件被 chart 拉起来。
  2. charts/langfuse/values.yaml:看 langfuse.weblangfuse.workerpostgresqlclickhouserediss3 六块。
  3. templates/web/deployment.yaml:看 web container env、probe、service port。
  4. templates/worker/deployment.yaml:看 worker container env、probe、port。
  5. templates/_helpers.tpl:看 values 如何变成 DATABASE_*CLICKHOUSE_*REDIS_*LANGFUSE_S3_*
  6. 回到 docker-compose.yml 对照同名环境变量,确认 K8s 不是新系统,而是同一套运行契约的生产编排版本。

源码锚点:

目标文件
本地完整部署docker-compose.yml
本地依赖栈docker-compose.dev.yml
web 镜像和迁移入口web/Dockerfileweb/entrypoint.sh
worker 镜像入口worker/Dockerfileworker/entrypoint.sh
Helm chart 依赖../langfuse-k8s/charts/langfuse/Chart.yaml
Helm values../langfuse-k8s/charts/langfuse/values.yaml
web Deployment../langfuse-k8s/charts/langfuse/templates/web/deployment.yaml
worker Deployment../langfuse-k8s/charts/langfuse/templates/worker/deployment.yaml
env helper../langfuse-k8s/charts/langfuse/templates/_helpers.tpl
官方部署文档../langfuse-docs/content/self-hosting/deployment/kubernetes-helm.mdx
官方 scaling 文档../langfuse-docs/content/self-hosting/configuration/scaling.mdx

3.8.9 读完后应该形成的判断

看到一个 Langfuse 部署问题时,先把它放进这张表:

问题优先检查
UI 慢、API 慢web CPU/内存、ClickHouse 查询、Postgres 连接、是否缺时间/project filter。
ingestion 延迟大Redis queue length、worker CPU、S3 写入、ClickHouse insert/merge。
worker 重试多S3/ClickHouse/Postgres 错误、job payload 兼容、lock/stalled 设置。
发布后启动失败migration、Secret、数据库连接、ClickHouse migration URL。
数据查不到ClickHouse 写入、worker 是否消费、project/time filter、events_core/events_full 投影。
media/export 失败S3 bucket、endpoint、presigned external endpoint、权限。
升级有风险schema migration、旧 queue job、ClickHouse migration、feature flag。

部署架构的本质是把第 3.5 节的数据设施、第 4.4 节 ingestion 链路、第 4.5 节 worker 队列和第 5 篇契约放到真实运行环境里。K8s 只是承载形式;真正需要学会的是:哪些组件是无状态、哪些组件是事实来源、哪些路径可以重试、哪些变更必须单独迁移。

下一篇

第 4 篇 · 运行链路