一台机器跑起整套湖仓:k3s 部署 Kyuubi/Spark/Iceberg/Doris 怎么做对

0 阅读6分钟

一套完整的 lakehouse——SQL 网关、湖表格式、元数据、对象存储、流引擎、OLAP、检索——通常被默认是「一个集群的事」。但 PoC、私有化交付前的验证、小团队起步,手里往往只有一台机器。可私有化部署的数据平台「我的数据空间」(datastudiohappy.cn)的底座就是这么跑的:单机 k3s,十几个常驻组件,高峰三四十个 pod,长期稳定。这篇讲清楚怎么做对:选型、部署顺序、资源分配、数据持久化、日志与安全。

一、全景与选型:为什么是 k3s

单机 k3s 湖仓全景:四层组件 + 资源三层策略

组件角色
MinIOS3 对象存储,Iceberg warehouse
MySQLHMS 元数据 + 平台元数据
Hive Metastore表元数据,四个引擎共享同一份
Kyuubi + SparkSQL 网关 + 批计算,引擎按需拉起
Flink流引擎,每作业一个独立集群
TrinoIceberg + 跨源联邦查询
DorisOLAP,高并发点查 / 聚合
Iceberg湖表格式,以 jar 进各引擎,非独立进程
ZooKeeper / Kafka / ES协调 / 消息 / 检索

底座选 k3s 而不是 docker-compose,三个理由:

  • 引擎的现代部署形态是 K8s 原生的:Spark on K8s 是 driver 拉 executor,Flink native application 是 JM 自建 TM,都需要真正的 K8s API;compose 只能退回 standalone 模式,和生产形态是两个物种。
  • 隔离与排队机制 K8s 白送:namespace + ResourceQuota 天然提供作业配额与排队,不必自己造。
  • k3s 是单二进制的完整 K8s:直跑宿主、没有虚拟化夹层,PVC 就是宿主目录,排障直观;写出的清单将来搬多节点集群几乎不用改。原则一句话:部署形态可以单机,清单和抽象必须是生产形态。

二、部署顺序:依赖即顺序

地基(MySQL、ZooKeeper、MinIO)→ HMS → Kyuubi → Trino / Doris / ES / Kafka(相互独立,可并行)→ 各工作空间 namespace 与配额 → 平台层。两个要点:

  • HMS 是咽喉。四个引擎全指向它,它值得最优先的稳定性投入:元数据放 MySQL 而非内嵌库;并逐一核对「谁在什么时刻访问对象存储」——不止引擎写数据,HMS 服务端建库时也要直接写对象存储,默认镜像不带这部分依赖,要提前补齐(设计依据:漏一处就是建库即报错)。
  • 验收按链路,不看 pod 全绿。用 SQL 网关建一张 Iceberg 表、插数、查回,一条命令打通网关→计算→元数据→存储;再用 Trino 查同一张表,验证元数据确实共享;起一个 Kafka 入湖流作业,确认 checkpoint 持续成功;最后重启宿主机,把三步重跑一遍——能活过重启的环境才算搭完。

搭好之后,组件健康、资源水位、作业状态收进一个页面盯:

平台运维监控:组件健康与资源水位一页看全

三、资源三层策略

以 64G 内存为例,全家桶挤得下,靠的是三层:

  1. 常驻压到最低。MySQL / ZK / MinIO / HMS / ES / Kafka / Trino / Doris 逐个设 JVM 堆和容器 limits,常驻层整体控制在 12–16G。
  2. 弹性按需拉起。Spark 引擎按用户复用、空闲自动回收;Flink 每个作业一个独立集群,作业停即销毁;History Server 也按需拉起。核心判断:永远不会同时用到所有引擎,「空闲即归零」远比「常驻等任务」划算。
  3. ResourceQuota 兜底。每个工作空间独立 namespace 配配额,作业申请超额就 Pending 排队,而不是把机器打爆(设计依据:单机没有配额约束时,一个失控作业就是一次宕机)。

在「我的数据空间」里,这层配额直接可视化:每个空间的查询引擎用了多少、还剩多少,页面上一眼可见(产品介绍)。

查询引擎配额:空间资源用量可视化

磁盘同样要提前规划:装 k3s 之前就把容器运行时与 PVC 的数据目录挂到大容量数据盘,事后迁移要停集群搬数据;引擎日志这类只增不减的数据,入对象存储的设桶生命周期、入 ES 的配索引过期,别「先存着再说」。

四、有状态数据:持久化四原则

  • 有状态组件一律挂 PVC,且逐个数据目录核对。像 Doris 这种元数据与数据分离的组件,漏挂一个目录就丢一类数据(设计依据:容器可写层在重建后归零,重启即蒸发)。
  • 身份不绑 pod IP。以 IP 为身份的有状态组件,pod 重建换 IP 后会拒绝服务;用固定网络身份(StatefulSet、FQDN 或固定宿主网络)一劳永逸。
  • 重启演练当验收。上线前真实 reboot 一次、逐组件验数据,比任何 review 都可靠。
  • 不可再生资产单列保护。本地构建的镜像、引擎元数据、HMS 库,没了就是没了:自建镜像推私有 registry 或保证可离线重建,批量清理类操作在这类机器上一律禁止。另一条经验:服务端 pod 重建后,顺手滚动重启它的长连接客户端——很多大数据组件的连接池对「对端换了 IP」没有自愈能力(设计依据:不重启就是一片过期连接报错)。

五、日志:边产边发,而不是事后去抓

Spark executor、Flink TM 退出后,pod 会被上层引擎立刻删除,「失败了再去抓日志」是在和删除动作竞速(设计依据:实测命中率约一半)。正确做法是节点级日志采集器边产边发:采集器持有日志文件句柄,pod 被删后仍能读完缓冲。落点做双路——全量压缩进对象存储,设 7 天生命周期,便宜;告警级以上进 ES,设 2 天过期,能搜。单机也值得做:排障时「有没有日志」是 10 分钟和一下午的区别。

六、安全基线:第一天就是生产姿势

  • 中间件统一 ClusterIP,只在集群内可达;本机调试用 port-forward 到回环地址,不对局域网暴露任何数据库端口。
  • 口令从第一天就是强随机值,脚本生成、存 Secret,演示环境也不例外。
  • 内部回调接口带共享密钥,且 fail-closed:检测到密钥还是占位默认值就拒绝启动——宁可起不来,不带默认口令上线。
  • 入口统一走 Ingress:TLS 终止、剥离伪造的身份头、内部路径对外直接拒绝。

七、三句话总结

  • 单机验证 ≠ 单机架构:清单与抽象保持生产形态,规模只是参数;
  • 资源策略三层缺一不可:常驻压最低、弹性按需拉起、配额兜底;
  • 把不可再生的东西列出来重点保护:自建镜像、引擎元数据、HMS 库。

这套湖仓是「我的数据空间」的底座——一套可私有化部署的数据平台,支持 OEM 合作。产品介绍:datastudiohappy.cn/,交流合作 QQ:1559851993。