Compartir comentarios
Las respuestas se generan en base a la documentación.

Enlaces de contenedores heredados

Warning

El flag --link es una característica heredada (legacy) de Docker. Es posible que eventualmente se elimine. A menos que necesites obligatoriamente seguir utilizándolo, te recomendamos que utilices redes definidas por el usuario para facilitar la comunicación entre dos contenedores en lugar de usar --link. Una característica que no admiten las redes definidas por el usuario y que sí puedes hacer con --link es compartir variables de entorno entre contenedores. Sin embargo, puedes utilizar otros mecanismos como los volúmenes para compartir variables de entorno entre contenedores de una forma más controlada.

Consulta Diferencias entre los bridges definidos por el usuario y el bridge predeterminado para conocer algunas alternativas al uso de --link.

La información de esta sección explica los enlaces de contenedores heredados dentro de la red bridge predeterminada de Docker, la cual se crea automáticamente al instalar Docker.

Antes de la característica de redes de Docker, podías utilizar la característica de enlace de Docker para permitir que los contenedores se descubrieran entre sí y transfirieran información de un contenedor a otro de forma segura. Con la introducción de la característica de redes de Docker, aún puedes crear enlaces, pero estos se comportan de manera diferente entre la red bridge predeterminada y las redes definidas por el usuario.

Esta sección analiza brevemente la conexión a través de un puerto de red y luego detalla la vinculación de contenedores en la red bridge predeterminada.

Conectar mediante mapeo de puertos de red

Supongamos que has utilizado este comando para ejecutar una aplicación sencilla de Flask en Python:

$ docker run -d -P training/webapp python app.py

Al crear ese contenedor, se utilizó el flag -P para mapear automáticamente cualquier puerto de red interno a un puerto alto aleatorio dentro de un rango de puertos efímeros en tu host de Docker. A continuación, al ejecutar docker ps, pudiste ver que el puerto 5000 del contenedor estaba vinculado al puerto 49155 del host.

$ docker ps nostalgic_morse

CONTAINER ID  IMAGE                   COMMAND       CREATED        STATUS        PORTS                    NAMES
bc533791f3f5  training/webapp:latest  python app.py 5 seconds ago  Up 2 seconds  0.0.0.0:49155->5000/tcp  nostalgic_morse

También viste cómo puedes vincular los puertos de un contenedor a un puerto específico utilizando el flag -p. Aquí, el puerto 80 del host se mapea al puerto 5000 del contenedor:

$ docker run -d -p 80:5000 training/webapp python app.py

Y pudiste ver por qué esto no es una gran idea, ya que te limita a tener un solo contenedor en ese puerto específico.

En su lugar, puedes especificar un rango de puertos del host para vincular el puerto de un contenedor que sea diferente del rango de puertos efímeros predeterminado:

$ docker run -d -p 8000-9000:5000 training/webapp python app.py

Esto vincularía el puerto 5000 del contenedor a un puerto disponible aleatoriamente entre el 8000 y el 9000 en el host.

También hay otras formas de configurar el flag -p. De forma predeterminada, el flag -p vincula el puerto especificado a todas las interfaces de la máquina host. Sin embargo, también puedes especificar una vinculación a una interfaz concreta, por ejemplo, únicamente a localhost.

$ docker run -d -p 127.0.0.1:80:5000 training/webapp python app.py

Esto vincularía el puerto 5000 dentro del contenedor al puerto 80 en la interfaz localhost o 127.0.0.1 de la máquina host.

O bien, para vincular el puerto 5000 del contenedor a un puerto dinámico pero solo en localhost, puedes usar:

$ docker run -d -p 127.0.0.1::5000 training/webapp python app.py

También puedes vincular puertos UDP y SCTP (utilizados habitualmente por protocolos de telecomunicaciones como SIGTRAN, Diameter y S1AP/X2AP) añadiendo un sufijo /udp o /sctp. Por ejemplo:

$ docker run -d -p 127.0.0.1:80:5000/udp training/webapp python app.py

También conociste el útil acceso directo docker port, el cual muestra las vinculaciones de puertos actuales. Esto también resulta de utilidad para mostrar configuraciones de puertos específicas. Por ejemplo, si has vinculado el puerto del contenedor to localhost en la máquina host, la salida de docker port lo reflejará.

$ docker port nostalgic_morse 5000

127.0.0.1:49155
Note

El flag -p se puede utilizar varias veces para configurar múltiples puertos.

Conectar con el sistema de enlaces

Note

Esta sección cubre la característica de enlaces heredada en la red bridge predeterminada. Consulta Diferencias entre los bridges definidos por el usuario y el bridge predeterminado para obtener más información sobre los enlaces en redes definidas por el usuario.

El mapeo de puertos de red no es la única forma en que los contenedores de Docker pueden conectarse entre sí. Docker también dispone de un sistema de enlaces (linking) que te permite vincular varios contenedores entre sí y enviar información de conexión de uno a otro. Al vincular los contenedores, la información sobre el contenedor de origen se puede enviar al contenedor de destino. Esto permite que el receptor vea datos seleccionados que describen aspectos del contenedor de origen.

La importancia de los nombres

Para establecer enlaces, Docker se basa en los nombres de tus contenedores. Ya has visto que cada contenedor que creas tiene un nombre generado automáticamente; de hecho, te has familiarizado con nuestro viejo amigo nostalgic_morse a lo largo de esta guía. También puedes nombrar los contenedores tú mismo. Esta nomenclatura cumple dos funciones útiles:

  1. Puede ser útil nombrar los contenedores que realizan funciones específicas de manera que te resulte más fácil recordarlos, por ejemplo, nombrar web a un contenedor que contenga una aplicación web.
  2. Proporciona a Docker un punto de referencia que le permite referirse a otros contenedores, por ejemplo, puedes especificar que deseas vincular el contenedor web al contenedor db.

Puedes nombrar tu contenedor utilizando el flag --name, por ejemplo:

$ docker run -d -P --name web training/webapp python app.py

Esto inicia un nuevo contenedor y utiliza el flag --name para nombrar el contenedor como web. Puedes ver el nombre del contenedor utilizando el comando docker ps.

$ docker ps -l

CONTAINER ID  IMAGE                  COMMAND        CREATED       STATUS       PORTS                    NAMES
aed84ee21bde  training/webapp:latest python app.py  12 hours ago  Up 2 seconds  0.0.0.0:49154->5000/tcp  web

También puedes utilizar docker inspect para obtener el nombre del contenedor.

Note

Los nombres de los contenedores deben ser únicos. Esto significa que solo puedes llamar a un contenedor web. Si deseas reutilizar el nombre de un contenedor, debes eliminar el contenedor antiguo (con docker container rm) antes de poder crear un contenedor nuevo con el mismo nombre. Como alternativa, puedes utilizar el flag --rm con el comando docker run. Esto elimina el contenedor inmediatamente después de detenerse.

Comunicación a través de enlaces

Los enlaces permiten que los contenedores se descubran entre sí y transfieran información de un contenedor a otro de forma segura. Al configurar un enlace, creas un canal entre un contenedor de origen y un contenedor de destino. El destinatario puede entonces acceder a datos seleccionados del origen. Para crear un enlace, utiliza el flag --link. Primero, crea un nuevo contenedor, esta vez con una base de datos.

$ docker run -d --name db training/postgres

Esto crea un nuevo contenedor llamado db a partir de la imagen training/postgres, que contiene una base de datos PostgreSQL.

Ahora, debes eliminar el contenedor web que creaste anteriormente para poder reemplazarlo con uno vinculado:

$ docker container rm -f web

Ahora, crea un nuevo contenedor web y vincúlalo con tu contenedor db.

$ docker run -d -P --name web --link db:db training/webapp python app.py

Esto vincula el nuevo contenedor web con el contenedor db que creaste anteriormente. El flag --link toma la forma:

--link <nombre o id>:alias

Donde nombre es el nombre del contenedor al que nos estamos vinculando y alias es un alias para el nombre del enlace. Ese alias se utilizará en breve. El flag --link también toma la forma:

--link <nombre o id>

En este caso, el alias coincide con el nombre. Podrías escribir el ejemplo anterior como:

$ docker run -d -P --name web --link db training/webapp python app.py

A continuación, inspecciona tus contenedores vinculados con docker inspect:

$ docker inspect -f "{{ .HostConfig.Links }}" web

[/db:/web/db]

Puedes ver que el contenedor web está ahora vinculado al contenedor db en /web/db. Lo que le permite acceder a la información sobre el contenedor db.

Entonces, ¿qué hace realmente la vinculación de contenedores? Has aprendido que un enlace permite que un contenedor de origen proporcione información sobre sí mismo a un contenedor de destino. En nuestro ejemplo, el destinatario, web, puede acceder a información sobre el origen db. Para hacer esto, Docker crea un túnel seguro entre los contenedores que no necesita exponer ningún puerto externamente en el contenedor; al iniciar el contenedor db no utilizamos los flags -P ni -p. Esa es una gran ventaja de la vinculación: no necesitamos exponer el contenedor de origen, en este caso la base de datos PostgreSQL, a la red.

Docker expone la información de conectividad del contenedor de origen al contenedor de destino de dos maneras:

  • Variables de entorno,
  • Actualización del archivo /etc/hosts.

Variables de entorno

Docker crea varias variables de entorno cuando vinculas contenedores. Docker crea automáticamente variables de entorno en el contenedor de destino basándose en los parámetros de --link. También expone todas las variables de entorno originadas por Docker del contenedor de origen. Estas incluyen variables procedentes de:

  • las instrucciones ENV en el Dockerfile del contenedor de origen.
  • las opciones -e, --env y --env-file en el comando docker run al iniciar el contenedor de origen.

Estas variables de entorno permiten descubrir mediante programación, desde el interior del contenedor de destino, la información relacionada con el contenedor de origen.

Warning

Es importante entender que todas las variables de entorno originadas por Docker dentro de un contenedor se ponen a disposición de cualquier contenedor que se vincule a él. Esto podría tener serias implicaciones de seguridad si se almacenan datos confidenciales en ellas.

Docker establece una variable de entorno <alias>_NAME para cada contenedor de destino enumerado en el parámetro --link. Por ejemplo, si un nuevo contenedor llamado web está vinculado a un contenedor de base de datos llamado db a través de --link db:webdb, entonces Docker crea una variable WEBDB_NAME=/web/webdb en el contenedor web.

Docker también define un conjunto de variables de entorno para cada puerto expuesto por el contenedor de origen. Cada variable tiene un prefijo único en la forma <nombre>_PORT_<puerto>_<protocolo>.

Los componentes de este prefijo son:

  • el alias <nombre> especificado en el parámetro --link (por ejemplo, webdb).
  • el número de <puerto> expuesto.
  • un <protocolo>, que es TCP o UDP.

Docker utiliza este formato de prefijo para definir tres variables de entorno distintas:

  • La variable prefix_ADDR contiene la dirección IP de la URL, por ejemplo WEBDB_PORT_5432_TCP_ADDR=172.17.0.82.
  • La variable prefix_PORT contiene solo el número de puerto de la URL, por ejemplo WEBDB_PORT_5432_TCP_PORT=5432.
  • La variable prefix_PROTO contiene solo el protocolo de la URL, por ejemplo WEBDB_PORT_5432_TCP_PROTO=tcp.

Si el contenedor expone múltiples puertos, se define un conjunto de variables de entorno para cada uno. Esto significa, por ejemplo, que si un contenedor expone 4 puertos, Docker crea 12 variables de entorno, 3 para cada puerto.

Además, Docker crea una variable de entorno llamada <alias>_PORT. Esta variable contiene la URL del primer puerto expuesto del contenedor de origen. El "primer" puerto se define como el puerto expuesto con el número más bajo. Por ejemplo, considera la variable WEBDB_PORT=tcp://172.17.0.82:5432. Si ese puerto se utiliza tanto para tcp como para udp, se especifica el de tcp.

Por último, Docker también expone cada variable de entorno originada por Docker del contenedor de origen como una variable de entorno en el de destino. Para cada variable, Docker crea una variable <alias>_ENV_<nombre> en el contenedor de destino. El valor de la variable se establece en el valor que Docker utilizó al iniciar el contenedor de origen.

Volviendo a nuestro ejemplo de la base de datos, puedes ejecutar el comando env para enumerar las variables de entorno del contenedor especificado.

$ docker run --rm --name web2 --link db:db training/webapp env

<...>
DB_NAME=/web2/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5432_TCP=tcp://172.17.0.5:5432
DB_PORT_5432_TCP_PROTO=tcp
DB_PORT_5432_TCP_PORT=5432
DB_PORT_5432_TCP_ADDR=172.17.0.5
<...>

Puedes ver que Docker ha creado una serie de variables de entorno con información útil sobre el contenedor de origen db. Cada variable tiene el prefijo DB_, que se rellena a partir del alias que especificaste anteriormente. Si el alias fuera db1, las variables tendrían el prefijo DB1_. Puedes utilizar estas variables de entorno para configurar tus aplicaciones para que se conecten a la base de datos en el contenedor db. La conexión es segura y privada; solo el contenedor web vinculado puede comunicarse con el contenedor db.

Notas importantes sobre las variables de entorno de Docker

A diferencia de las entradas de host en el archivo /etc/hosts, las direcciones IP almacenadas en las variables de entorno no se actualizan automáticamente si se reinicia el contenedor de origen. Recomendamos utilizar las entradas de host en /etc/hosts para resolver la dirección IP de los contenedores vinculados.

Estas variables de entorno solo se establecen para el primer proceso del contenedor. Algunos daemons, como sshd, las eliminan al iniciar interfaces de shell para la conexión.

Actualización del archivo /etc/hosts

Además de las variables de entorno, Docker añade una entrada de host para el contenedor de origen en el archivo /etc/hosts. Aquí se muestra una entrada para el contenedor web:

$ docker run -t -i --rm --link db:webdb training/webapp /bin/bash

root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7  aed84ee21bde
<...>
172.17.0.5  webdb 6e5cdeb2d300 db

Puedes ver dos entradas de host relevantes. La primera es una entrada para el contenedor web que utiliza el ID del contenedor como nombre de host. La segunda entrada utiliza el alias del enlace para hacer referencia a la dirección IP del contenedor db. Además del alias que proporciones, el nombre del contenedor vinculado (si es único del alias proporcionado en el parámetro --link) y el nombre de host del contenedor vinculado también se añaden a /etc/hosts para la dirección IP del contenedor vinculado. Puedes hacer ping a ese host a través de cualquiera de estas entradas:

root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-ping
root@aed84ee21bde:/opt/webapp# ping webdb

PING webdb (172.17.0.5): 48 data bytes
56 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.267 ms
56 bytes from 172.17.0.5: icmp_seq=1 ttl=64 time=0.250 ms
56 bytes from 172.17.0.5: icmp_seq=2 ttl=64 time=0.256 ms
Note

En el ejemplo, tuviste que instalar ping porque no estaba incluido en el contenedor inicialmente.

Aquí utilizaste el comando ping para hacer ping al contenedor db mediante su entrada de host, que se resuelve como 172.17.0.5. Puedes utilizar esta entrada de host para configurar una aplicación para que haga uso de tu contenedor db.

Note

Puedes vincular múltiples contenedores de destino a un único origen. Por ejemplo, podrías tener múltiples contenedores web (con diferentes nombres) conectados a tu contenedor db.

Si reinicias el contenedor de origen, los archivos /etc/hosts de los contenedores vinculados se actualizan automáticamente con la nueva dirección IP del contenedor de origen, lo que permite que la comunicación vinculada continúe.

$ docker restart db
db

$ docker run -t -i --rm --link db:db training/webapp /bin/bash

root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7  aed84ee21bde
<...>
172.17.0.9  db