Kafka + Docker + Net Core 101 - parte 1
Tags: kafka, dotnetcore, aspnetcore, csharp
Published: 2020-06-10 21:33:52

Updates
* 20/07/2020: Actualizado para May 2020 Windows Update y Docker Desktop sobre WSL2.

***

Si llegaste hasta este lugar es que ya sabes de lo que estamos hablando y quieres meter manos en el código de forma inmediata entonces esta serie de artículos es para vos 😀

Si aún no tienes bien en claro qué es **Kafka** y para que se lo puede usar puedes pasarte por estos lados:

{% medium https://medium.com/@sonusharma.mnnit/apache-kafka-in-depth-49aae1e844be %}

{% medium https://medium.com/@rinu.gour123/kafka-for-beginners-74ec101bc82d %}

Bueno, ahora ya sabemos para que podemos usar Kafka tratemos de correrlo localmente en nuestra PC, probar comandos básicos y crear una aplicación básica que publique eventos y otra que los consuma utilizando **Net Core**.

***

# Kafka + Docker

Para levantar nuestro ambiente creamos un archivo de Docker Compose donde instanciaremos un servicio de _Zookeeper_ y un servicio de _Kafka_ (luego se pueden levantar mas y armar los clusters).

Las imágenes base que vamos a utilizar son las de los amigos de _[Confluence](https://github.com/confluentinc/cp-docker-images)_. No son las únicas, también podemos usar las de _[Bitmani|Zookeeper](https://bitnami.com/stack/zookeeper/containers)_, _[Bitmani|Kafka](https://bitnami.com/stack/kafka/containers)_, _[Spotify](https://hub.docker.com/r/spotify/kafka/)_ o _[Wurstmeister](https://github.com/wurstmeister/kafka-docker)_.

Nuestro docker-compose file nos queda de esta forma:

``` docker-compose
version: '2'
services:
    zookeeper:
        image: confluentinc/cp-zookeeper:latest
        environment:
            ZOOKEEPER_CLIENT_PORT: 2181
            ZOOKEEPER_TICK_TIME: 2000
    kafka:
        image: confluentinc/cp-kafka:latest
        depends_on:
            - zookeeper
        ports:
            - 9092:9092
        environment:
            KAFKA_BROKER_ID: 1
            KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
            KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://127.0.0.1:9092
            KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
            KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
            KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
```

> **Imporatante:** Para acceder a _Kafka_ desde fuera del container se debe reeamplazar `localhost` en _ADVERTISED_ por el IP del Docker-Machine.

## Levantando el ambiente con Docker Tools (windows)

Abrimos la consola de docker y creamos una nueva *Docker Machine* ejecutando el siguiente comando

```bash
docker-machine create --driver virtualbox confluent
```

Indicamos que vamos a utilizar el driver de *VirtualBox* para crear la máquina virtual del host y la llamaremos _confluent_ (podemos usar cualquier nombre)

Si usamos Linux o Windows Profesional en adelante, podemos saltarnos esta opción e ir directamente al `docker-compose up`, pero para quienes usamos Windows 10 Home o versiones mas antiguas con Docker Toolbox es un paso que necesitamos hacer para tener una experiencia similar.

Revisamos que se haya creado

```bash
docker-machine ls
```

Debemos ver algo similar a esto

![doker-machine ls](https://dev-to-uploads.s3.amazonaws.com/i/zs27km03s4aleugf1bxk.PNG)

Lo siguiente a realizar es entrar a la *docker machine* que acabamos de crear para que los comandos se ejecuten sobre ella (sino se haría sobre la default)

```bash
eval $(docker-machine env confluent)
```

Obtenemos el IP para luego poder conectarnos desde *C#* y para usarla en la variable de entorno _KAFKA_ADVERTISED_LISTENERS_ del archivo de docker compose

```bash
docker-machine ip confluent
```

Por último, corremos `docker-compose` en la carpeta donde hayamos puesto el archivo yaml

```bash
docker-compose up -d
```

Verificamos que se encuentren Kafka y Zookeeper levantados

```bash
docker container ps
```

## Levantando el ambiente en Linux o con Docker for Windows

Simplemente corremos Docker Compose
```bash
docker-compose up -d
```

Si estas usando WSL puedes obtener el IP de la siguiente forma

```
wsl ip -a
```

Con esta mínima configuración tenemos *Kafka* listo para las primeras pruebas, let's go! 💪😀

***

# Divirtámonos con Kafka 

Por default, **Kafka** viene con scrips para crear topics, publicar/leer mensajes, etc. Durante esta parte, vamos a ver como trabajar con ellos

## Crear un topic nuevo

```bash
docker container exec <kafka-container-id> kafka-topics \
--create \
--topic foo \
--partitions 1 \
--replication-factor 1 \
--if-not-exists \
--zookeeper zookeeper:2181
```

Creamos un nuevo _Topic_ en _Kafka_ con una factor de replicación de 1 y con una sola partición. Si quisiéramos aumentar estos valores debemos levantar otras instancias de Kafka.
El parámetro _--if-not-exists_ indica que el topic se creará si no existe previamente.

###Listar los topics###

```bash
docker container exec <kafka-container-id> kafka-topics \
--list \
--zookeeper zookeeper:2181
```

Este comando va a listar únicamente los nombres de los _Topics_ que tenemos creados

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mzsz90i9y72tbws6f3bu.PNG)


## Ver la descripción de un topic

```bash
docker container exec <kafka-container-id> kafka-topics \
--describe \
--topic foo \
--zookeeper zookeeper:2181
```

Nos muestra información relevante del Topic

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/aiaxxozrh3ih9e8v2lq5.PNG)


## Publicar un mensaje al topic

En este caso es mejor ingresar a la consola del container de Kafka y ejecutar el Producer desde allí

```bash
docker container exec -it <kafka-container-id> /bin/bash
```

Ahora ejecutamos el _Publisher_

```bash
kafka-console-producer \
--request-required-acks 1 \
--broker-list <docker-machine-ip>:9092 \
--topic foo
```

Luego por cada línea que escribamos (separadas por un [Enter]) se enviará un mensaje (usamos [ctrl+c] para salir del comando y exit para salir del container).

## Leer de un topic

```bash
docker container exec <kafka-container-id> kafka-console-consumer \
--bootstrap-server <docker-machine-ip>:9092 \
--topic foo \
--zookeeper  zookeeper:2181
```

Estos son los básicos para comenzar y hacer las primeras pruebas. Cada uno tiene múltiples parámetros a investigar y sacar mayor provecho.
Para un major detalle pueden ir a la docu [oficial](https://kafka.apache.org/documentation/#operations).


***

# C# conoce Kafka

Ya tenemos **Kafka** ejecutándose en nuestro PC, ahora veremos como publicar y leer mensajes en un _Topic_.

**Producer**
Creamos una aplicación de consola y usamos el siguiente código reemplazando el _<IP>_ por el que tiene el _Docker-Machine_.
{% gist https://gist.github.com/andreslozada/2378608202576fb94248c9b1e50cf728 file=Producer.cs  %}

Compilamos y ejecutamos indicando el nombre del topic al cual enviaremos mensajes. La aplicación queda a la espera de que escribamos mensajes uno por línea.

```bash
dotnet build
.
.
.
dotnet run foo

```

**Consumer**
Para el Consumer, creamos una nueva aplicación de consola y usamos el siguiente código reemplazando el valor de _<IP>_ por el IP del _Docker-Machine_ que estamos usando.

{% gist https://gist.github.com/andreslozada/2378608202576fb94248c9b1e50cf728 file=Consumer.cs  %}

Compilamos y corremos indicando que _Topic_ queremos escuchar

```bash
dotnet build
.
.
.
dotnet run foo
```

Ejecutando ambos programas en la consola una al lado de la otra, podremos ver que cuando publicamos un mensaje, éste aparece en la otra.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/rj2ku5r6xnevrtvjt5g5.PNG)

Pueden bajarse el código desde [acá](https://github.com/andreslozada/net-thoughts/tree/master/kafka/examples/parte1)

***

# Kafka tools

El universo de Tools de Kafka es inmenso y en estos listados se puede encontrar de todo:

* https://github.com/monksy/awesome-kafka
* https://github.com/semantalytics/awesome-kafka
* https://github.com/dharmeshkakadia/awesome-kafka#testing

Hay varias herramientas para manejar _Kafka_ por medio de UI y no por comandos

* [Conduktor](https://www.conduktor.io/)
* [KafkaTool](https://www.kafkatool.com/)
* [Kafdrop](https://github.com/obsidiandynamics/kafdrop)

Hay otros dando vueltas por ahí...

Entre los que probe, _Conduktor_ parece bastante completo aunque la version free tiene sus limitantes. Lo bueno es que tiene integración para conectarse a _Confluent_ y _Aiven_ para usar sus _plataformas SaaS de Kafka_

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/7zyszz9k299wuqlq4se0.PNG)

***

# Cerrando

**Kafka** es un monstruo que nos provee un nuevo universo de posibilidades para extender nuestras arquitecturas de maneras impensadas pero no hay que tenerle miedo para comenzar.

Vimos que con muy poco (gracias a los que crearon las imágenes de docker) se puede tener todo lo necesario para tener a Kafka corriendo sobre docker en nuestra PC y lo fácil que es comunicarse desde C#.

En las futuras entradas de esta serie de artículos iremos viendo como ir avanzando un poco más.


🧉 + 🥐🥐 = Happy coder 💻