博客

如何通过 Kafka 向 Elasticsearch 摄取数据

逐步指导如何使用 Python、Docker Compose 和 Kafka Connect 将 Apache Kafka 与 Elasticsearch 集成,以实现高效的数据摄取、索引和可视化。

在本文中,我们将展示如何将 Apache Kafka 与 Elasticsearch 集成,以实现数据摄取和索引。我们将概述 Kafka 及其生产者和消费者的概念,并创建一个日志索引,通过 Apache Kafka 接收消息并编制索引。该项目使用 Python 实现,代码可在GitHub 上获取。

准备工作

  • Docker 和 Docker Compose:确保计算机上安装了 Docker 和 Docker Compose。

  • Python 3.x:运行生产者和消费者脚本。

Apache Kafka 简介

Apache Kafka 是一个分布式流平台,可实现高扩展性和可用性以及容错。在 Kafka 中,数据管理通过主要组件进行:

  • 经纪人:负责在生产者和消费者之间存储和分发信息。

  • Zookeeper:管理和协调 Kafka 代理,控制集群状态、分区领导者和消费者信息。

  • 主题:发布和存储数据以供消费的渠道。

  • 消费者和生产者:生产者向主题发送数据,消费者检索数据。

图解 Apache Kafka

这些组件共同构成了 Kafka 生态系统,为数据流提供了一个强大的框架。

项目结构

为了解数据摄取过程,我们将其分为几个阶段:

  • 基础架构调配:设置 Docker 环境以支持 Kafka、Elasticsearch 和 Kibana。

  • 创建生产者:实现向日志主题发送数据的 Kafka 生产者。

  • 创建消费者:开发 Kafka 消费者,以便在 Elasticsearch 中读取信息并编制索引。

  • 输入验证:验证和确认发送和消耗的数据。

使用 Docker Compose 配置基础设施

我们利用 Docker Compose 配置和管理必要的服务。下面是 Docker Compose 代码,用于设置集成 Apache Kafka、Elasticsearch 和 Kibana 所需的各项服务,确保数据摄取过程。

version: "3"

services:

  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    container_name: zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181

  kafka:
    image: confluentinc/cp-kafka:latest
    container_name: kafka
    depends_on:
      - zookeeper
    ports:
      - "9092:9092"
      - "9094:9094"
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST:${HOST_IP}:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.15.1
    container_name: elasticsearch-8.15.1
    environment:
      - node.name=elasticsearch
      - xpack.security.enabled=false
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    volumes:
      - ./elasticsearch:/usr/share/elasticsearch/data
    ports:
      - 9200:9200

  kibana:
    image: docker.elastic.co/kibana/kibana:8.15.1
    container_name: kibana-8.15.1
    ports:
      - 5601:5601
    environment:
      ELASTICSEARCH_URL: http://elasticsearch:9200
      ELASTICSEARCH_HOSTS: '["http://elasticsearch:9200"]'

您可以直接从 Elasticsearch LabsGitHubrepo 访问该文件。

使用 Kafka 生产者发送数据

生产者负责向日志主题发送消息。通过分批发送信息,它提高了网络使用效率,允许使用batch_sizelinger_ms 设置进行优化,这两个设置分别控制批次的数量和延迟。配置acks='all' 可确保信息的持久存储,这对重要的日志数据至关重要。

producer = KafkaProducer(
   bootstrap_servers=['localhost:9092'],  # Specifies the Kafka server to connect
   value_serializer=lambda x: json.dumps(x).encode('utf-8'),  # Serializes data as JSON and encodes it to UTF-8 before sending
   batch_size=16384,     # Sets the maximum batch size in bytes (here, 16 KB) for buffered messages before sending
   linger_ms=10,         # Sets the maximum delay (in milliseconds) before sending the batch
   acks='all'            # Specifies acknowledgment level; 'all' ensures message durability by waiting for all replicas to acknowledge
)


def generate_log_message():
   levels = ["INFO", "WARNING", "ERROR", "DEBUG"]
   messages = [
       "User login successful",
       "User login failed",
       "Database connection established",
       "Database connection failed",
       "Service started",
       "Service stopped",
       "Payment processed",
       "Payment failed"
   ]
   log_entry = {
       "level": random.choice(levels),
       "message": random.choice(messages),
       "timestamp": time.time()
   }
   return log_entry

def send_log_batches(topic, num_batches=5, batch_size=10):
   for i in range(num_batches):
       logger.info(f"Sending batch {i + 1}/{num_batches}")
       for _ in range(batch_size):
           log_message = generate_log_message()
           producer.send(topic, value=log_message)
       producer.flush()


if __name__ == "__main__":
   topic = "logs"
   send_log_batches(topic)
   producer.close()

启动生产者时,消息会分批发送到主题,如下图所示:

INFO:kafka.conn:Set configuration …
INFO:log_producer:Sending batch 1/5 
INFO:log_producer:Sending batch 2/5
INFO:log_producer:Sending batch 3/5
INFO:log_producer:Sending batch 4/5

使用 Kafka 消费者消费数据并编制索引

消费者旨在高效处理消息,从日志主题中批量消费,并将其索引到 Elasticsearch 中。通过auto_offset_reset='latest' ,它可以确保消费者开始处理最新的邮件,而忽略较早的邮件,并且max_poll_records=10 将批量限制为 10 封邮件。使用fetch_max_wait_ms=2000 时,消费者最多等待 2 秒钟,积累足够的报文后再处理批处理。

在其主循环中,消费者消耗日志信息,处理每个批次并将其索引到 Elasticsearch 中,从而确保持续的数据摄取。

consumer = KafkaConsumer(
   'logs',                               
   bootstrap_servers=['localhost:9092'],
   auto_offset_reset='latest',            # Ensures reading from the latest offset if the group has no offset stored
   enable_auto_commit=True,               # Automatically commits the offset after processing
   group_id='log_consumer_group',         # Specifies the consumer group to manage offset tracking
   max_poll_records=10,                   # Maximum number of messages per batch
   fetch_max_wait_ms=2000                 # Maximum wait time to form a batch (in ms)
)

def create_bulk_actions(logs):
   for log in logs:
       yield {
           "_index": "logs",
           "_source": {
               'level': log['level'],
               'message': log['message'],
               'timestamp': log['timestamp']
           }
       }

if __name__ == "__main__":
   try:
       print("Starting message processing…")
       while True:

           messages = consumer.poll(timeout_ms=1000)  # Poll receive messages

           # process each batch messages
           for _, records in messages.items():
               logs = [json.loads(record.value) for record in records]
               bulk_actions = create_bulk_actions(logs)
               response = helpers.bulk(es, bulk_actions)
               print(f"Indexed {response[0]} logs.")
   except Exception as e:
       print(f"Erro: {e}")
   finally:
       consumer.close()
       print(f"Finish")

在 Kibana 中可视化数据

有了 Kibana,我们就能探索和验证从 Kafka 采集并在 Elasticsearch 中编入索引的数据。通过访问 Kibana 中的 "开发工具",您可以查看已编入索引的信息,并确认数据符合预期。例如,如果我们的 Kafka 生产者发送了 5 个批次,每个批次 10 条消息,那么我们应该在索引中看到总共 50 条记录。

要验证数据,可以使用开发工具部分的以下查询:

GET /logs/_search
{
  "query": {
    "match_all": {}
  }
}

响应:

响应验证数据 - Kafka& Elasticsearch

此外,Kibana 还提供创建可视化和仪表盘的功能,有助于使分析更加直观和互动。下面,您可以看到我们创建的仪表盘和可视化的一些示例,这些仪表盘和可视化以各种格式展示了数据,增强了我们对所处理信息的理解。

Kibana 可视化 - Kafka& Elasticsearch

使用 Kafka Connect 进行数据摄取

Kafka Connect 是一项服务,旨在促进数据源和目的地(汇)(如数据库或文件系统)之间的集成。它通过预定义的连接器自动处理数据移动。在我们的案例中,Elasticsearch 发挥着数据汇的作用。

使用 Kafka Connect 进行数据输入

使用 Kafka Connect,我们可以简化数据摄取流程,无需在 Elasticsearch 中手动实施数据摄取工作流。有了适当的连接器,Kafka Connect 就能将发送到 Kafka 主题的数据直接编入 Elasticsearch 索引,只需极少的设置,也无需额外编码。

使用 Kafka Connect

为了实现 Kafka Connect,我们将在 Docker Compose 设置中添加 kafka-connect 服务 。此配置的关键部分是安装 Elasticsearch 连接器,它将处理数据索引。

配置服务并创建 Kafka Connect 容器后,需要为 Elasticsearch 连接器创建配置文件。该文件定义了基本参数,如

  • connection.url:Elasticsearch 的连接 URL。

  • topics:连接器将监控的 Kafka 主题(本例中为"logs" )。

  • type.name:Elasticsearch 中的文档类型(通常为 _doc)。

  • value.converter:将 Kafka 消息转换为 JSON 格式。

  • value.converter.schemas.enable:指定是否包含模式。

  • schema.ignorekey.ignore :在编制索引时忽略 Kafka 模式和键的设置。

下面是在 Kafka Connect 中创建 Elasticsearch 连接器的curl 命令:

curl --location '{{url}}/connectors' \
--header 'Content-Type: application/json' \
--data '{
    "name": "elasticsearch-sink-connector",
    "config": {
        "connector.class": "io.confluent.connect.elasticsearch.ElasticsearchSinkConnector",
        "topics": "logs",
        "connection.url": "http://elasticsearch:9200",
        "type.name": "_doc",
        "value.converter": "org.apache.kafka.connect.json.JsonConverter",
        "value.converter.schemas.enable": "false",
        "schema.ignore": "true",
        "key.ignore": "true"
    }
}'

使用此配置后,Kafka Connect 将自动开始摄取发送到"logs" 主题的数据,并在 Elasticsearch 中编制索引。这种方法可实现全自动数据摄取和索引,无需额外编码,从而简化了整个集成过程。

结论

集成 Kafka 和 Elasticsearch 可为实时数据摄取和分析创建一个强大的管道。本指南为构建强大的数据摄取架构提供了基本方法,可在 Kibana 中实现无缝可视化和分析,随时适应未来更复杂的要求。

此外,使用 Kafka Connect 使 Kafka 和 Elasticsearch 之间的集成更加简化,无需额外的代码来处理数据和编制索引。Kafka Connect 使发送到特定主题的数据只需最少的配置就能在 Elasticsearch 中自动编入索引。

相关内容

显示 Elasticsearch 索引中的字段

Kofi Bartlett

在 Logstash 中使用 Ruby 脚本

Dai Sugimori

Elasticsearch 中的索引模板:如何使用可组合模板

Kofi Bartlett

如何显示 Elasticsearch 索引的字段

JD Armada

如何通过 LlamaIndex 向 Elasticsearch 采集数据

Andre Luiz

准备好打造最先进的搜索体验了吗?

足够先进的搜索不是一个人的努力就能实现的。Elasticsearch 由数据科学家、ML 操作员、工程师以及更多和您一样对搜索充满热情的人提供支持。让我们联系起来,共同打造神奇的搜索体验,让您获得想要的结果。

亲自试用