Hola de nuevo, en el último post de esta serie hablamos un poco del acceso a la red de los contenedores y de como conseguir permanencia de datos usando volúmenes.
En este post vamos a profundizar un poco más en las caraterísticas de red de los contenedores para poder comunicarlos entre ellos, además de explorar otras opciones y subcomandos para su uso con nuestros contenedores.
Para entender un poco más como funciona el subsistema de red asociado a Docker, vamos a ver que configuración de red tenemos en nuestro sistema de pruebas.
|
Interfaz virtual docker0. |
Como vemos, hay un interfaz de red con una dirección IP asociada al demonio Docker la cual está relacionada con la red de cada uno de los contenedores que creamos, así como ya hicimos en un post anterior, si comprobamos la dirección IP de nuestros contenedores veremos algo como lo siguiente:
|
Interfaz de red de un contenedor. |
Nuestro contenedor webserver dispone de un interfaz en la misma red que el interfaz virtual docker0 de nuestro host. Como es lógico, cualquier nuevo contenedor con acceso a la red tendrá un interfaz con un direccionamiento similar, por ejemplo:
|
Red usada por interfaces de red en contenedores Docker. |
Y estas interfaces de red ¿como se relacionan con nuestro host?, pues comprobando de nuevo el estado de la red con dos contenedores corriendo vemos que han aparecido dos nuevas interfaces de red virtuales y lo que es más importante, que se ha creado un bridge:
|
Interfaces de red virtuales en el host ejecutando Docker. |
|
Bridge creado en el host ejecutando Docker. |
Sin entrar en mucho detalle sobre lo que es un bridge, está claro que el funcionamiento básico de Docekr con la red, consiste en crear un bridge en el host al cual se conectan los interfaces de red de todos nuestros contenedores. Como ya vimos, gracias a esto, los contenedores que creamos tienen acceso al exterior por defecto pero no se puede acceder a ellos desde el exterior de nuestro host.
Al tratarse de un bridge, los contenedores tendrán acceso por red entre ellos pero con ese direccionamiento la pregunta obvia es, ¿como fijamos que puedan comunicarse correctamente entre ellos si no podemos asegurar su direccionamiento?
Para esto disponemos de una opción que nos permitirá enlazar un contenedor con otro ya existente en el momento de su creación. Básicamente lo que hacemos es crear un contenedor y enlazarlo con otro que ya se encuentra corriendo en nuestro host. Como siempre, para entenderlo mejor, supongamos que vamos crear un servicio web formado por un servidor de base de datos, por ejemplo una base de datos MariaDB, un servidor web Apache y una estación de gestión de la base de datos.
Con esta descripción de nuestro servicio, lo primero que debemos pensar es que vamos a necesitar tres contenedores y que tanto nuestro servidor web como nuestra estación de gestión de la base de datos, dependerán del contenedor en el que se ejecute el motor de base de datos. Con este pequeño análisis ya hemos determinado un orden de arranque, por tanto primero creamos nuestro contenedor de base de datos:
|
Creamos un contenedor con una base de datos. |
Si comprobamos si nuestro contenedor con el motor de base de datos está corriendo, nos daremos cuenta que no es así:
|
Error en el arranque del contenedor webDB. |
En estos casos, para poder comprobar que ha sucedido y porque no ha arrancado correctamente, podemos usar el subcomando logs para que nos muestre el contenido de STDERR del contenedor y así poder determinar la causa del problema. En el caso de un contenedor MariaDB tenemos el siguiente mensaje:
|
Salida de logs de un contenedor MySQL. |
|
|
Este mensaje nos indica que para poder inicializar correctamente la base de datos, es necesario que le proporcionemos la password de root a través de alguna de las variables de entorno que indica en la salida, pero ¿como podemos pasarle variables de entorno a un contenedor? Para esto solo necesitamos usar la opción -e cuando creamos nuestro contenedor. Por tanto, si ahora volvemos a crear el contenedor de base de datos y especificamos la password de root como nos indica el log, tendremos lo siguiente:
|
Creación de contenedor MariaDB con variables de entorno. |
Ya tenemos el primer contenedor de nuestro servicio web, ahora ya podemos crear los otros dos contenedores en el orden que queramos ya que solo dependen de este. Para crear el servidor web que realizará consultas a la base de datos ejecutaremos el sigueinte comando, en el cual ya establecemos el enlace entre ambos:
|
Creación de un contenedor enlazado con otro. |
Como podemos ver en el comando hemos especificado la opción --link webDB:DB con lo que estamos diciendo que hay un enlace con el contenedor llamado webDB y el alias del mismo lo llamamos DB. La pregunta es, ¿como se establece esta relación entre ambos contenedores? ¿donde se guarda la información de este enalce? lo que sucede al usar esta opción es que Docker modifica el fichero /etc/hosts del contenedor webserver2 que acabamos de crear y añade uan entrada como la siguiente:
|
Fichero /etc/hosts del contendor Apache dependiente de base de datos. |
De este modo, cuando desarrollemos nuestra aplciación web, cualquier operación que realicemos a la base de datos en el host DB, se realizará sobre el contenedor con la base de datos MySQL.
Ahora creamos un contenedor que usaremos para administrar la base de datos de forma remota, así que por ejemplo podemos crear un contenedor a partir de una imagen de CentOS, instalar el cliente de mariaDB y conectarnos a nuestra base de datos:
|
Contenedor para administrar la base de datos. |
Con el cliente de administración de base de datos instalado en este contenedor, podremos conectarnos a la base de datos corriendo en el contenedor webDB y administrar la base de datos:
|
Conexión del contenedor de administración con el contenedor de base de datos. |
Por tanto vemos que gracias a la opción --link usada en el momento de crear los contenedores, podemos enlazar unos con otros de manera muy sencilla y solventar así el problema que puede suponer la asignación dinámica de direcciones IP a los contenedores para la creación de nuestros servicios.
Como es lógico, estas configuraciones crean relaciones de dependencia entre los contenedores que provocan que, si intentamos arrancar un contenedor que se enlaza con otro que está parado, recibiremos un error como el siguiente:
|
Error arrancando un contenedor enlazado a uno parado. |
Es muy importante entender esta funcionalidad dentro del concepto de creación de servicios complejos, en los cuales varios contenedores relacionados entre sí nos permitirán proporcionar un servicio el cual se definirá a partir de los elementos que lo forman. Para esto usaremos otras herramientas de Docker como docker-compose, lo cual veremos más adelante.
Hasta aquí hemos ampliado un poco más el uso de la red y hemos visto como enlazar contenedores entre sí para crear servicios un poco más complejos, en las siguientes entradas ampliaremos más la idea de servicio y como crearlos.