4. 环境搭建:Docker Compose 搭建 Flink + Kafka + Hudi 开发环境
说实话,做流批一体最头疼的往往不是写代码,而是搭环境。我见过太多团队在环境上折腾一周,最后发现是版本不兼容。今天我们就用 Docker Compose 一把梭,把 Flink、Kafka、Hudi 这三件套一次性搞定。
4.1 为什么选 Docker Compose?
你可能会问:为啥不用直接装?嗯,我个人的习惯是——开发环境能容器化就容器化。原因很简单:
- 隔离性:每个组件各跑各的容器,互不干扰
- 一致性:团队所有人都用同一套配置,不会出现「我本地能跑」的尴尬
- 快速重置:搞坏了?
docker-compose down && up一键重生
我在项目中遇到过好几次,因为本地装了不同版本的 Kafka,导致生产者和消费者序列化对不上。自从用了 Docker Compose,这种问题再也没出现过。
4.2 整体架构图
先看一张图,了解我们今天要搭什么:
4.3 目录结构
我个人喜欢把配置文件和代码分开。下面是推荐的项目结构:
flink-kafka-hudi-env/
├── docker-compose.yml # 主编排文件
├── flink/
│ ├── Dockerfile # Flink 自定义镜像
│ └── conf/
│ ├── flink-conf.yaml # Flink 配置
│ └── sql-client.yaml # SQL 客户端配置
├── kafka/
│ └── server.properties # Kafka 配置(可选)
├── hudi/
│ └── hudi-default.conf # Hudi 配置
└── data/ # 持久化数据目录
├── kafka/
├── zookeeper/
└── minio/
4.4 docker-compose.yml 详解
这是核心文件。我直接贴出完整配置,然后逐段解释:
version: '3.8'
services:
# ========== ZooKeeper ==========
zookeeper:
image: confluentinc/cp-zookeeper:7.4.0
container_name: zookeeper
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ports:
- "2181:2181"
volumes:
- ./data/zookeeper:/var/lib/zookeeper/data
networks:
- stream-net
# ========== Kafka ==========
kafka:
image: confluentinc/cp-kafka:7.4.0
container_name: kafka
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
volumes:
- ./data/kafka:/var/lib/kafka/data
networks:
- stream-net
# ========== Flink JobManager ==========
flink-jobmanager:
image: flink:1.17.1-scala_2.12
container_name: flink-jobmanager
ports:
- "8081:8081"
command: jobmanager
environment:
- |
FLINK_PROPERTIES=
jobmanager.rpc.address: flink-jobmanager
jobmanager.memory.process.size: 2048m
taskmanager.memory.process.size: 2048m
volumes:
- ./flink/conf:/opt/flink/conf
- ./hudi/hudi-flink-bundle.jar:/opt/flink/lib/hudi-flink-bundle.jar
networks:
- stream-net
# ========== Flink TaskManager ==========
flink-taskmanager:
image: flink:1.17.1-scala_2.12
container_name: flink-taskmanager
depends_on:
- flink-jobmanager
command: taskmanager
environment:
- |
FLINK_PROPERTIES=
jobmanager.rpc.address: flink-jobmanager
taskmanager.numberOfTaskSlots: 2
taskmanager.memory.process.size: 2048m
volumes:
- ./flink/conf:/opt/flink/conf
- ./hudi/hudi-flink-bundle.jar:/opt/flink/lib/hudi-flink-bundle.jar
networks:
- stream-net
# ========== MinIO (S3兼容对象存储) ==========
minio:
image: minio/minio:latest
container_name: minio
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: password
command: server /data --console-address ":9001"
volumes:
- ./data/minio:/data
networks:
- stream-net
networks:
stream-net:
driver: bridge
4.5 关键配置解读
这段配置里,有几个地方我特别想强调一下:
Kafka 的 ADVERTISED_LISTENERS
这个参数坑过我好几次。简单说,它告诉客户端「你应该连哪个地址」。如果你在容器内用 kafka:9092,在宿主机用 localhost:9092。我这里的配置是给宿主机用的,如果你要在其他容器里访问,记得改成服务名。
Flink 的 Hudi JAR 挂载
注意看 volumes 里那一行:
- ./hudi/hudi-flink-bundle.jar:/opt/flink/lib/hudi-flink-bundle.jar
这是把 Hudi 的依赖直接塞进 Flink 的 lib 目录。为什么这么做?因为 Flink 官方镜像默认不带 Hudi,你不挂载的话,跑 SQL 时会报 ClassNotFoundException。我曾经因为这个排查了半小时,后来发现是 jar 包版本不对——记住,Hudi 0.13+ 才支持 Flink 1.17。
MinIO 的端口映射
9000 是 S3 API 端口,9001 是管理控制台。开发时你可以打开 http://localhost:9001 看看数据有没有写进去。
4.6 启动与验证
配置写好了,我们来启动它:
# 启动所有服务
docker-compose up -d
# 查看日志
docker-compose logs -f
# 检查各服务状态
docker-compose ps
启动后,验证一下各个组件是否正常:
| 组件 | 验证方式 | 预期结果 |
|---|---|---|
| ZooKeeper | echo ruok | nc localhost 2181 |
返回 imok |
| Kafka | docker exec kafka kafka-topics --list --bootstrap-server localhost:9092 |
返回空列表(无报错) |
| Flink | 浏览器访问 http://localhost:8081 |
看到 Flink Web UI |
| MinIO | 浏览器访问 http://localhost:9001 |
看到登录页面 |
docker exec kafka kafka-topics --create --topic test --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1。这样能快速确认 Kafka 是否正常工作。
4.7 避坑指南
- 端口冲突:本地如果已经装了 Kafka 或 Flink,记得先停掉,或者改端口映射
- 内存不足:Flink JobManager 和 TaskManager 各占 2G,加上 Kafka 和 MinIO,建议给 Docker 至少分配 8G 内存
- 网络问题:所有服务必须在同一个 network 里,否则连不上
- Hudi 版本:Flink 1.17 必须用 Hudi 0.13.0+,低版本会报序列化错误
4.8 验证 Hudi 集成
最后一步,确认 Flink 能正常读写 Hudi。启动 Flink SQL Client:
docker exec -it flink-jobmanager ./bin/sql-client.sh
然后执行:
CREATE TABLE hudi_test (
id INT,
name STRING,
ts TIMESTAMP(3),
PRIMARY KEY (id) NOT ENFORCED
) WITH (
'connector' = 'hudi',
'path' = 's3a://hudi-bucket/test_table',
'table.type' = 'COPY_ON_WRITE',
'hoodie.bucket.index.num.buckets' = '4',
's3a.endpoint' = 'http://minio:9000',
's3a.access.key' = 'admin',
's3a.secret.key' = 'password'
);
INSERT INTO hudi_test VALUES (1, 'hello', NOW());
SELECT * FROM hudi_test;
如果能看到数据返回,恭喜你,环境搭建成功了!
核心要点回顾:
- Docker Compose 让环境搭建变得可重复、可分享
- Kafka 的 ADVERTISED_LISTENERS 是常见坑点
- Hudi JAR 必须挂载到 Flink lib 目录
- MinIO 提供 S3 兼容存储,方便本地开发
- 所有服务必须在同一个 Docker network 内