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

Montajes de tipo bind

Cuando utilizas un montaje de tipo bind, un archivo o directorio de la máquina anfitriona se monta desde el anfitrión en un contenedor. Por el contrario, cuando utilizas un volumen, se crea un nuevo directorio dentro del directorio de almacenamiento de Docker en la máquina anfitriona. Docker crea y mantiene esta ubicación de almacenamiento, pero los contenedores acceden a ella directamente utilizando operaciones estándar del sistema de archivos.

Cuándo utilizar montajes de tipo bind

Los montajes de tipo bind son apropiados para los siguientes tipos de casos de uso:

  • Compartir código fuente o artefactos de compilación entre un entorno de desarrollo en el anfitrión de Docker y un contenedor.

  • Cuando quieres crear o generar archivos en un contenedor y persistir los archivos en el sistema de archivos del anfitrión.

  • Compartir archivos de configuración de la máquina anfitriona con los contenedores. Así es como Docker proporciona resolución DNS a los contenedores por defecto, montando /etc/resolv.conf de la máquina anfitriona en cada contenedor.

Los montajes de tipo bind también están disponibles para las compilaciones (builds): puedes montar el código fuente del anfitrión en el contenedor de compilación para probar, analizar (lint) o compilar un proyecto.

Montajes de tipo bind sobre datos existentes

Si montas un archivo o directorio en un directorio del contenedor en el que ya existen archivos o directorios, los archivos preexistentes quedarán ocultos por el montaje. Esto es similar a si guardaras archivos en /mnt en un anfitrión Linux y luego montaras una unidad USB en /mnt. El contenido de /mnt quedaría oculto por el contenido de la unidad USB hasta que esta se desmontara.

En el caso de los contenedores, no existe una forma sencilla de eliminar un montaje para volver a mostrar los archivos ocultos. Tu mejor opción es volver a crear el contenedor sin el montaje.

Consideraciones y restricciones

  • Los montajes de tipo bind tienen acceso de escritura a los archivos del anfitrión por defecto.

    Un efecto secundario de usar montajes de tipo bind es que puedes cambiar el sistema de archivos del anfitrión a través de procesos que se ejecutan en un contenedor, incluyendo la creación, modificación o eliminación de archivos o directorios importantes del sistema. Esta capacidad puede tener implicaciones de seguridad. Por ejemplo, puede afectar a los procesos que no son de Docker en el sistema anfitrión.

    Puedes utilizar la opción readonly o ro para evitar que el contenedor escriba en el montaje.

  • Los montajes de tipo bind se crean en el anfitrión del demonio de Docker, no en el cliente.

    Si estás utilizando un demonio de Docker remoto, no puedes crear un montaje de tipo bind para acceder a los archivos de la máquina cliente en un contenedor.

    En el caso de Docker Desktop, el demonio se ejecuta dentro de una máquina virtual Linux, no directamente en el anfitrión nativo. Docker Desktop cuenta con mecanismos integrados que gestionan los montajes de tipo bind de forma transparente, permitiéndote compartir rutas del sistema de archivos del anfitrión nativo con contenedores que se ejecutan en la máquina virtual.

  • Los contenedores con montajes de tipo bind están fuertemente vinculados al anfitrión.

    Los montajes de tipo bind dependen de que el sistema de archivos de la máquina anfitriona tenga disponible una estructura de directorios específica. Esta dependencia significa que los contenedores con montajes de tipo bind pueden fallar si se ejecutan en un anfitrión diferente que no disponga de la misma estructura de directorios.

Sintaxis

Para crear un montaje de tipo bind, puedes utilizar la bandera --mount o --volume.

$ docker run --mount type=bind,src=<host-path>,dst=<container-path>
$ docker run --volume <host-path>:<container-path>

En general, se prefiere --mount. La principal diferencia es que la bandera --mount es más explícita y admite todas las opciones disponibles.

Si utilizas --volume para montar un archivo o directorio que aún no existe en el anfitrión de Docker, Docker crea automáticamente el directorio en el anfitrión por ti. Siempre se crea como un directorio.

Por defecto, --mount no crea automáticamente un directorio si la ruta de montaje especificada no existe en el anfitrión. En su lugar, produce un error:

$ docker run --mount type=bind,src=/dev/noexist,dst=/mnt/foo alpine
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /dev/noexist.

Puedes utilizar la opción bind-create-src para crear automáticamente el directorio de origen en el anfitrión si no existe:

$ docker run --mount type=bind,src=/home/user/mydir,dst=/mnt/foo,bind-create-src alpine

Opciones para --mount

La bandera --mount consta de múltiples pares clave-valor, separados por comas, cada uno de los cuales consiste en una tupla <key>=<value>. El orden de las claves no es relevante.

$ docker run --mount type=bind,src=<host-path>,dst=<container-path>[,<key>=<value>...]

Las opciones válidas para --mount type=bind incluyen:

OpciónDescripción
source, srcLa ubicación del archivo o directorio en el anfitrión. Puede ser una ruta absoluta o relativa.
destination, dst, targetLa ruta donde se monta el archivo o directorio en el contenedor. Debe ser una ruta absoluta.
readonly, roSi está presente, hace que el montaje de tipo bind se monte en el contenedor como de solo lectura.
bind-propagationSi está presente, cambia la propagación del montaje (bind propagation).
bind-create-srcCrea automáticamente el directorio de origen en el anfitrión si no existe. Por defecto, --mount produce un error si la ruta de origen no existe en el demonio.
Example
$ docker run --mount type=bind,src=.,dst=/project,ro,bind-propagation=rshared

Opciones para --volume

La bandera --volume o -v consta de tres campos, separados por caracteres de dos puntos (:). Los campos deben estar en el orden correcto.

$ docker run -v <host-path>:<container-path>[:opts]

El primer campo es la ruta en el anfitrión para realizar el montaje de tipo bind en el contenedor. El segundo campo es la ruta donde se monta el archivo o directorio en el contenedor.

El tercer campo es opcional y consiste en una lista de opciones separadas por comas. Las opciones válidas para --volume con un montaje de tipo bind incluyen:

OpciónDescripción
readonly, roSi está presente, hace que el montaje de tipo bind se monte en el contenedor como de solo lectura.
z, ZConfigura el etiquetado de SELinux. Consulta Configurar la etiqueta de SELinux
rprivate (default)Establece la propagación del montaje en rprivate para este montaje. Consulta Configurar la propagación del montaje.
privateEstablece la propagación del montaje en private para este montaje. Consulta Configurar la propagación del montaje.
rsharedEstablece la propagación del montaje en rshared para este montaje. Consulta Configurar la propagación del montaje.
sharedEstablece la propagación del montaje en shared para este montaje. Consulta Configurar la propagación del montaje.
rslaveEstablece la propagación del montaje en rslave para este montaje. Consulta Configurar la propagación del montaje.
slaveEstablece la propagación del montaje en slave para este montaje. Consulta Configurar la propagación del montaje.
Example
$ docker run -v .:/project:ro,rshared

Iniciar un contenedor con un montaje de tipo bind

Considera un caso en el que tienes un directorio source y que cuando compilas el código fuente, los artefactos se guardan en otro directorio, source/target/. Quieres que los artefactos estén disponibles para el contenedor en /app/ y quieres que el contenedor tenga acceso a una nueva compilación cada vez que compiles el código fuente en tu anfitrión de desarrollo. Utiliza el siguiente comando para montar el directorio target/ en tu contenedor en /app/. Ejecuta el comando desde el directorio source. El subcomando $(pwd) se expande al directorio de trabajo actual en anfitriones Linux o macOS. Si estás en Windows, consulta también las Conversiones de rutas en Windows.

Los siguientes ejemplos de --mount y -v producen el mismo resultado. No puedes ejecutarlos ambos a menos que elimines el contenedor devtest después de ejecutar el primero.

$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest
$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app \
  nginx:latest

Utiliza docker inspect devtest para verificar que el montaje de tipo bind se haya creado correctamente. Busca la sección Mounts:

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

Esto muestra que el montaje es de tipo bind, muestra el origen y destino correctos, muestra que el montaje es de lectura-escritura y que la propagación está configurada en rprivate.

Detén y elimina el contenedor:

$ docker container rm -fv devtest

Montar en un directorio no vacío en el contenedor

Si montas un directorio en un directorio no vacío del contenedor, el contenido existente de ese directorio quedará oculto por el montaje de tipo bind. Esto puede ser beneficioso, por ejemplo, cuando quieres probar una nueva versión de tu aplicación sin necesidad de compilar una nueva imagen. Sin embargo, también puede ser sorprendente y este comportamiento difiere del de los volúmenes.

Este ejemplo se ha forzado para ser extremo, pero reemplaza el contenido del directorio /usr/ del contenedor con el directorio /tmp/ de la máquina anfitriona. En la mayoría de los casos, esto resultaría en un contenedor que no funciona.

Los ejemplos de --mount y -v tienen el mismo resultado final.

$ docker run -d \
  -it \
  --name broken-container \
  --mount type=bind,source=/tmp,target=/usr \
  nginx:latest

docker: Error response from daemon: oci runtime error: container_linux.go:262:
starting container process caused "exec: \"nginx\": executable file not found in $PATH".
$ docker run -d \
  -it \
  --name broken-container \
  -v /tmp:/usr \
  nginx:latest

docker: Error response from daemon: oci runtime error: container_linux.go:262:
starting container process caused "exec: \"nginx\": executable file not found in $PATH".

El contenedor se crea pero no se inicia. Elimínalo:

$ docker container rm broken-container

Usar un montaje de tipo bind de solo lectura

Para algunas aplicaciones de desarrollo, el contenedor necesita escribir en el montaje de tipo bind, de modo que los cambios se propaguen de vuelta al anfitrión de Docker. En otras ocasiones, el contenedor solo necesita acceso de lectura.

Este ejemplo modifica el anterior, pero monta el directorio como un montaje de tipo bind de solo lectura, añadiendo ro a la lista de opciones (vacía por defecto), después del punto de montaje dentro del contenedor. Si hay varias opciones presentes, sepáralas por comas.

Los ejemplos de --mount y -v tienen el mismo resultado.

$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app,readonly \
  nginx:latest
$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:ro \
  nginx:latest

Utiliza docker inspect devtest para verificar que el montaje de tipo bind se haya creado correctamente. Busca la sección Mounts:

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "ro",
        "RW": false,
        "Propagation": "rprivate"
    }
],

Detén y elimina el contenedor:

$ docker container rm -fv devtest

Montajes recursivos

Cuando montas una ruta que a su vez contiene montajes, esos submontajes también se incluyen en el montaje de tipo bind por defecto. Este comportamiento es configurable mediante la opción bind-recursive para --mount. Esta opción solo es compatible con la bandera --mount, no con -v ni --volume.

Si el montaje de tipo bind es de solo lectura, Docker Engine realiza un intento de mejor esfuerzo para que los submontajes también sean de solo lectura. Esto se denomina montajes de solo lectura recursivos. Los montajes de solo lectura recursivos requieren la versión 5.12 o posterior del kernel de Linux. Si ejecutas una versión del kernel anterior, los submontajes se montan automáticamente como lectura-escritura por defecto. Intentar configurar los submontajes como de solo lectura en una versión del kernel anterior a la 5.12, utilizando la opción bind-recursive=readonly, produce un error.

Los valores admitidos para la opción bind-recursive son:

ValorDescripción
enabled (default)Los montajes de solo lectura se hacen recursivamente de solo lectura si el kernel es v5.12 o posterior. De lo contrario, los submontajes son de lectura-escritura.
disabledLos submontajes se ignoran (no se incluyen en el montaje de tipo bind).
writableLos submontajes son de lectura-escritura.
readonlyLos submontajes son de solo lectura. Requiere la versión del kernel v5.12 o posterior.

Configurar la propagación del montaje

La propagación del montaje (bind propagation) se establece de forma predeterminada en rprivate tanto para montajes de tipo bind como para volúmenes. Solo se puede configurar para montajes de tipo bind y únicamente en máquinas anfitrionas Linux. La propagación del montaje es un tema avanzado y muchos usuarios nunca necesitarán configurarla.

La propagación del montaje se refiere a si los montajes creados dentro de un montaje de tipo bind determinado pueden propagarse a las réplicas de ese montaje. Considera un punto de montaje /mnt, que también está montado en /tmp. Los ajustes de propagación controlan si un montaje en /tmp/a también estaría disponible en /mnt/a. Cada configuración de propagación tiene una contraparte recursiva. En el caso de la recursividad, considera que /tmp/a también está montado como /foo. Los ajustes de propagación controlan si existiría /mnt/a y/o /tmp/a.

Note

La propagación de montaje no funciona con Docker Desktop.

Configuración de propagaciónDescripción
sharedLos submontajes del montaje original se exponen a los montajes réplica, y los submontajes de los montajes réplica también se propagan al montaje original.
slaveSimilar a un montaje compartido, pero solo en una dirección. Si el montaje original expone un submontaje, el montaje réplica puede verlo. Sin embargo, si el montaje réplica expone un submontaje, el montaje original no puede verlo.
privateEl montaje es privado. Los submontajes dentro de él no se exponen a los montajes réplica, y los submontajes de los montajes réplica no se exponen al montaje original.
rsharedLo mismo que compartido, pero la propagación también se extiende hacia y desde los puntos de montaje anidados dentro de cualquiera de los puntos de montaje originales o réplicas.
rslaveLo mismo que esclavo, pero la propagación también se extiende hacia y desde los puntos de montaje anidados dentro de cualquiera de los puntos de montaje originales o réplicas.
rprivateEl predeterminado. Lo mismo que privado, lo que significa que ningún punto de montaje dentro de los puntos de montaje originales o réplicas se propaga en ninguna dirección.

Antes de poder configurar la propagación del montaje en un punto de montaje, el sistema de archivos del anfitrión ya debe admitir la propagación del montaje.

Para obtener más información sobre la propagación del montaje, consulta la documentación del kernel de Linux para subárboles compartidos (shared subtree).

El siguiente ejemplo monta el directorio target/ en el contenedor dos veces, y el segundo montaje establece tanto la opción ro como la opción de propagación de montaje rslave.

Los ejemplos de --mount y -v tienen el mismo resultado.

$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  --mount type=bind,source="$(pwd)"/target,target=/app2,readonly,bind-propagation=rslave \
  nginx:latest
$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app \
  -v "$(pwd)"/target:/app2:ro,rslave \
  nginx:latest

Ahora, si creas /app/foo/, /app2/foo/ también existirá.

Configurar la etiqueta de SELinux

Si utilizas SELinux, puedes añadir las opciones z o Z para modificar la etiqueta SELinux del archivo o directorio del anfitrión que se está montando en el contenedor. Esto afecta al propio archivo o directorio de la máquina anfitriona y puede tener consecuencias fuera del ámbito de Docker.

  • La opción z indica que el contenido del montaje de tipo bind se comparte entre múltiples contenedores.
  • La opción Z indica que el contenido del montaje de tipo bind es privado y no compartido.

Utiliza estas opciones con extrema precaución. Realizar un montaje de tipo bind de un directorio del sistema como /home o /usr con la opción Z dejará inoperable tu máquina anfitriona y es posible que debas volver a etiquetar los archivos de la máquina anfitriona a mano.

Important

Al utilizar montajes de tipo bind con servicios, las etiquetas SELinux (:Z y :z), así como :ro se ignoran. Consulta moby/moby #32579 para obtener más detalles.

Este ejemplo establece la opción z para especificar que múltiples contenedores pueden compartir el contenido del montaje de tipo bind:

No es posible modificar la etiqueta de SELinux utilizando la bandera --mount.

$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:z \
  nginx:latest

Usar un montaje de tipo bind con Docker Compose

Un único servicio de Docker Compose con un montaje de tipo bind se ve así:

services:
  frontend:
    image: node:lts
    volumes:
      - type: bind
        source: ./static
        target: /opt/app/static
volumes:
  myapp:

Para obtener más información sobre el uso de volúmenes de tipo bind con Compose, consulta la referencia de Compose sobre el elemento de nivel superior de volúmenes y la referencia de Compose sobre el atributo de volumen.

Siguientes pasos