Introducción a las redes en docker. Enlazando contenedores docker

6 minute read

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 es 172.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 estaría en la misma red que el host (por lo tanto toma direccionamiento del servidor DHCP de nuestra red). Además los puerto son accesibles directamente desde el host. 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.

docker

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/

Updated:

Comments