# 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](/engine/network/links/drivers/bridge/#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](/engine/network/links/), 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](/engine/network/links/drivers/bridge/#diferencias-entre-los-bridges-definidos-por-el-usuario-y-el-bridge-predeterminado).

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:

```console
$ 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.

```console
$ 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:

```console
$ 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:

```console
$ 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`.

```console
$ 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:

```console
$ 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:

```console
$ 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á.

```console
$ 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](/engine/network/links/drivers/bridge/#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:

```console
$ 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`.

```console
$ 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.

```console
$ 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:

```console
$ docker container rm -f web
```

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

```console
$ 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:

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

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

```console
$ 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.

```console
$ 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`](#actualizacion-del-archivo-etchosts), 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`:

```console
$ 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:

```console
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.

```console
$ 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
```

