Introducción a las redes en docker. Enlazando contenedores docker
Hace unos año escribí una entrada en este blog titulada: Enlazando contenedores docker donde hacía una primera aproximación al mecanismo que nos ofrece docker de que varios contenedores sean accesibles entre ellos por medio de su nombre (resolución de nombres). Este mecanismo funciona de manera distinta según la red docker donde estén conectados los contenedores. En este artículo vamos a introducir los distintos tipos de redes que nos ofrece docker y los distintos métodos de asociación o enlazado entre contenedores que tenemos a nuestra disposición.
Introducción a las redes en docker
Cuando instalamos docker tenemos las siguientes redes predefinidas:
# docker network ls
NETWORK ID NAME DRIVER SCOPE
ec77cfd20583 bridge bridge local
69bb21378df5 host host local
089cc966eaeb none null local
- Por defecto los contenedores que creamos se conectan a la red de tipo bridge llamada
bridge
(por defecto el direccionamiento de esta red es172.17.0.0/16
). Los contenedores conectados a esta red que quieren exponer algún puerto al exterior tienen que usar la opción-p
para mapear puertos. -
Si conecto un contenedor a la red host, el contenedor será accesible usando la misma IP que tu máquina. Por ejemplo:
# docker run -d --name mi_servidor --network host josedom24/aplicacionweb:v1 # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 135c742af1ff josedom24/aplicacionweb:v1 "/usr/sbin/apache2ct…" 3 seconds ago Up 2 seconds mi_servidor
Podemos acceder directamente al puerto 80 del servidor para ver la página web.
- La red
none
no configurará ninguna IP para el contenedor y no tiene acceso a la red externa ni a otros contenedores. Tiene la dirección loopback y se puede usar para ejecutar trabajos por lotes.
Nosotros podemos crear nuevas redes (redes definidas por el usuario), por ejemplo para crear una red de tipo bridge:
# docker network create mired
Y para ver las características de esta nueva red, podemos ejecutar:
# docker network inspect mired
Para crear un contenedor en esta red, ejecutamos:
# docker run -d --name mi_servidor --network mired -p 80:80 josedom24/aplicacionweb:v1
Dependiendo de la red que estemos usando (la red puente por defecto o una red definida por el usuario) el mecanismo de enlace entre contenedores será distinto.
Enlazando contenedores conectados a la red bridge por defecto
Esta manera en enlazar contenedores no está recomendada y esta obsoleta. Además el uso de contenedores conectados a la red por defecto no está recomendado en entornos de producción. Para realizar este tipo de enlace vamos a usar el flag --link
:
Veamos un ejemplo, primero creamos un contenedor de mariadb:
# docker run -d --name servidor_mysql -e MYSQL_DATABASE=bd_wp -e MYSQL_USER=user_wp -e MYSQL_PASSWORD=asdasd -e MYSQL_ROOT_PASSWORD=asdasd mariadb
A continuación vamos a crear un nuevo contenedor, enlazado con el contenedor anterior:
# docker run -d --name servidor --link servidor_mysql:mariadb josedom24/aplicacionweb:v1
Para realizar la asociación entre contenedores hemos utilizado el parámetro --link
, donde se indica el nombre del contenedor enlazado y un alias por el que nos podemos referir a él.
En este tipo de enlace tenemos dos características:
-
Se comparten las variables de entorno
Las variables de entorno del primer contenedor son accesibles desde el segundo contenedor. Por cada asociación de contenedores, docker crea una serie de variables de entorno, en este caso, en el contenedor
servidor
, se crearán las siguientes variables, donde se utiliza el nombre del alias indicada en el parámetro--link
:# docker exec servidor env ... MARIADB_PORT=tcp://172.17.0.2:3306 MARIADB_PORT_3306_TCP=tcp://172.17.0.2:3306 MARIADB_PORT_3306_TCP_ADDR=172.17.0.2 MARIADB_PORT_3306_TCP_PORT=3306 MARIADB_PORT_3306_TCP_PROTO=tcp MARIADB_NAME=/servidor/mariadb MARIADB_ENV_MYSQL_USER=user_wp MARIADB_ENV_MYSQL_PASSWORD=asdasd MARIADB_ENV_MYSQL_ROOT_PASSWORD=asdasd MARIADB_ENV_MYSQL_DATABASE=bd_wp MARIADB_ENV_GOSU_VERSION=1.10 MARIADB_ENV_GPG_KEYS=177F4010FE56CA3336300305F1656F24C74CD1D8 MARIADB_ENV_MARIADB_MAJOR=10.4 MARIADB_ENV_MARIADB_VERSION=1:10.4.11+maria~bionic ...
-
Los contenedores son conocido por resolución estática
Otro mecanismo que se realiza para permitir la comunicación entre contenedores asociados es modificar el fichero
/etc/hosts
para que tengamos resolución estática entre ellos. Podemos comprobarlo:# docker exec servidor cat /etc/hosts ... 172.17.0.2 mariadb c76089892798 servidor_mysql
Enlazando contenedores conectados a una red definida por el usuario
En este caso vamos a definir una red de tipo bridge:
# docker network create mired
Y creamos los contenedores conectados a dicha red:
# docker run -d --name servidor_mysql --network mired -e MYSQL_DATABASE=bd_wp -e MYSQL_USER=user_wp -e MYSQL_PASSWORD=asdasd -e MYSQL_ROOT_PASSWORD=asdasd mariadb
# docker run -d --name servidor --network mired josedom24/aplicacionweb:v1
En este caso no se comparten las variables de entorno, y la resolución de nombres de los contenedores se hace mediante un servidor dns que se ha creado en el gateway de la red que hemos creado:
# docker exec -it servidor bash
root@a86f6d758eba:/# apt update && apt install dnsutils -y
...
root@a86f6d758eba:/# dig servidor_mysql
...
; ANSWER SECTION:
servidor_mysql. 600 IN A 172.20.0.2
...
;; SERVER: 127.0.0.11#53(127.0.0.11)
root@a86f6d758eba:/# dig servidor_mysql.mired
...
; ANSWER SECTION:
servidor_mysql.mired. 600 IN A 172.20.0.2
root@a86f6d758eba:/# dig servidor
...
; ANSWER SECTION:
servidor. 600 IN A 172.20.0.3
Como vemos desde un contenedor se pueden resolver tanto los nombres de los servidores, como el FHQN formado por el nombre del contenedor y como nombre de dominio el nombre de la red a la que están conectados.
Instalación de wordpress en docker
Veamos un ejemplo, vamos a instalar wordpress usando dos contenedores enlazados: uno con la base de datos mariadb y otro con la aplicación wordpress.
Creamos una red de tipo bridge:
# docker network create red_wp
Creamos un contenedor desde la imagen mariadb con el nombre servidor_mysql
, conectada a la red creada:
docker run -d --name servidor_mysql --network red_wp -e MYSQL_DATABASE=bd_wp -e MYSQL_USER=user_wp -e MYSQL_PASSWORD=asdasd -e MYSQL_ROOT_PASSWORD=asdasd mariadb
A continuación vamos a crear un nuevo contenedor, con el nombre servidor_wp
, con el servidor web a partir de la imagen wordpress, conectada a la misma red y con las variables de entorno necesarias:
docker run -d --name servidor_wp --network red_wp -e WORDPRESS_DB_HOST=servidor_mysql -e WORDPRESS_DB_USER=user_wp -e WORDPRESS_DB_PASSWORD=asdasd -e WORDPRESS_DB_NAME=bd_wp -p 80:80 wordpress
La variable de entorno del contenedor wordpress WORDPRESS_DB_HOST
la hemos inicializado con el nombre del contenedor de la base de datos, ya que como hemos explicado anteriormente, al estar conectado a la misma red los dos contenedores, este nombre se podrá resolver. Podemos acceder a la ip del servidor docker y comprobar la instalación de wordpress.
Enlazando contenedores con docker-compose
Cuando trabajamos con escenarios donde necesitamos correr varios contenedores podemos utilizar docker-compose para gestionarlos. En el fichero docker-compose.yml
vamos a definir el escenario. El programa docker-compose
se debe ejecutar en el directorio donde este ese fichero. Por ejemplo para la ejecución de wordpress persistente podríamos tener un fichero con el siguiente contenido:
version: '3.1'
services:
wordpress:
container_name: servidor_wp
image: wordpress
restart: always
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: user_wp
WORDPRESS_DB_PASSWORD: asdasd
WORDPRESS_DB_NAME: bd_wp
ports:
- 80:80
volumes:
- /opt/wordpress:/var/www/html/wp-content
db:
container_name: servidor_mysql
image: mariadb
restart: always
environment:
MYSQL_DATABASE: bd_wp
MYSQL_USER: user_wp
MYSQL_PASSWORD: asdasd
MYSQL_ROOT_PASSWORD: asdasd
volumes:
- /opt/mysql_wp:/var/lib/mysql
Cuando creamos un escenario con docker-compose
se crea una nueva red definida por el usuario docker donde se conectan los contenedores, por lo tanto están enlazados, pero no comparten las variables de entorno (por esta razón hemos creado las variables de entorno al definir el contenedor de wordpress). Además tenemos resolución por dns que resuelve tanto el nombre del contendor (por ejemplo, servidor_mysql
) como el alias (por ejemplo, db
).
Para crear el escenario:
# docker-compose up -d
Creating network "dc_default" with the default driver
Creating servidor_wp ... done
Creating servidor_mysql ... done
La primera imagen de este artículo está tomada de la siguiente página web: https://www.nuagenetworks.net/blog/docker-networking-overview/
Comments
Andrés Gorostidi Pulgar
Tengo una duda. Imaginemos que yo tenga un Centos con varias IPs, y que estoy corriendo varios dockers. En principio, cuando haga el port mapping al levantar el docker acceder por la IP principal al servicio que me brinde el docker (imaginemos un apache, corriendo en un docker, en el puerto 80). Y si ahora quiere levantar otro docker, corriendo en el mismo puerto, usando otra de las IPs del host ? Como le digo que usa una IP diferente del host y no la misma ? El proposito es tener en la misma maquina ambos dockers, ambos escuchando en el mismo puerto, con IPs diferentes…
Gracias!
José Domingo Muñoz
Hola Andrés, podrías ejecutar los siguientes comandos para ejecutar dos servidores web (he usado nginx en la prueba) y que cada uno responda en el puerto 80 de cada una de las direcciones ip que tiene la máquina:
Y comprobamos que están corriendo los dos contenedores:
Prueba a entrar en las distintas ip y verás que tiene acceso a cada uno de los servidores web. Un saludo
Emil
Hola. Estoy recién empezando con Docker y tengo una duda. En una maquina virtual tengo dos SO, Ubuntu Desktop y Ubuntu Server. En los dos sistemas he configurado una red interna para que se puedan comunicar. En Ubuntu Server, he instalado docker y mediante docker-compose he creado una imagen mysql, que en el archivo docker-compose.yml he puesto “ports: 3306:3306”, con la idea desde Ubuntu Desktop acceder a información al servidor. Todo parece estar bien, incluso cuando hago “sudo docker ps”, me aparece mysql funcionando y con el port 3306 configurado. Pero, desde el host (Ubuntu Desktop) intento verificar el funcionamiento, escribiendo en la terminal “sudo nmap -p- –open -n" o "nc -z4nv 3306", no funciona. El primer comando me devuelve el port 22 en lugar de 3306, y el segundo comando si escribo 22, si me reconoce la ip_guest. Cualquier ayuda me seria útil.
Gracias
José Domingo Muñoz
Hola Emil,
Intenta acceder desde el ubuntu Desktop al servidor de base de datos. Para ello, instala el cliente de mysql y ejecuta:
Un saludo, espero que te sirva.
Gleiver Fndez. Harria.
Hola José, leyendo tu artículo me surge una duda. Como puedo mediante una archivo docker-compose.yml especificar que un servicio dentro de un determinado contenedor se emita por una determinada interfaz de red determinada asumiendo que en el host anfitrión tenga 2.el escenario que quiero interpretar en un archivo .yml de docker-compose es el siguiente: docker run –detach –volume mi-data:/data –network host –name X –restart unless-stopped imagen-servicio etho. El network lo puedo modificar y declarar directamente en el yml como un bridge pero la interfaz eth0 del host anfitrión como lo declaró??
José Domingo Muñoz
Hola Gleiver, He estado buscando por internet y por la documentación de docker y yo no he encontrado ninguna solución a tu problema.
Entiendo que cuando usas el tipo de red host en docker el contenedor está ofreciendo el servicio en el mismo host, y por lo tanto ofrece el servicio en todas las interfaces de red de la máquina.
Una posible solución que se me ocurre, y que no se si es válida para tu caso es usar el cortafuego para restringir el acceso al servicio desde una determina interfaz.
Siento no poder ayudar más. Un saludo
Leave a Comment
Your email address will not be published. Required fields are marked *