4、环境准备:Docker Compose搭建Kafka + MinIO + Spark + Iceberg环境
说实话,做数据管道最头疼的事,就是搭环境。
我记得刚接触Iceberg那会儿,光装依赖就折腾了两天。后来我学乖了——能用Docker Compose解决的,绝不动手装。这一节,我就带你用一套编排文件,把Kafka、MinIO、Spark和Iceberg全部跑起来。
本章目标:你将在本地启动一个完整的实时湖仓实验环境。所有组件容器化,一键部署,用完即毁。
4.1 整体架构
先看看我们要搭什么。说白了就是四层:
- 存储层:MinIO(兼容S3的对象存储),Iceberg的表文件就放这里
- 消息层:Kafka + Zookeeper,负责流数据的摄入
- 计算层:Spark,负责读写Iceberg表
- 元数据层:Iceberg的Hive Metastore(用PostgreSQL做后端),管理表结构
我画了张图,你看一眼就明白了:
我的建议:如果你机器内存小于8GB,可以把Spark的executor内存调小一点。我曾在4GB的笔记本上跑过,把spark.executor.memory改成1g也能用,就是慢点。
4.2 编写docker-compose.yml
好,直接上代码。这是我反复打磨过的一个版本,每个组件的版本都经过验证:
version: '3.8'
services:
# ---------- 存储层 ----------
minio:
image: minio/minio:RELEASE.2023-09-04T19-57-37Z
container_name: ice-minio
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: password
command: server /data --console-address ":9001"
volumes:
- minio-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 10s
timeout: 5s
retries: 5
# ---------- 元数据层 ----------
postgres:
image: postgres:13
container_name: ice-postgres
ports:
- "5432:5432"
environment:
POSTGRES_DB: metastore
POSTGRES_USER: hive
POSTGRES_PASSWORD: hive
volumes:
- pg-data:/var/lib/postgresql/data
hive-metastore:
image: apache/hive:4.0.0
container_name: ice-hive-metastore
ports:
- "9083:9083"
environment:
SERVICE_NAME: metastore
DB_DRIVER: postgres
DB_HOST: postgres
DB_PORT: 5432
DB_DATABASE: metastore
DB_USER: hive
DB_PASSWORD: hive
depends_on:
postgres:
condition: service_started
# ---------- 消息层 ----------
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
container_name: ice-zk
ports:
- "2181:2181"
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
kafka:
image: confluentinc/cp-kafka:7.5.0
container_name: ice-kafka
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
depends_on:
- zookeeper
# ---------- 计算层 ----------
spark:
image: bitnami/spark:3.5.0
container_name: ice-spark
ports:
- "8080:8080"
- "7077:7077"
environment:
SPARK_MODE: master
SPARK_MASTER_URL: spark://spark:7077
SPARK_RPC_AUTHENTICATION_ENABLED: "no"
SPARK_RPC_ENCRYPTION_ENABLED: "no"
SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED: "no"
volumes:
- ./iceberg-spark-runtime-3.5_2.12-1.4.3.jar:/opt/bitnami/spark/jars/iceberg-spark-runtime.jar
- ./aws-java-sdk-bundle-1.12.262.jar:/opt/bitnami/spark/jars/aws-bundle.jar
- ./hadoop-aws-3.3.4.jar:/opt/bitnami/spark/jars/hadoop-aws.jar
depends_on:
minio:
condition: service_healthy
hive-metastore:
condition: service_started
volumes:
minio-data:
pg-data:
注意:Spark镜像里默认没有Iceberg和AWS的依赖包。你需要手动下载三个jar包放到当前目录下。我曾经忘了放aws-bundle.jar,结果Spark连MinIO一直报403,排查了半天才发现是S3客户端没加载进去。
4.3 下载依赖JAR包
这三个jar包缺一不可。我列个表,你直接去Maven仓库搜:
| JAR包名称 | 版本 | 作用 |
|---|---|---|
| iceberg-spark-runtime-3.5_2.12-1.4.3.jar | 1.4.3 | Iceberg与Spark的集成运行时 |
| aws-java-sdk-bundle-1.12.262.jar | 1.12.262 | MinIO兼容S3的Java SDK |
| hadoop-aws-3.3.4.jar | 3.3.4 | Hadoop与AWS S3的连接桥 |
下载命令示例(以Linux/Mac为例):
wget https://repo1.maven.org/maven2/org/apache/iceberg/iceberg-spark-runtime-3.5_2.12/1.4.3/iceberg-spark-runtime-3.5_2.12-1.4.3.jar
wget https://repo1.maven.org/maven2/com/amazonaws/aws-java-sdk-bundle/1.12.262/aws-java-sdk-bundle-1.12.262.jar
wget https://repo1.maven.org/maven2/org/apache/hadoop/hadoop-aws/3.3.4/hadoop-aws-3.3.4.jar
避坑指南:版本一定要对齐。我曾经试过用hadoop-aws 3.4.0配Spark 3.5.0,结果启动时直接ClassNotFoundException。老老实实按我上面列的组合来,都是实测过的。
4.4 启动与验证
所有文件就位后,执行一条命令:
docker-compose up -d
等个一两分钟,检查所有容器是否正常运行:
docker ps --format "table {{.Names}}\t{{.Status}}"
你应该看到类似这样的输出:
NAMES STATUS
ice-minio Up 2 minutes (healthy)
ice-postgres Up 2 minutes
ice-hive-metastore Up 2 minutes
ice-zk Up 2 minutes
ice-kafka Up 2 minutes
ice-spark Up 2 minutes
然后验证一下关键组件:
- MinIO控制台:浏览器打开
http://localhost:9001,用 admin/password 登录 - Kafka可用性:创建一个测试topic
docker exec ice-kafka kafka-topics --create --topic test --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1 - Spark UI:浏览器打开
http://localhost:8080
4.5 配置Spark连接Iceberg
环境跑起来之后,还需要告诉Spark怎么找到Iceberg和MinIO。我习惯在启动Spark-shell时直接传配置:
docker exec -it ice-spark spark-shell \
--conf spark.sql.catalog.demo=org.apache.iceberg.spark.SparkCatalog \
--conf spark.sql.catalog.demo.type=hadoop \
--conf spark.sql.catalog.demo.warehouse=s3a://iceberg-warehouse/ \
--conf spark.sql.catalog.demo.hadoop.fs.s3a.endpoint=http://minio:9000 \
--conf spark.sql.catalog.demo.hadoop.fs.s3a.access.key=admin \
--conf spark.sql.catalog.demo.hadoop.fs.s3a.secret.key=password \
--conf spark.sql.catalog.demo.hadoop.fs.s3a.path.style.access=true
关键点:s3a://iceberg-warehouse/ 这个桶需要提前在MinIO里创建。登录MinIO控制台,新建一个叫 iceberg-warehouse 的bucket。不然Spark写数据时会报桶不存在的错误。
进到Spark-shell后,跑一条简单的SQL验证:
spark.sql("CREATE DATABASE IF NOT EXISTS demo.db")
spark.sql("USE demo.db")
spark.sql("CREATE TABLE IF NOT EXISTS test_table (id INT, name STRING) USING iceberg")
spark.sql("INSERT INTO test_table VALUES (1, 'hello'), (2, 'world')")
spark.sql("SELECT * FROM test_table").show()
如果能看到两行数据,恭喜你——环境搭好了。
我曾经踩过的坑:第一次跑INSERT时卡了很久,最后超时了。后来发现是MinIO的桶没创建,Spark一直在重试。所以记得先建桶,再跑SQL。
4.6 清理环境
实验结束后,想重置环境很简单:
docker-compose down -v
-v 参数会删除volume,下次启动时就是全新的环境。我个人习惯每次实验前都跑一遍这个命令,避免残留数据干扰。