Configurar Kafka Para Tener Separacion De Redes
Kafka con varias NICs, la receta perfecta para sufrir :)
Un dia! un dia entero!
Eso es el tiempo que tuve que dedicar a bucear entre la documentacion de HortonWorks para encontrar una forma de publicar diferentes IPs en diferentes NICs en los nodos del cluster de Kafka y conseguir tener la red de Datos y la de Clientes diferenciada.
Para mas INRI, como esta gestionado con Ambari, no vale cambiar los ficheros de configuracion del Kafka a pelo…hay que hacer cositas.
Dale al boton de ▼ y descubrelo!
El porque
He querido hacer este post antes que la segunda parte porque creo que a mas de uno le va a valer, o eso espero.
Resulta que decidimos poner una red interna de datos para poder conseguir dos cosas:
- Seguridad: tener la red separada fisicamente en sus propias equipos de red evita que te puedan sniffear si se te cuelan en alguna VM
- Velocidad: Tenemos ahora mismo 4 x 1 GBPS en LACP, lo cual es menos que los adaptadores VM del vmware esx (que aunque te pongan 1 gb, suelen llegar a los 10 gpbs), estos son en exclusiva y no compartidos con toda la infraestructura.
Volvamos al tema principal. Nuestro Kafka esta gestionado por Ambari, y aunque a priori suena muy bien por la facilidad que te da en la gestion y despliegue de los mismos, nos estamos dando cuenta que no lo es tanto.
En nuestros entornos de produccion, todos los micro-servicios se comunican con los demas a nivel de mensajes de kafka. Esto vuelve a Kafka el centro de toda la logica de servicios y lo convierte en critico: si se para kafka, Couchbase no puede agregar las transacciones a MariaDB y los clientes no pueden saber cuanto dinero tienen.
Por ello, Kafka ha de ser una especie de servicio inmutable que una vez configurado, se le debe de monitorizar y dejar bastante en paz, asi que tenerlo gestionado por Ambari, donde puedes reiniciarlo sin siquiera estar logado en la maquina, se me antoja como poco, peligroso.
El problema
Resulta que cuando tienes varias NICs, hay que configurar Kafka especificamente para ese menester. Por defecto, Kafka esta pensado para escuchar todo en un interface y ya que usa zookeeper, es este interface (a traves del hostname que registra en zooKeeper) que usa cuando accedas a un topic del cual ese nodo no es el lider, te redirija al que si lo es.
Segun esta guia de HortonWorks, para tener un Hadoop multihomed (que asi le llaman a que tengan mas de una NIC) hay que registrar en cada una de las DNS de las redes donde tienen las NICs, el mismo hostname con diferente IP.
Asi, segun de donde venga la peticion, manda por la red publica (la de clientes) o por la red interna (la de datos)
Peeeero si no tienes DNS en alguna de ellas y usas el /etc/hosts, estas condenado a tener un solo interface:
If /etc/hosts files are used in lieu of DNS, than the above constraint to have the same hostname on all interfaces means that each /etc/hosts file can only specify one interface per server. Thus, clients at least are likely to have different /etc/hosts files then servers, and the allowable choices for use of the different networks may be constrained. If these constraints are not acceptable, provide DNS services.
Esto nos dejaba con solo un interface y asi estaba el archivo hosts por cada maquina:
1 | 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 |
Todo bien hasta este punto, pero ahora es cuando las cosas comienzan a torcerse.
Kafka funciona por el sistema de liderato por defecto: Un nodo es lider de la partition hasta que lo es otro. Y solo ese nodo ofrece esa particion.
Esto te permite que si tienes un topic en varias particiones, puedan tener varios flujos de datos tantos como particiones simultaneamente.
Las cosas ocurren en este orden:
- kafka registra en zooKeeper como broker usando su hostname
- kafka registra en zooKeeper las particiones y los lideres
- un cliente accede a kafka al nodo NO es el lider
- zooKeeper le indica a que nodo debe de ir referenciado por el punto 1
- el cliente accede al nodo que es el lider de la particion
Que pasa cuando kafka pasa las direcciones internas a zooKeeper como forma de acceso a los lideres de las particiones? Pues que tienes un cliente intentando acceder una red que no existe y te empieza a escupir toda clase de errores, desde que no encuentra la metatada para el topic hasta que el topic no tiene lider.
Haciendo que Kafka escuche donde debe de escuchar
Vale, tenemos ya el problema, ahora: como lo solucionamos?
En un primer momento pense en crear un fichero de variables de entorno que las leyera Ambari y los ejecutara. De manera, que $[variable] seria igual a un valor en cada maquina que seria donde iba a escuchar el kafka para los clientes…pues no. No funciona.
Resulta que ambari no lee ni ejecuta los ficheros de variables de entorno, la unica personalizacion que te permite, son variables Jinja2 > {{ variable }} al estilo que usa Ansible.
Osea que te permite usar esa variable para meter variables (valga la rebuznancia) pero no te deja leer variables de entorno.
Solucion? ninguna. Por el momento no hemos encontrado ninguna excepto cambiar los ficheros de server.properties a mano. Horrible.
Como esa solucion es una autentica porqueria, vamos a sacar los Kafka de Ambari y los vamos a dejar corriendo solos sin mas gestor que los ficheros de configuracion que podamos tocar.
Mientras, hemos cambiado los permisos del fichero y realizado una copia de seguridad, para que aunque quiera, no pueda cambiarlos. Otra vez, horrible.
Si sabeis alguna manera de hacerlo, os ruego que lo dejeis en los comentarios. Os estare enternamente agradecido
Pero vamos a suponer que la solucion os gusta y lo que quereis es saber como demonios echarlo a andar.
editamos server.properties
1 | advertised.listeners=PLAINTEXT://10.25.12.110:6667 |
advertised.listener: la ip / fqdn donde tiene que conectar los clientes
inter.broker.listener.name: el protocolo y la ip donde se conectan los broker entre si.
listeners: los interfaces donde kafka va a estar escuchando
Aqui hay una cosa que es super interesante: en los advertised.listeners PLAINTEXT os permite definir un protocolo por puerto e interface. Con lo cual, podeis poner SSL://IP-publica y dejarlo listo solo para tener SSL ahi. Mientras que en el interbroker, lo teneis en PLAINTEXT. Cuando salgamos de la PoC, es lo haremos.
Service restart y listo!
Si, llegamos al final.
Haceis un /bin/kafka stop; /bin/kafka start y si todo ha ido bien, podeis ver algo parecido a esto al hacer un netstat -putona (me encanta!)
1 | tcp6 0 0 :::6667 :::* LISTEN 26146/java off (0.00/0/0) |
Conclusion
Ser novato se paga y tenerme que pegar con esto sin haberlo tocado nunca ha sido duro. No obstante, es algo que mola mucho y una vez que lo tienes funcionando, el tener el kafka escuchando en varios interfaces de la manera que quieres, te permite un control y una seguridad que es maravillosa.
Bonus
No sera en este post, pero en el siguiente, os voy a contar como estamos haciendo para replicar mensajes de Kafka entre DCs usando el KafkaMirror y el Kafka-Connect para meter esos datos en Hive. Ademas, de como renombrar los topics de kafka en transito hacia el data lake.
Adios!
[1]Kafka server configuration - listeners vs. advertised.listeners
(https://stackoverflow.com/questions/42998859/kafka-server-configuration-listeners-vs-advertised-listeners)
[2]KIP-103: Separation of Internal and External traffic
(https://cwiki.apache.org/confluence/display/KAFKA/KIP-103%3A+Separation+of+Internal+and+External+traffic)