3. 环境搭建:Docker Compose 搭建 Kafka + Flink + Redis 开发环境
说实话,学实时数据流,最劝退的就是环境搭建。
Kafka、Flink、Redis,这三个东西单独装都不算难,但要让它们在一个局域网里好好配合,端口不冲突、版本不打架、网络能互通——我当年第一次搞的时候,折腾了整整一个周末。后来我学乖了,直接用 Docker Compose,一条命令拉起所有服务。
这一章,我就带你走一遍我的「标准配置」。你跟着做,半小时内就能跑起来。
3.1 为什么选 Docker Compose?
你可能会问:我直接在本地装不行吗?
行,但我不建议。原因有三:
- 隔离性:每个服务跑在独立容器里,不会污染你的宿主机。我曾经在 Mac 上装 Kafka,结果和本地的 Java 版本冲突,修了半天。
- 可复现:写一个
docker-compose.yml,团队里任何人拉下来都能跑出一模一样的环境。你想想看,这比写十页部署文档靠谱多了。 - 易清理:
docker-compose down -v一键清空,不留痕迹。做实验、搞坏了,重来就是几秒钟的事。
说白了,Docker Compose 就是给本地开发环境用的「基础设施即代码」。你值得拥有。
3.2 整体架构图
先看一眼我们要搭的东西长什么样:
3.3 准备工作
在开始之前,确认你的机器上已经装好了:
- Docker:20.10 以上版本。用
docker --version检查。 - Docker Compose:v2 以上。用
docker compose version检查(注意是空格,不是横线)。
如果你还没装,去官网下载 Docker Desktop 就行,它自带 Compose。我个人习惯用 macOS,但这套配置在 Linux 和 Windows WSL2 上同样跑得很顺。
3.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"
networks:
- realtime-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
networks:
- realtime-net
# ---------- Flink JobManager ----------
flink-jobmanager:
image: flink:1.17.1-scala_2.12
container_name: flink-jobmanager
ports:
- "8081:8081"
command: jobmanager
environment:
- JOB_MANAGER_RPC_ADDRESS=flink-jobmanager
networks:
- realtime-net
# ---------- Flink TaskManager ----------
flink-taskmanager:
image: flink:1.17.1-scala_2.12
container_name: flink-taskmanager
depends_on:
- flink-jobmanager
command: taskmanager
environment:
- JOB_MANAGER_RPC_ADDRESS=flink-jobmanager
- TASK_MANAGER_NUMBER_OF_TASK_SLOTS=2
networks:
- realtime-net
# ---------- Redis ----------
redis:
image: redis:7.2-alpine
container_name: redis
ports:
- "6379:6379"
networks:
- realtime-net
networks:
realtime-net:
driver: bridge
latest。我曾经被 latest 坑过一次——某天拉下来发现 API 变了,整个项目跑不起来。版本锁定,是专业选手的基本素养。
3.5 启动服务
把上面的内容保存为 docker-compose.yml,然后在终端里执行:
# 启动所有服务(后台运行)
docker compose up -d
# 查看容器状态
docker compose ps
# 查看实时日志(按 Ctrl+C 退出)
docker compose logs -f
启动后,你会看到类似这样的输出:
NAME IMAGE STATUS PORTS
zookeeper confluentinc/cp-zookeeper:7.4.0 Up 2181/tcp
kafka confluentinc/cp-kafka:7.4.0 Up 0.0.0.0:9092->9092/tcp
flink-jobmanager flink:1.17.1-scala_2.12 Up 0.0.0.0:8081->8081/tcp
flink-taskmanager flink:1.17.1-scala_2.12 Up 6121-6123/tcp
redis redis:7.2-alpine Up 0.0.0.0:6379->6379/tcp
看到所有服务都是 Up 状态,就说明成功了。
3.6 验证连通性
服务跑起来了,但能不能互相通信?我们来验证一下。
3.6.1 验证 Kafka
进入 Kafka 容器,创建一个测试主题:
docker exec -it kafka kafka-topics --create \
--topic test-topic \
--bootstrap-server localhost:9092 \
--partitions 1 \
--replication-factor 1
然后启动一个生产者,随便发几条消息:
docker exec -it kafka bash -c 'echo "hello realtime" | kafka-console-producer --topic test-topic --bootstrap-server localhost:9092'
再启动消费者看看能不能收到:
docker exec -it kafka kafka-console-consumer \
--topic test-topic \
--from-beginning \
--bootstrap-server localhost:9092
如果看到 hello realtime 打印出来,Kafka 就通了。
3.6.2 验证 Redis
# 进入 Redis 容器
docker exec -it redis redis-cli
# 设置一个键值对
127.0.0.1:6379> set mykey "hello from flink"
OK
# 读取
127.0.0.1:6379> get mykey
"hello from flink"
# 退出
127.0.0.1:6379> exit
3.6.3 验证 Flink UI
打开浏览器,访问 http://localhost:8081。你应该能看到 Flink 的 Web Dashboard,里面显示有一个 TaskManager 在线,2 个 Slot 可用。
Resources → Network 的 DNS 改成 8.8.8.8,然后重启 Docker。
3.7 常用操作速查表
开发过程中,你肯定会频繁用到这些命令。我整理了一张表,建议你收藏:
| 操作 | 命令 |
|---|---|
| 启动所有服务 | docker compose up -d |
| 停止所有服务 | docker compose down |
| 停止并删除数据卷 | docker compose down -v |
| 查看日志 | docker compose logs -f [服务名] |
| 重启某个服务 | docker compose restart [服务名] |
| 进入容器 | docker exec -it [容器名] bash |
3.8 本章小结
到这里,你的本地开发环境就搭好了。我们有了:
- Kafka:消息队列,负责接收和缓存实时数据流。
- Flink:流计算引擎,负责处理数据。
- Redis:高速缓存,负责存储计算结果。
这三个服务通过 Docker 内部网络互联,你在宿主机上通过映射端口就能访问。接下来几章,我们会基于这个环境,一步步写出第一个实时数据管道。
嗯,环境搭好了,心里就踏实了。后面的事,咱们慢慢来。