3. 开发环境搭建:Docker Compose 一键部署、Flink 集群配置、Kafka 安装与 Topic 创建、Doris 建表与 Redis 配置

说实话,搭建开发环境这件事,看着简单,但坑是真不少。我见过太多团队,项目还没开始跑,光环境就折腾了两三天。所以这一章,咱们直接上 Docker Compose,把 Flink、Kafka、Doris、Redis 一次性搞定。

你想想看,如果每个组件都手动装一遍,版本冲突、端口占用、配置遗漏……光是排查这些问题就够你喝一壶的。我个人习惯,只要是本地开发环境,能容器化就容器化。干净、可复现、一键销毁,这才是现代开发该有的样子。

本章核心目标:用 Docker Compose 在本地拉起一套完整的实时指标平台开发环境,包含 Flink 集群(1 JobManager + 2 TaskManager)、Kafka(含 Zookeeper)、Doris(FE + BE)和 Redis。

开发环境容器架构总览 Docker Compose 编排 Flink JobManager TaskManager 1 TaskManager 2 Kafka + Zookeeper Topic: click_events, order_flow Doris FE + BE 数据库: realtime_platform Redis 7.x 💡 说明 所有服务通过 docker-compose.yml 统一管理,端口映射、网络、数据卷均已预配置。 启动命令:docker compose up -d,停止命令:docker compose down。

3.1 Docker Compose 一键部署

我们先从 docker-compose.yml 开始。这个文件是整个环境的「总开关」。我习惯把每个服务拆成一个独立的 service 块,方便后续单独启停。

下面是我整理好的配置,你直接复制就能用。注意版本号,我选的都是经过生产验证的稳定版本。

version: '3.8'

services:
  # ========== Zookeeper ==========
  zookeeper:
    image: confluentinc/cp-zookeeper:7.4.0
    hostname: zookeeper
    container_name: zookeeper
    ports:
      - "2181:2181"
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    networks:
      - realtime-net

  # ========== Kafka ==========
  kafka:
    image: confluentinc/cp-kafka:7.4.0
    hostname: kafka
    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
    hostname: flink-jobmanager
    container_name: flink-jobmanager
    ports:
      - "8081:8081"
    command: jobmanager
    environment:
      - JOB_MANAGER_RPC_ADDRESS=flink-jobmanager
    networks:
      - realtime-net

  # ========== Flink TaskManager ==========
  flink-taskmanager-1:
    image: flink:1.17.1-scala_2.12
    hostname: flink-taskmanager-1
    container_name: flink-taskmanager-1
    depends_on:
      - flink-jobmanager
    command: taskmanager
    environment:
      - JOB_MANAGER_RPC_ADDRESS=flink-jobmanager
      - TASK_MANAGER_NUMBER_OF_TASK_SLOTS=4
    networks:
      - realtime-net

  flink-taskmanager-2:
    image: flink:1.17.1-scala_2.12
    hostname: flink-taskmanager-2
    container_name: flink-taskmanager-2
    depends_on:
      - flink-jobmanager
    command: taskmanager
    environment:
      - JOB_MANAGER_RPC_ADDRESS=flink-jobmanager
      - TASK_MANAGER_NUMBER_OF_TASK_SLOTS=4
    networks:
      - realtime-net

  # ========== Doris FE ==========
  doris-fe:
    image: apache/doris:2.0.3-fe
    hostname: doris-fe
    container_name: doris-fe
    ports:
      - "8030:8030"
      - "9030:9030"
    environment:
      - FE_ROLE=leader
    networks:
      - realtime-net

  # ========== Doris BE ==========
  doris-be:
    image: apache/doris:2.0.3-be
    hostname: doris-be
    container_name: doris-be
    depends_on:
      - doris-fe
    ports:
      - "8040:8040"
    environment:
      - FE_MASTER_HOST=doris-fe
      - FE_MASTER_PORT=9010
    networks:
      - realtime-net

  # ========== Redis ==========
  redis:
    image: redis:7.2.3
    hostname: redis
    container_name: redis
    ports:
      - "6379:6379"
    command: redis-server --requirepass realtime123
    networks:
      - realtime-net

networks:
  realtime-net:
    driver: bridge

我的经验:Kafka 的 ADVERTISED_LISTENERS 一定要配成 localhost:9092,否则你在宿主机上用客户端连 Kafka 会报连接超时。这个坑我踩过两次,第一次排查了半小时才发现是地址没配对。

配置写好了,启动就一句话:

docker compose up -d

等个一两分钟,所有容器都起来后,用 docker ps 检查一下。你应该能看到 8 个容器都在运行状态。

3.2 Flink 集群配置

Flink 集群启动后,访问 http://localhost:8081 就能看到 Web UI。我个人习惯先检查两个东西:TaskManager 数量和 Task Slot 总数。

默认配置下,我们有两个 TaskManager,每个 4 个 Slot,总共 8 个并发槽位。对于本地开发来说,完全够用了。

如果你想调整并行度,可以在 flink-conf.yaml 里改。但容器化环境下,我更推荐通过环境变量传参:

environment:
  - TASK_MANAGER_NUMBER_OF_TASK_SLOTS=4
  - JOB_MANAGER_RPC_ADDRESS=flink-jobmanager

注意:Flink 容器之间通过容器名通信,所以 JOB_MANAGER_RPC_ADDRESS 必须填 service 名称,不能填 localhost 或 127.0.0.1。否则 TaskManager 注册不上 JobManager。

3.3 Kafka 安装与 Topic 创建

Kafka 容器起来后,我们进到容器里创建 Topic。这个步骤我建议写成脚本,因为后面会反复用到。

先进入 Kafka 容器:

docker exec -it kafka bash

然后创建两个核心 Topic:

# 创建点击事件 Topic
kafka-topics --create \
  --bootstrap-server localhost:9092 \
  --replication-factor 1 \
  --partitions 3 \
  --topic click_events

# 创建订单流 Topic
kafka-topics --create \
  --bootstrap-server localhost:9092 \
  --replication-factor 1 \
  --partitions 3 \
  --topic order_flow

验证一下 Topic 是否创建成功:

kafka-topics --list --bootstrap-server localhost:9092

你应该能看到 click_events 和 order_flow 两个 Topic。

为什么分区数设 3? 分区数决定了 Kafka 的并行消费能力。本地开发设 3 个分区,既能模拟多分区场景,又不会太浪费资源。生产环境一般按业务流量估算,通常 6~12 个分区起步。

3.4 Doris 建表

Doris 的 FE 启动后,我们用 MySQL 客户端连上去。默认端口是 9030,用户名 root,密码为空。

mysql -h 127.0.0.1 -P 9030 -u root

连上之后,先建数据库,再建表。咱们这个平台核心是实时指标,所以表结构要支持高并发写入和快速聚合查询。

-- 创建数据库
CREATE DATABASE IF NOT EXISTS realtime_platform;

-- 使用数据库
USE realtime_platform;

-- 创建点击事件明细表
CREATE TABLE IF NOT EXISTS click_events (
    event_id      VARCHAR(64)   NOT NULL,
    user_id       VARCHAR(32)   NOT NULL,
    page_url      VARCHAR(512)  NOT NULL,
    click_time    DATETIME      NOT NULL,
    event_type    VARCHAR(32)   NOT NULL,
    duration_ms   INT           DEFAULT 0,
    load_time     DATETIME      REPLACE_IF_NOT_NULL DEFAULT '1970-01-01 00:00:00'
)
ENGINE = OLAP
UNIQUE KEY(event_id, user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 10
PROPERTIES (
    "replication_num" = "1",
    "storage_medium" = "SSD"
);

关键点说明:

  • UNIQUE KEY:保证同一事件不会重复写入,适合实时去重场景
  • DISTRIBUTED BY HASH:按 user_id 分桶,查询时能快速定位到对应分片
  • BUCKETS 10:本地开发 10 个桶够用,生产环境按数据量调整
  • replication_num = 1:本地单副本,生产环境至少 3 副本

建完表后,用 SHOW TABLES; 确认一下。如果看到 click_events 表,说明 Doris 这边搞定了。

3.5 Redis 配置

Redis 我们用来做实时指标的缓存层。比如用户最近 5 分钟的点击次数、实时排行榜这些,直接从 Redis 读比查 Doris 快得多。

在 docker-compose.yml 里,我已经配了密码:realtime123。连接方式如下:

redis-cli -h 127.0.0.1 -p 6379 -a realtime123

验证一下连通性:

127.0.0.1:6379> PING
PONG

能返回 PONG,说明 Redis 跑起来了。

我的习惯:开发环境 Redis 密码设简单点没关系,但生产环境一定要用强密码,并且开启 TLS 加密。另外,建议把 maxmemory 设一个上限,防止 Redis 把宿主机内存吃光。

3.6 环境验证清单

所有组件都配好了,最后跑一遍验证清单,确保每个环节都没问题。

组件 验证方式 预期结果
Docker 容器 docker ps | wc -l 8 个容器运行中
Flink Web UI 浏览器访问 localhost:8081 显示 2 个 TaskManager,8 个 Slot
Kafka Topic kafka-topics --list 显示 click_events, order_flow
Doris 建表 SHOW TABLES; 显示 click_events 表
Redis 连通性 PING 返回 PONG

到这步,你的开发环境就全部就绪了。接下来就可以开始写 Flink 作业,把数据从 Kafka 读到 Doris,再用 Redis 做实时缓存。嗯,这才是真正有意思的部分。