De Docker Noob a Pro
Hola Fronters, espero que estén excelente!!! En días pasados aprendimos a crear contenedores y a exponerlos mediante el mapeo de puertos. Del mismo modo, también aprendimos a adjuntar volúmenes en nuestros contenedores, generando persistencia de los datos y evitando pérdidas. Ya podemos hacer muchas cosas!!! High Five!!!. Si no lo has visto, puedes verlo aquí
En este capítulo aprenderemos la forma como Docker gestiona las redes y como podemos sacar provecho de esos mecanismos para poder exteriorizar nuestros contenedores, más allá del mapeo de puertos: sus tipos de conexiones y utilidad posible de cada uno de ellos, sin embargo como este tema de la gestión de redes es algo extenso lo cubriremos en varios capítulos.
Les aviso de antemano que hay bastante lectura aquí y si quieren entre subtítulo y subtítulo dejen espacio para un cafecito, porque la idea no es saturarnos, sino entender esto. También trataremos de ser lo más explicativos y simples posibles
Listos??? Primer cafecito… =P
Docker Networking: ¿Qué es y cómo funciona?
Docker Networking es la forma como Docker gestiona las conexiones de redes en los contenedores. No es muy complicado, pero hay que prestarle un poco de atención a cada tipo. Para ello, Docker utiliza controladores, conocidos en inglés como Drivers, que son simplemente instrucciones de código (sub programas o plugins, bien sea nativos de Docker o foráneos ), que indicará al Docker Host, (nuestro Docker Engine Server o DES, como lo hemos descrito en artículos anteriores), si se conectarán y como se conectarán los contenedores que se almacenan en él.
Docker tiene cinco controladores o modos esenciales de conexión de red:
bridge: es el modo de red que Docker utiliza por defecto y lo que hace es crear una red interna con un rango de red que sólo funciona y es reconocible dentro del Docker Host y este contenedor y se utilizan cuando requerimos correr aplicaciones dentro de contenedores independientes, pero deseamos que se comuniquen con otros contenedores y a su vez estar aislados del exterior.
host: Para contenedores independientes que requerimos que sean conectados directamente a la red, bien sea porque deben estar expuestos al exterior con la dirección de la subred física, o bien si requiere una exposición múltiple de puertos. Esto eliminaría el aislamiento de los contenedores.
overlay: nos permite la conexión de múltiples demonios de Docker y permite la conexión entre un grupo de contenedores, el cual se denomina enjambre, en inglés docker swarm service, y un contenedor independiente.
ipvlan y macvlan: ipvlan nos permite el control en el direccionamiento IPv4 e IPv6 mientras que macvlan nos permite asignar una mac address haciendo que aparezca como un dispositivo físico en la red.
none: nos permite deshabilitar todo tipo de conexión de red en nuestro contenedor.
Todos estos controladores o modos de conexión podemos incorporarlos o desincorporarlos en nuestros contenedores en cualquier momento de su ciclo de vida.
En este capítulo nos enfocaremos en las conexiones de red tipo bridge, Dejando para futuras entregas el resto de los tipos de conexiones.
Segundo cafecito…
Conexiones bridge
Ahora vamos a hablar sobre las conexiones de tipo bridge. Existen dos tipos de bridges o conexiones de tipo puente:
default-bridge: La que es establecida por defecto en Docker, es decir, cuando creamos un contenedor por defecto y no especificamos ningún modo de conexión adicional, se seleccionará este por defecto. Puede ser la ideal para iniciar nuestros pasos con Docker y probar el funcionamiento de los contenedores, en una etapa inicial, pero no es lo recomendable en términos de establecer líneas de desarrollo y procesos de deployment sustentables. En definitiva: el workflow no será óptimo ni eficiente en ambientes de producción.
user-defined bridge: estas redes son definidas por el usuario y permiten un mayor control, tanto de aislamiento como de funcionamiento. Una red de tipo bridge permite interconectar a todos los contenedores que se encuentran en ese mismo segmento de red y dentro del mismo Docker Host.
Cuando creamos nuestro contenedor web, al no haber especificado que tipo de conexión de red o driver queríamos implementar, Docker automáticamente implementó un tipo de controlador bridge por defecto para proporcionar una conectividad básica y funcional.
Al listar todas nuestras conexiones de red podremos ver todas las conexiones de red que tenemos disponibles, entre esas una conexión de tipo bridge, la cual se llama tal cual: bridge:
docker network ls

Al inspeccionar la conexión bridge podemos ver características tales como la fecha y hora de creación, su alcance (scope), el Driver que utiliza, su ID numérico, subred y puerta de enlace que utiliza, entre otros datos. También tiene en su configuración los datos del contenedor creado anteriormente (web) y que utiliza sus servicios de conectividad.
docker network inspect bridge

Bien, una de las diferencias entre el default-bridge por defecto y los user-defined-bridges, tal y como lo mencionamos anteriormente, es la capa de aislamiento que proporcionan. Si al momento de crear contenedores no definimos las redes con las cuales trabajarán, todos estarán por defecto en el default-bridge del Docker Host. Esto traerá como consecuencia que todos los contenedores están juntos y esto puede no ser muy bueno o adecuado, en la medida que queramos organizar y aislar a aquellos servicios que no sean necesarios o requeridos que funcionen en la misma subred.
Para crear una nueva conexión de red con el driver bridge, sólo debemos ejecutar el siguiente comando:
docker network create -d bridge nombreconexion
Sustituiremos en donde dice nombreconexion por el nombre que deseemos, en este caso la nombraremos lan0:
docker network create -d bridge lan0
Y luego de crear la red lan0 procedemos a listar nuestras redes:

Inspeccionaremos nuestra red lan0 y veremos sus propiedades:

Creo que vamos bastante bien. Nos tomamos otro café?
Eliminando conexiones de red innecesarias
Ya sabemos crear redes de tipo bridge, de hecho, a modo de práctica puedes crear unas cuantas. Yo creé las redes juanito y pepito. Estas últimas no tienen nombres muy profesionales, pero sirven bien para lo que viene a continuación.
Hemos creado estas dos redes de más, juanito y pepito. No tienen nombres acordes a los estándares de nuestra organización, entonces procedemos a eliminar las redes innecesarias:
docker network rm juanito
docker network rm pepito
O también, podemos eliminarlas en una sola línea, separando cada red por un espacio:

Conectando y desconectando nuestros contenedores a Docker Network
Es hora de conectar nuestros contenedores a nuestras conexiones de red (valga la redundancia!). Para ello analizaremos cada uno de los casos de uso. En el caso de nuevos contenedores, simplemente le indicaremos de forma explícita la red por medio de la que vamos a conectarlo al momento de crearlo.
En este caso crearemos el contenedor que se llamará intranet y lo conectaremos a la red lan0 (se acuerdan?, el que creamos previamente!) y expondremos el puerto 80 del contenedor hacia el 8081 del Docker Host. El comando en cuestión será así:
docker create --name intranet --network lan0 --publish 8081:80 nginx:latest
Quedando de esta forma:

Si listamos nuestros containers, podremos ver intranet, sin embargo no está iniciado, para hacerlo, debemos ejecutar:
docker start intranet
Y al listar nuevamente los contenedores lo veremos corriendo:

Si entramos en nuestro navegador a la dirección IP de nuestro Docker Host (recuerden: la IP puede variar, en este caso es la 192.168.16.60, pero en el caso de ustedes puede ser cualquier otra, incluso localhost) y el puerto que asignamos a nuestro contenedor intranet, el cual era el 8081:

Y si inspeccionamos el contenedor intranet veremos que efectivamente la subred a la cual esta suscrito es diferente a la del contenedor web:


Y esto traerá como consecuencia que ambos contenedores no podrán comunicarse, pues mutuamente no se alcanzan por estar en subredes aisladas entre sí. Entraremos en cualquiera de los dos contenedores, conociendo previamente la dirección IP de cada uno y haremos ping al otro:
docker exec -it intranet bash
Entraremos en el prompt del contenedor y ejecutaremos ping y… No tenemos ping, Por qué??? Te lo cuento ahorita, luego de un café!!!
Intermedio: el problema de las imágenes ligeras
Antes de continuar con lo que explicábamos sobre la comunicación entre contenedores que están en diferentes conexiones de red es que cuando necesitamos cierto tipo de herramientas básicas de debugging o testing (diagnósticos o pruebas), no las tenemos a la mano. Esto se resuelve de forma relativamente fácil, pues sólo basta instalar el paquete que necesitamos para dicha tarea:
apt update
apt install -y iputils-ping
Seguramente necesitaremos otras herramientas básicas de red para verificación o edición de archivos / ficheros, así que las instalaremos. Esto es opcional y se recomienda no sobrecargar el contenedor:
apt install -y nano net-tools ifupdown
Ahora si queremos que estos cambios sean permanentes en nuestro contenedor, tendremos que hacer un commit en nuestro contenedor para escribir dichos cambios (Spoiler… buu!!!):
docker commit -m "Instaladas herramientas basicas" --author "Gabriel W <gabrielw@itfrontech.com>" intranet nginx:latest
Y luego, al verificar el estado de nuestra imagen, podemos constatar que el último cambio en dicha imagen fue hace veinte segundos:

NOTA IMPORTANTE: antes de ejecutar el docker commit (…), debemos asegurarnos de vaciar la caché de los paquetes instalados. No necesitamos esa carga en nuestros contenedores e imágenes, así que antes del commit ejecutemos apt clean / apt-cache clean / yum clean / dnf clean o lo que sea que utilicemos para limpiar caché de paquetes instalados… No necesitamos esa negatividad en nuestras vidas =D
Manipulando conexiones con Docker Network (cont)
Bien, una vez ya pudimos instalar las herramientas necesarias para hacer las pruebas y demás configuraciones pertinentes, al hacer ping hacia el contenedor web, que está en el default bridge, nos muestra que no puede llegarle:

Para solventar eso, podemos agregar un container creado previamente a nuestra user-defined-bridge. En este caso conectaremos nuestro contenedor web a la red lan0:
docker network connect lan0 web
Y al chequear las conexiones con docker inspect web veremos que, efectivamente nuestro contenedor web está ahora conectado a los dos redes bridges: El default bridge del Docker Host, siendo su dirección IP la 172.17.0.2; y por otra parte, el bridge creado por nosotros, lan0, cuya dirección IP es la 172.19.0.3:

¿Qué creen que pasará ahora si intentamos hacer ping a web desde intranet, sabiendo que están en el mismo bridge?, Pues ahora si se verán:

Excelente!!!, Ya podemos hacer que nuestros contenedores se vean y no estén aislados uno del otro. Creo que podemos desconectar a web del default-bridge ahora y que, a partir de ahora, ambos convivan y compartan en la misma red lan0:
docker network disconnect bridge web
Al inspeccionar las conexiones de red de nuestro contenedor web, podemos constatar que solo está conectado a la red lan0:

Interesante, verdad? Hasta aquí por hoy…
Conclusiones y Despedida
El día de hoy, hemos aprendido a crear conexiones de red, modo bridge. También aprendimos a conectarnos, desconectarnos de dichas conexiones y eliminarlas, según nuestra necesidad y voluntad.
Café? Vamos a tomarnos uno y así me cuentas que tal te pareció el artículo. Espero que sean de su agrado porque llevan mucho esfuerzo, investigación y cariño darles este contenido cuya única finalidad es hacer que aprendamos más y de la forma como yo creo que a muchos nos gustaría que nos explicaran. Por favor, deja en los comentarios tus inquietudes y dudas, relacionadas con estos artículos y hagamos de esta una bonita comunidad en la cual todos podamos aprender sin pena ni complejos, pues todos estamos aquí para aprender.
Hasta luego Fronters!!!!