Enlaces de contenedores heredados
WarningEl flag
--linkes 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--linkes 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
NoteEl flag
-pse puede utilizar varias veces para configurar múltiples puertos.
Conectar con el sistema de enlaces
NoteEsta sección cubre la característica de enlaces heredada en la red
bridgepredeterminada. 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:
- Puede ser útil nombrar los contenedores que realizan funciones específicas de manera que te resulte más fácil recordarlos, por ejemplo, nombrar
weba un contenedor que contenga una aplicación web. - Proporciona a Docker un punto de referencia que le permite referirse a otros contenedores, por ejemplo, puedes especificar que deseas vincular el contenedor
webal contenedordb.
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.
NoteLos 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 (condocker container rm) antes de poder crear un contenedor nuevo con el mismo nombre. Como alternativa, puedes utilizar el flag--rmcon el comandodocker 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
ENVen el Dockerfile del contenedor de origen. - las opciones
-e,--envy--env-fileen el comandodocker runal 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.
WarningEs 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_ADDRcontiene la dirección IP de la URL, por ejemploWEBDB_PORT_5432_TCP_ADDR=172.17.0.82. - La variable
prefix_PORTcontiene solo el número de puerto de la URL, por ejemploWEBDB_PORT_5432_TCP_PORT=5432. - La variable
prefix_PROTOcontiene solo el protocolo de la URL, por ejemploWEBDB_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
NoteEn el ejemplo, tuviste que instalar
pingporque 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.
NotePuedes 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