3、环境搭建:Docker与Docker Compose安装、Kafka单机/集群部署、Zookeeper配置、网络与端口规划
好,咱们正式开始动手了。
这一章,说白了就是先把「厨房」搭好。你想想看,后面所有的数据回放逻辑、Kafka 的读写、消息的流转,全都要跑在咱们今天搭建的环境上。如果地基没打牢,后面出问题你都不知道是代码的锅还是环境的锅——我踩过这种坑,一次就够了。
3.1 为什么选 Docker?
我个人习惯,所有中间件优先用 Docker 部署。原因很简单:隔离、可复现、快速销毁。
你想想看,Kafka 依赖 Zookeeper,Zookeeper 又依赖 Java 环境。如果直接在宿主机上装,版本冲突、路径污染、卸载不干净……这些破事我当年全经历过。用 Docker,一个 docker-compose up -d 就全搞定了。出了问题,docker-compose down 一键清理,干干净净。
3.2 Docker 与 Docker Compose 安装
3.2.1 安装 Docker
嗯,这里要注意。不同操作系统安装方式略有不同,但核心思路一致。
Ubuntu / Debian 系:
# 更新包索引
sudo apt-get update
# 安装依赖
sudo apt-get install ca-certificates curl gnupg lsb-release
# 添加 Docker 官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 设置稳定版仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装 Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
# 验证安装
sudo docker run hello-world
CentOS / RHEL 系:
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER。然后退出重新登录生效。
3.2.2 安装 Docker Compose
Docker Compose 是用来定义和运行多容器 Docker 应用的工具。说白了,就是用一个 YAML 文件把 Kafka、Zookeeper 这些容器的启动参数全写在一起,一键启动。
# 下载最新稳定版
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 赋予执行权限
sudo chmod +x /usr/local/bin/docker-compose
# 验证安装
docker-compose --version
我曾经在某个项目里,因为 Docker Compose 版本太低,导致 depends_on 条件不生效,Kafka 在 Zookeeper 还没完全启动时就尝试连接,结果一直报错。所以建议你装最新版,省心。
3.3 Zookeeper 配置
Kafka 依赖 Zookeeper 来管理集群元数据、选举 Controller。虽然新版本 Kafka 正在尝试脱离 ZK,但现阶段咱们还是老老实实用。
3.3.1 单机 Zookeeper
最简单的配置,直接跑一个容器:
version: '3.8'
services:
zookeeper:
image: confluentinc/cp-zookeeper:7.4.0
container_name: zookeeper
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ports:
- "2181:2181"
这里 ZOOKEEPER_CLIENT_PORT 是客户端连接端口,ZOOKEEPER_TICK_TIME 是心跳间隔(毫秒)。默认 2000 就够用。
3.3.2 集群 Zookeeper(3 节点)
生产环境建议至少 3 个 ZK 节点,形成奇数集群。为什么是奇数?因为 ZK 选举需要超过半数节点存活,3 节点允许挂 1 个,4 节点也只允许挂 1 个(因为需要 3 票),多一个节点纯属浪费资源。
version: '3.8'
services:
zk1:
image: confluentinc/cp-zookeeper:7.4.0
container_name: zk1
environment:
ZOOKEEPER_SERVER_ID: 1
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_INIT_LIMIT: 5
ZOOKEEPER_SYNC_LIMIT: 2
ZOOKEEPER_SERVERS: "zk1:2888:3888;zk2:2888:3888;zk3:2888:3888"
ports:
- "2181:2181"
zk2:
image: confluentinc/cp-zookeeper:7.4.0
container_name: zk2
environment:
ZOOKEEPER_SERVER_ID: 2
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_INIT_LIMIT: 5
ZOOKEEPER_SYNC_LIMIT: 2
ZOOKEEPER_SERVERS: "zk1:2888:3888;zk2:2888:3888;zk3:2888:3888"
ports:
- "2182:2181"
zk3:
image: confluentinc/cp-zookeeper:7.4.0
container_name: zk3
environment:
ZOOKEEPER_SERVER_ID: 3
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_INIT_LIMIT: 5
ZOOKEEPER_SYNC_LIMIT: 2
ZOOKEEPER_SERVERS: "zk1:2888:3888;zk2:2888:3888;zk3:2888:3888"
ports:
- "2183:2181"
3.4 Kafka 单机 / 集群部署
3.4.1 单机 Kafka
单机版适合开发测试。配置如下:
version: '3.8'
services:
kafka:
image: confluentinc/cp-kafka:7.4.0
container_name: kafka
depends_on:
- zookeeper
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
ports:
- "9092:9092"
这里 KAFKA_ADVERTISED_LISTENERS 是关键。它告诉客户端「你应该用这个地址连接我」。如果你在本地开发,写 localhost:9092 没问题。但如果你在远程服务器或者容器间通信,就得写成宿主机 IP 或者容器名。
我曾经犯过一个低级错误:在服务器上部署了 Kafka,客户端连不上,折腾了半天才发现是 ADVERTISED_LISTENERS 写成了 localhost,客户端从外部访问当然连不上。改成公网 IP 就好了。
3.4.2 集群 Kafka(3 节点)
生产环境至少 3 个 Broker,配合 3 个 ZK 节点。配置如下:
version: '3.8'
services:
kafka1:
image: confluentinc/cp-kafka:7.4.0
container_name: kafka1
depends_on:
- zk1
- zk2
- zk3
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: 'zk1:2181,zk2:2181,zk3:2181'
KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://localhost:9092'
KAFKA_LISTENERS: 'PLAINTEXT://0.0.0.0:9092'
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
ports:
- "9092:9092"
kafka2:
image: confluentinc/cp-kafka:7.4.0
container_name: kafka2
depends_on:
- zk1
- zk2
- zk3
environment:
KAFKA_BROKER_ID: 2
KAFKA_ZOOKEEPER_CONNECT: 'zk1:2181,zk2:2181,zk3:2181'
KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://localhost:9093'
KAFKA_LISTENERS: 'PLAINTEXT://0.0.0.0:9093'
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
ports:
- "9093:9093"
kafka3:
image: confluentinc/cp-kafka:7.4.0
container_name: kafka3
depends_on:
- zk1
- zk2
- zk3
environment:
KAFKA_BROKER_ID: 3
KAFKA_ZOOKEEPER_CONNECT: 'zk1:2181,zk2:2181,zk3:2181'
KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://localhost:9094'
KAFKA_LISTENERS: 'PLAINTEXT://0.0.0.0:9094'
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
ports:
- "9094:9094"
KAFKA_BROKER_ID 必须唯一。KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR 设置为 3,确保 offset 信息有副本,不会因为单点故障丢失。
3.5 网络与端口规划
这一节容易被忽略,但恰恰是出问题最多的地方。我见过太多人因为端口冲突、网络不通排查半天。
3.5.1 端口规划表
| 服务 | 容器内部端口 | 宿主机映射端口 | 用途 |
|---|---|---|---|
| Zookeeper 1 | 2181 | 2181 | 客户端连接 |
| Zookeeper 2 | 2181 | 2182 | 客户端连接 |
| Zookeeper 3 | 2181 | 2183 | 客户端连接 |
| Zookeeper 集群通信 | 2888 | 不映射 | Leader 选举 |
| Zookeeper 集群通信 | 3888 | 不映射 | 数据同步 |
| Kafka 1 | 9092 | 9092 | 生产者/消费者连接 |
| Kafka 2 | 9093 | 9093 | 生产者/消费者连接 |
| Kafka 3 | 9094 | 9094 | 生产者/消费者连接 |
3.5.2 网络规划
Docker Compose 默认会创建一个 bridge 网络,所有容器可以通过服务名互相访问。但如果你需要跨宿主机通信(比如 Kafka 集群分布在多台机器上),就得用 overlay 网络或者直接使用宿主机网络模式。
我个人建议:开发环境用 bridge 网络,生产环境用宿主机网络模式。宿主机网络模式性能更好,延迟更低,但端口管理要更小心。
# 使用宿主机网络模式的配置示例
services:
kafka1:
network_mode: "host"
environment:
KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://192.168.1.10:9092' # 写实际宿主机 IP
ADVERTISED_LISTENERS 必须写宿主机 IP,不能写 localhost。否则其他机器上的客户端连不上。
3.6 整体架构图
下面这张图展示了咱们搭建的完整环境:3 个 ZK 节点 + 3 个 Kafka Broker,通过 Docker 容器运行在同一台宿主机上。
3.7 启动与验证
所有配置写好后,启动就一行命令:
docker-compose up -d
验证是否启动成功:
# 查看所有容器状态
docker-compose ps
# 查看 Kafka 是否正常
docker exec -it kafka1 kafka-topics --bootstrap-server localhost:9092 --list
# 创建一个测试主题
docker exec -it kafka1 kafka-topics --bootstrap-server localhost:9092 --create --topic test-topic --partitions 3 --replication-factor 3
# 查看主题详情
docker exec -it kafka1 kafka-topics --bootstrap-server localhost:9092 --describe --topic test-topic
如果看到类似 Leader: 1, Replicas: 1,2,3, Isr: 1,2,3 的输出,说明集群搭建成功,三个 Broker 都在正常工作。
docker logs zk1 查看日志,看到 binding to port 0.0.0.0/0.0.0.0:2181 就说明 ZK 就绪了。
好了,环境搭完了。后面咱们就可以开始写代码,真正玩转数据回放了。