环境初始化与部署:硬件选型建议、操作系统调优、Docker/K8s集群搭建、Spark与Trino部署
说实话,Lakehouse 环境初始化这一步,踩过的坑比写代码多得多。我见过太多团队,上来就急着搭集群,结果硬件选型不对、操作系统没调优,后面跑任务天天出幺蛾子。这一章,我把这些年积累的实战经验掰开揉碎讲给你听。
核心原则:环境初始化不是一次性工作,而是后续所有稳定性的基石。硬件、OS、容器编排、计算引擎,每一层都要对齐。
2.1 硬件选型建议
硬件选型,说白了就是「算清楚账」。我习惯从三个维度来评估:计算密度、IO 吞吐、成本约束。
| 组件 | 推荐配置 | 避坑点 |
|---|---|---|
| CPU | Intel Xeon Gold / AMD EPYC,48核以上 | 不要选低频高核,Spark 对单核性能敏感 |
| 内存 | 256GB ~ 512GB 起步 | Trino 对内存极度饥渴,128GB 以下别碰 |
| 存储 | NVMe SSD 4TB × 4(本地盘) | 避免 NFS 做 shuffle 目录,IO 会炸 |
| 网络 | 25GbE 或 100GbE RDMA | 10GbE 在数据 shuffle 时就是瓶颈 |
我记得有一次,客户买了 64 核的 ARM 服务器跑 Spark,结果 shuffle 性能比 x86 差了 30%。为什么?因为 Spark 的 shuffle 实现里有很多针对 x86 的指令集优化。所以,别盲目追新架构。
我的习惯:先跑一轮 TPC-DS 基准测试,用真实数据验证硬件选型。别信厂商的 PPT。
2.2 操作系统调优
操作系统调优,很多人觉得「装完系统就能用」。其实不然。我见过默认配置下,一个简单的 df -h 都能卡住,因为 IO 调度器没调。
以下是我每次必做的调优项:
- 内核参数调整:
vm.swappiness = 1,减少 swap 使用;net.core.somaxconn = 1024,提升网络连接队列深度。 - 文件系统:挂载参数加
noatime,nodiratime,减少元数据写入。 - IO 调度器:NVMe 盘用
none,SATA SSD 用mq-deadline。 - 透明大页:必须关闭。Spark 和 Trino 对内存碎片敏感,透明大页会导致内存分配失败。
# 我的标准调优脚本片段
echo 'vm.swappiness = 1' >> /etc/sysctl.conf
echo 'net.core.somaxconn = 1024' >> /etc/sysctl.conf
echo 'vm.max_map_count = 262144' >> /etc/sysctl.conf
sysctl -p
# 关闭透明大页
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
我曾经踩过的坑:有一次没关透明大页,Trino 集群跑了三天,内存分配越来越慢,最后 coordinator 直接 OOM。查了两天才定位到问题。从那以后,透明大页成了我的「必杀项」。
2.3 Docker / K8s 集群搭建
容器化部署 Lakehouse,我推荐 K8s。Docker 本身只是运行时,K8s 才是真正的编排大脑。
搭建步骤我总结为「三步走」:
- 节点规划:至少 3 台 master 节点(etcd 高可用),worker 节点按计算需求扩展。
- 容器运行时:containerd 优于 Docker Engine,因为 containerd 更轻量,且 K8s 已弃用 Docker 作为默认运行时。
- 网络插件:Calico 或 Cilium。我习惯用 Cilium,eBPF 技术让网络性能接近宿主机。
# 使用 kubeadm 初始化集群(我常用的版本)
kubeadm init --pod-network-cidr=10.244.0.0/16 \
--kubernetes-version=v1.28.0 \
--cri-socket=unix:///run/containerd/containerd.sock
# 安装 Cilium
cilium install --version 1.14.0
你想想看,K8s 集群搭好了,但 Pod 之间网络不通,那多尴尬。所以网络插件一定要在初始化后立刻验证。
我的经验:etcd 的磁盘一定要用高性能 SSD,延迟超过 10ms 就会影响集群稳定性。我见过有人把 etcd 放机械盘上,结果集群频繁 re-elect leader。
2.4 Spark 与 Trino 部署
Spark 和 Trino,一个偏批处理,一个偏交互查询。部署时要注意资源隔离。
2.4.1 Spark on K8s
Spark 3.x 已经原生支持 K8s,不需要 YARN 了。部署方式有两种:
- client mode:driver 在集群外运行,适合调试。
- cluster mode:driver 也在 Pod 内运行,适合生产。
我推荐生产环境用 cluster mode。为什么?因为 driver 挂了,K8s 会自动重启,不用人工介入。
# Spark 提交命令示例
spark-submit \
--master k8s://https://<api-server>:6443 \
--deploy-mode cluster \
--conf spark.kubernetes.container.image=apache/spark:3.5.0 \
--conf spark.kubernetes.namespace=spark-jobs \
--conf spark.executor.instances=10 \
--conf spark.executor.cores=4 \
--conf spark.executor.memory=16g \
local:///opt/spark/examples/jars/spark-examples.jar
注意:Spark 的 shuffle 目录一定要挂载本地 NVMe 盘,否则远程存储会成为瓶颈。我曾经用 NFS 做 shuffle,一个 1TB 的 join 跑了 6 小时没跑完,换成本地盘后 40 分钟搞定。
2.4.2 Trino 部署
Trino 的部署相对简单,但要注意 coordinator 和 worker 的配置差异。
| 角色 | 配置要点 | 资源建议 |
|---|---|---|
| Coordinator | query.max-memory 设小一点,避免 OOM | 16核 / 64GB 内存 |
| Worker | query.max-memory-per-node 按实际内存设 | 32核 / 256GB 内存 |
# Trino config.properties 示例(worker 节点)
coordinator=false
http-server.http.port=8080
query.max-memory=50GB
query.max-memory-per-node=20GB
discovery.uri=http://trino-coordinator:8080
部署完成后,记得用 curl 测试一下:
curl -X POST http://trino-coordinator:8080/v1/statement \
-H "Content-Type: application/json" \
-d '{"query": "SELECT 1"}'
如果返回 200,说明 Trino 跑起来了。嗯,这一步很简单,但很多人会忘记验证。
2.5 本章知识体系
下面这张图,是我对本章内容的总结。你可以把它当作环境初始化的「检查清单」。
这张图把本章内容串起来了。硬件选型是地基,OS 调优是钢筋,K8s 是框架,Spark 和 Trino 是装修。哪一层出问题,整个 Lakehouse 都会受影响。
最后说一句:环境初始化阶段,多花一天时间做测试,后面能省一周的排障时间。别急着上线,先把基础打牢。