Saltar al contenido

lección 5

Apache Kafka: instalar con Docker y entender los fundamentos

Levanta un cluster Kafka con Docker, entiende topics, particiones, brokers y produce/consume tus primeros mensajes.

55 min

### Kafka: la autopista de datos que mueve el mundo

Si SQS es un buzón de correos y EventBridge es una centralita de hotel, Kafka es una AUTOPISTA DE DATOS. Una infraestructura industrial diseñada para mover MILLONES de mensajes por segundo con latencia de milisegundos. LinkedIn la creó en 2011 porque necesitaba procesar la actividad de 700 millones de usuarios en tiempo real. Hoy, Kafka mueve datos en Netflix, Uber, Airbnb, Goldman Sachs, y prácticamente cualquier empresa con flujos de datos a escala.

¿Por qué existe Kafka si ya teníamos colas como RabbitMQ o SQS? Porque las colas tradicionales tienen un modelo "consume y desaparece": cuando un consumidor lee un mensaje, se borra. Kafka es diferente: los mensajes se PERSISTEN en disco durante días o semanas. Múltiples consumidores pueden leer el MISMO mensaje. Puedes "rebobinar" y releer desde el principio. Es un LOG DISTRIBUIDO, no una cola.

### La analogía del periódico vs el buzón

SQS es como un buzón: recibes una carta, la lees, la tiras. Nadie más puede leerla. Kafka es como un periódico: se publica UNA VEZ, pero miles de personas lo leen independientemente. El periódico no desaparece cuando tú lo lees — sigue disponible para los demás. Y si llegaste tarde, puedes ir a la hemeroteca y leer ediciones anteriores (replay). Eso es Kafka: un log inmutable y persistente que múltiples consumidores leen a su propio ritmo.

### Los conceptos fundamentales de Kafka

  • TOPIC: canal con nombre donde se publican mensajes. Equivale al "tema" del periódico (deportes, economía, etc.).
  • PARTITION: subdivisión de un topic para paralelismo. Cada partición es un log ordenado e inmutable.
  • BROKER: un servidor Kafka. Un cluster tiene múltiples brokers para redundancia.
  • PRODUCER: quien escribe mensajes en un topic.
  • CONSUMER: quien lee mensajes de un topic.
  • OFFSET: posición del consumidor en una partición. "Voy por el mensaje 4.237 de 10.000".
  • CONSUMER GROUP: grupo de consumidores que se reparten las particiones. Paralelismo coordinado.
Kafka: un topic tiene particiones, cada una es un log ordenado que múltiples consumidores leen independientemente

### Instalar Kafka con Docker Compose

Kafka necesita un servicio de coordinación. Históricamente era ZooKeeper, pero desde Kafka 3.3+ existe KRaft (Kafka Raft) que elimina esa dependencia. Usaremos KRaft porque es el futuro y simplifica el setup. Un solo contenedor Docker con Kafka en modo KRaft es suficiente para aprender.

1# Crear carpeta de trabajo
2# Windows (PowerShell):
3mkdir kafka-lab; cd kafka-lab
4
5# Mac (Terminal):
6mkdir kafka-lab && cd kafka-lab

Crear el directorio del laboratorio Kafka

1# docker-compose.yml para Kafka con KRaft (sin ZooKeeper)
2cat > docker-compose.yml << 'EOF'
3services:
4 kafka:
5 image: confluentinc/cp-kafka:7.6.0
6 hostname: kafka
7 container_name: kafka-broker
8 ports:
9 - "9092:9092"
10 environment:
11 KAFKA_NODE_ID: 1
12 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
13 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
14 KAFKA_LISTENERS: PLAINTEXT://kafka:29092,CONTROLLER://kafka:29093,PLAINTEXT_HOST://0.0.0.0:9092
15 KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
16 KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:29093
17 KAFKA_PROCESS_ROLES: broker,controller
18 KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
19 KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
20 KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
21 KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
22 KAFKA_LOG_RETENTION_HOURS: 168
23 CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk
24 volumes:
25 - kafka-data:/var/lib/kafka/data
26volumes:
27 kafka-data:
28EOF

docker-compose.yml: Confluent Platform 7.6 (Apache Kafka 3.6) con KRaft. Un broker, perfecto para desarrollo.

1# Levantar Kafka
2# Windows (PowerShell) y Mac (Terminal):
3docker compose up -d
4
5# Verificar que está corriendo:
6docker compose ps
7
8# Esperar ~15 segundos a que arranque completamente
9# Verificar que responde:
10docker exec kafka-broker kafka-topics --bootstrap-server localhost:9092 --list
11
12# Si no hay error, Kafka está listo.

Levantar y verificar el cluster Kafka

### Crear tu primer topic

Un topic es el canal donde se publican los mensajes. Es como una carpeta con nombre. Vamos a crear un topic "pedidos" con 3 particiones. ¿Por qué 3 particiones? Porque cada partición puede ser consumida por un consumidor diferente en paralelo. 3 particiones = hasta 3 consumidores procesando en paralelo.

1# Crear un topic con 3 particiones
2docker exec kafka-broker kafka-topics --bootstrap-server localhost:9092 \
3 --create --topic pedidos --partitions 3 --replication-factor 1
4
5# Verificar que se creó:
6docker exec kafka-broker kafka-topics --bootstrap-server localhost:9092 \
7 --describe --topic pedidos
8
9# Salida esperada:
10# Topic: pedidos Partitions: 3 ReplicationFactor: 1
11# Partition: 0 Leader: 1 Replicas: 1 Isr: 1
12# Partition: 1 Leader: 1 Replicas: 1 Isr: 1
13# Partition: 2 Leader: 1 Replicas: 1 Isr: 1

Crear un topic "pedidos" con 3 particiones

### Producir y consumir desde la terminal

Kafka incluye herramientas de línea de comandos para producir y consumir. Vamos a usarlas primero para ver el flujo antes de programar en Python.

1# Terminal 1 — Consumidor (escucha desde el principio):
2docker exec -it kafka-broker kafka-console-consumer \
3 --bootstrap-server localhost:9092 \
4 --topic pedidos \
5 --from-beginning
6
7# Terminal 2 — Productor (escribe mensajes):
8docker exec -it kafka-broker kafka-console-producer \
9 --bootstrap-server localhost:9092 \
10 --topic pedidos
11
12# Escribe mensajes y pulsa Enter:
13# > {"pedido_id": "ORD-001", "total": 49.99}
14# > {"pedido_id": "ORD-002", "total": 129.00}
15# > {"pedido_id": "ORD-003", "total": 15.50}
16# Verás los mensajes aparecer en la Terminal 1 instantáneamente.

Producir y consumir desde la CLI: la forma más rápida de verificar que funciona

### Producir desde Python con kafka-python

1# Instalar la librería kafka-python
2# Windows (PowerShell) y Mac (Terminal):
3pip install kafka-python

Instalar kafka-python en tu entorno virtual

1from kafka import KafkaProducer
2import json
3import time
4
5# Crear productor
6producer = KafkaProducer(
7 bootstrap_servers=["localhost:9092"],
8 value_serializer=lambda v: json.dumps(v).encode("utf-8"),
9 key_serializer=lambda k: k.encode("utf-8") if k else None,
10)
11
12# Producir 10 mensajes
13for i in range(10):
14 pedido = {
15 "pedido_id": f"ORD-{1000 + i}",
16 "cliente_id": f"CLI-{i % 5}",
17 "total": round(20 + i * 15.5, 2),
18 "timestamp": time.time(),
19 }
20 # La KEY determina a qué partición va el mensaje
21 future = producer.send(
22 topic="pedidos",
23 key=pedido["cliente_id"], # Misma key = misma partición
24 value=pedido,
25 )
26 metadata = future.get(timeout=10)
27 print(f"Enviado: {pedido['pedido_id']} -> partition={metadata.partition} offset={metadata.offset}")
28
29producer.flush()
30producer.close()
31print("\nProductor finalizado. 10 mensajes enviados.")

Productor Python: la KEY decide en qué partición aterriza cada mensaje

### Consumir desde Python

1from kafka import KafkaConsumer
2import json
3
4consumer = KafkaConsumer(
5 "pedidos",
6 bootstrap_servers=["localhost:9092"],
7 auto_offset_reset="earliest", # Leer desde el principio
8 group_id="mi-primer-grupo",
9 value_deserializer=lambda m: json.loads(m.decode("utf-8")),
10)
11
12print("Consumidor escuchando topic 'pedidos'...")
13count = 0
14for message in consumer:
15 pedido = message.value
16 print(f" [{message.partition}:{message.offset}] {pedido['pedido_id']} - {pedido['total']}EUR")
17 count += 1
18 if count >= 10:
19 break
20
21consumer.close()
22print(f"\nConsumidos {count} mensajes.")

Consumidor Python: lee mensajes indicando partición y offset de cada uno

### La KEY y la distribución entre particiones

La KEY del mensaje es FUNDAMENTAL en Kafka. Determina en qué partición aterriza el mensaje (mediante murmur2(key) % num_particiones — no el hash() de Python, que cambia en cada proceso). Mensajes con la MISMA key van SIEMPRE a la MISMA partición. ¿Por qué importa? Porque dentro de una partición el ORDEN está garantizado. Si quieres que todos los eventos de un mismo cliente se procesen en orden, usa el cliente_id como key. Todos irán a la misma partición y se consumirán en secuencia.

Si no especificas key (key=None), el reparto depende del cliente: kafka-python elige partición al azar, y el cliente de Java (desde 2.4) usa un partidor "sticky" que llena un lote y luego cambia. En ambos casos, máxima distribución y cero garantía de orden entre mensajes del mismo cliente.

Consejo de senior: elige la key con CUIDADO. Una key con poca cardinalidad (ej. "país" con 4 valores) crea particiones desbalanceadas — una partición tiene el 80% del tráfico. Una key con alta cardinalidad (ej. pedido_id) distribuye uniformemente pero pierde ordenación por cliente. La key ideal es el "agregado" que quieres ordenar: cliente_id, cuenta_id, dispositivo_id.

Lo que le diría a mi yo de hace 5 años: el número de particiones es DIFÍCIL de cambiar después. Si creas un topic con 3 particiones y mañana necesitas 10, puedes añadirlas pero los mensajes existentes NO se redistribuyen. Planifica desde el inicio: para producción, piensa en el throughput máximo esperado.

Kafka no garantiza orden ENTRE particiones, solo DENTRO de una partición. Si tu topic tiene 3 particiones, los mensajes de particiones diferentes pueden llegar en cualquier orden al consumidor. No asumas orden global.

## ejercicios

[01]

Productor Kafka: stream de ventas Black Friday

Es Black Friday y el e-commerce genera 1 venta por segundo. Escribe un productor que envíe 20 eventos de venta al topic "ventas-blackfriday" con key=tienda_id (hay 4 tiendas). Cada venta tiene: venta_id, tienda_id, producto, precio, timestamp. Qué deberías ver: 20 líneas con venta_id, tienda, partición y offset. Las ventas de la misma tienda siempre caen en la misma partición.

Cargando editor...
[02]

Consumidor Kafka: leer ventas y calcular total por tienda

Escribe un consumidor que lea del topic "ventas-blackfriday" desde el inicio y calcule el total vendido por cada tienda. Usa auto_offset_reset="earliest" y group_id="analytics-v1". Qué deberías ver: una línea por cada mensaje con partición, offset, venta_id, tienda y precio; al final, el resumen de ventas por tienda con el total en EUR.

Cargando editor...
[03]

Explorar topics y particiones con la CLI

Usa los comandos CLI de Kafka para: 1) Listar todos los topics, 2) Describir el topic "pedidos" (ver particiones y réplicas), 3) Ver el offset actual de cada partición del topic "pedidos". Qué deberías ver: --list muestra los topics creados; --describe muestra 3 particiones con Leader:1, Replicas:1, Isr:1; kafka-get-offsets muestra pedidos:0:N pedidos:1:N pedidos:2:N con los offsets actuales.

Cargando editor...
[04]

Verificar que la key dirige a la misma partición

Produce 10 mensajes con key="cliente-A", 10 con key="cliente-E" y 10 con key="cliente-F". Verifica que cada key va siempre a la misma partición y que las tres caen en particiones distintas (con 3 particiones y murmur2, estas keys reparten 0/1/2). Qué deberías ver: los 10 de cliente-A siempre en la misma partición, los de cliente-E en otra, y los de cliente-F en la tercera.

Cargando editor...

Regístrate para guardar tu progreso.

## comentarios

Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.

Inicia sesión para comentar y responder.

cargando comentarios...