lunes, 1 de julio de 2024

Kubernetes - Redes y PODs I

Uno de los aspectos que, al menos a mi, me resulta más confuso cuando hablamos de Kubernetes es como funciona la red. Hay diferentes conceptos y abstracciones involucradas que se relacionan entre si, para permitir que los PODs se comuniquen entre ellos y con el exterior del cluster.

Empezando por lo básico, recordemos que un POD es una abstracción de uno o más contenedores que se ejecutan en el mismo host, compartiendo recursos como volúmenes y, para el caso que nos ocupa, la pila de red. Este punto es muy importante porque, si todos los contenedores que forman parte de un POD comparten la pila de red, pueden comunicarse entre ellos accediendo a localhost.

Comencemos por lo más simple, que consiste en que un POD solo encapsule un contenedor. En este caso, vamos a intentar explicarlo a partir de la imagen siguiente:

Un contenedor encapsulado en un POD.

El host donde se ejecuta el motor de contenedores tiene un interfaz de red físico eth0, al cual se conecta el bridge que crea dicho motor de contenedores. Este bridge, dependiendo de nuestra configuración, tendrá una dirección IP la cual, y es esto es muy importante, será el gateway de cualquier contenedor que creemos. A este bridge, se conectará el interfaz de red virtual veth del contenedor en el momento de su creación, siendo la dirección IP asignada a dicho interfaz veth, del mismo rango de red que el del bridge.

Podemos ver lo anterior con solo ejecutar el comando ip addr show en nuestro host físico:

Red en el host.

Como vemos en la imagen anterior, además del interfaz físico y de loopback, aparece el bridge cni0 creado por el motor de contenedores.

Ahora, podemos crear manualmente un POD y asignar un contenedor a dicho POD, para lo cual podemos hacer algo como lo siguiente:

Creación de un contenedor dentro de un POD.

En el momento de crear el POD, un interfaz veth aparece en el sistema y al inspeccionar el POD creado con los comandos anteriores, podemos ver la dirección IP asignada al mismo:

Dirección IP asignada al POD.

Con los comandos anteriores, hemos creado un POD y a continuación, hemos creado y arrancado un contenedor empleando una imagen simple de busybox. Al ejecutar el comando /bin/sh en dicho contenedor, podemos comprobar la dirección IP asignada al mismo:

Interfaz de red en el contendor.

Como vemos, la dirección IP es la misma, es decir, la dirección IP asignada al POD es la que se ha asignado al contenedor encapsulado por dicho POD.

En el caso de crear un segundo contenedor, la situación que tendríamos sería muy similar a la siguiente:

Dos contenedores en PODs separados.

De forma similar a como ocurría en el primer caso, el nuevo contenedor tiene un interfaz de red virtual al que se asigna una dirección IP del mismo rango y, si los contenedores tienen alguna manera de descubrimiento, podrán comunicarse entre ellos a través del bridge.

En este segundo caso, en el momento de crear el nuevo POD, podemos comprobar que un nuevo interfaz virtual veth aparece en nuestro host físico:

 

Creación de un segundo POD.

Repitiendo el proceso anterior y creando otro contenedor, encapsulado por este nuevo POD, podemos comprobar que, de nuevo, la dirreción IP del segundo contenedor es la misma que la del POD que lo encapsula:

Configuración de red del segundo contenedor.

Si ambos contenedores necesitan comunicarse entre ellos, podemos ver que esta comunicación funciona sin problema y se realiza a través del bridge cni0.

Los interfaces virtuales veth correspondientes a cada POD, desaparecerán del host en el momento en el que destruyamos los PODs.

Por tanto, hasta aquí tenemos el caso más sencillo usando contenedores pero, como hemos comentado, en Kubernetes un POD puede contener más de un contenedor y es en ese caso cuando todos comparten la pila de red. Por tanto, la imagen anterior, quedaría del siguiente modo:

Dos contenedores encapsulados por el mismo POD.

Para crear un POD y arrancar dos contenedores dentro del mismo, haríamos algo como lo siguiente:

Creación de dos contenedores encapsulados en el mismo POD.

Podemos observar en la salida anterior que ambos contenedores forman parte del mismo POD, ya que la columna POD ID muestra el mismo identificador para ambos. Además, como esperábamos, ahora solo hay un interfaz veth presente en el host:

Interfaces de red en el host.

Al comprobar la red en cada uno de los contenedores, podemos observar que ambos contenedores comparten la misma dirección IP:

Dirección IP en un contendor.....

.... y en el otro.

Es importante recordar que, como indica la documentación de Kubernetes, lo normal es que un POD solo encapsule un contenedor, siendo el caso de varios contenedores "dentro" de un mismo POD un caso de uso avanzado y solo recomendado cuando estos tienen una fuerte dependencia entre ellos.

Pensemos que, al fin y al cabo, lo que está haciendo Kubernetes es controlar al motor de contenedores para indicarle que, al crear el nuevo contenedor dentro del POD, no se cree un nuevo interfaz virtual, sino que utilice el ya existente. Por tanto, en este caso, ambos contenedores tienen un único interfaz de red común compartiendo la pila de red. Como es lógico, esto tiene las siguientes implicaciones:

  • Con la configuración adecuada, ambos contenedores son accesibles desde el exterior.
  • Al compartir la dirección IP, no pueden abrir el mismo puerto a la vez.

Ahora, podemos comprobar que efectivamente ambos contenedores pueden comunicarse entre si usando localhost como dirección haciendo algo como lo siguiente:

Arrancamos el servidor telnet en uno de los contenedores.

Ahora, podemos conectarnos al otro contenedor, usando crictl exec y ejecutar el comando telnet 127.0.0.1 para establecer una conexión contra el otro contenedor:

Conexión telnet con localhost.

Como podemos ver, nos podemos conectar de un contenedor a otro usando directamente localhost ya que, al compartir la pila de red, las redes de ambos contenedores son la misma.

¿Como implementa esto Kubernetes? mediante la ejecución de un contenedor adicional especial cuyo único objetivo es proporcionar la red al resto de contenedores del POD. Este contenedor, que es una instancia de la imagen pause, es el corazón del POD permitiendo la comunicación por red del contenedor o contenedores que estén encapsulados por el POD, ya sea entre ellos o con el exterior del cluster.

Hasta aquí tenemos un resumen breve de como funciona la red entre PODs o contenedores que se encuentran en el mismo host, pero como sabemos, un cluster de Kubernetes estará compuesto por varios nodos y los diferentes PODs deben comunicarse entre ellos estén o no en el mismo nodo del cluster.

En la siguiente entrada, veremos como funciona la red entre PODs cuando hablamos de un cluster y, por tanto, de PODs en diferentes nodos del cluster.