¿Cómo incluir archivos fuera del contexto de compilación de Docker?

462

¿Cómo puedo incluir archivos desde fuera del contexto de compilación de Docker usando el comando "AGREGAR" en el archivo Docker?

De la documentación de Docker:

La ruta debe estar dentro del contexto de la compilación; no puede AGREGAR ../something/something, porque el primer paso de una compilación de docker es enviar el directorio de contexto (y subdirectorios) al demonio de docker.

No quiero reestructurar todo mi proyecto solo para acomodar a Docker en este asunto. Quiero mantener todos mis archivos Docker en el mismo subdirectorio.

Además, parece que Docker aún no admite (y puede que nunca lo haga ) los enlaces simbólicos: el comando ADD de Dockerfile no sigue los enlaces simbólicos en el host # 1676.

Lo único que se me ocurre es incluir un paso previo a la compilación para copiar los archivos en el contexto de compilación de Docker (y configurar mi control de versiones para ignorar esos archivos). ¿Hay una mejor solución para eso?

ben_frankly
fuente
94
Esto tiene que ser lo peor de Docker. Desde mi punto de vista, no existe un "proyecto Docker". Docker es para proyectos de envío. Es solo una herramienta. No quiero tener que reconstruir todo mi proyecto para acomodar docker, agregar .dockerignore, etc. Al final del día, ¿quién sabe cuánto durará Docker? Sería genial tener una separación entre el código (es decir, el proyecto angular) y cualquier medio para implementarlo (es decir, acoplador). Después de todo, realmente no hay ningún beneficio en tener un archivo acoplable junto a todo lo demás. Es solo conectar las cosas para crear una imagen :(
TigerBear
3
Sí, esto es una gran decepción. Estoy enfrentando el mismo problema y tengo un archivo binario de mayor tamaño (ya comprimido) que no quiero copiar en cada contexto de compilación de Docker. Prefiero obtenerlo de su ubicación actual (fuera del contexto de compilación de Docker). Y no quiero asignar un volumen en tiempo de ejecución, porque estoy tratando de COPIAR / AGREGAR el archivo en el momento de la compilación y descomprimir y hacer lo que necesito para que ciertos binarios se incorporen a la imagen. De esta manera, girar los contenedores es rápido.
jersey bean
Encontré una buena estructura y explico con detalles en stackoverflow.com/a/53298446/433814
Marcello de Sales el
1
El problema con las compilaciones de Docker es el concepto inventado de "contexto". Los archivos Docker no son suficientes para definir una compilación, a menos que se coloquen bajo un directorio estratégico (también conocido como contexto), es decir, "/" como un extremo, por lo que puede acceder a cualquier ruta (tenga en cuenta que eso no es lo correcto en un proyecto cuerdo) o bien ..., además, hace que las construcciones de Docker sean muy lentas porque Docker escanea todo el contexto al inicio). Puede considerar crear una imagen acoplable con todos los archivos necesarios y utilizar FROMpara continuar desde allí. No cambiaría la estructura del proyecto para acomodar Docker (ni ninguna herramienta de compilación).
Devis L.

Respuestas:

413

La mejor manera de evitar esto es especificar el Dockerfile independientemente del contexto de compilación, usando -f.

Por ejemplo, este comando le dará acceso al comando ADD a cualquier cosa en su directorio actual.

docker build -f docker-files/Dockerfile .

Actualización : Docker ahora permite tener el Dockerfile fuera del contexto de compilación (corregido en 18.03.0-ce, https://github.com/docker/cli/pull/886 ). Entonces también puedes hacer algo como

docker build -f ../Dockerfile .
Cyberwiz
fuente
8
@Ro. utiliza la dockerfile:propiedad en la build:sección del archivo de composición docs.docker.com/compose/compose-file/#/compose-file-reference
Emerson Farrugia
3
Obtengo "El Dockerfile debe estar dentro del contexto de compilación". Realmente me gustaría tener un Dockerfile que pueda residir debajo del contexto de compilación actual. En su ejemplo, tiene el Dockerfile dentro / debajo del contexto de compilación actual, que por supuesto funciona.
Alexander Mills
3
Sí, solo quiero un Dockerfile compartido que corresponda a múltiples subdirectorios, todos los cuales son "contextos de compilación"
Alexander Mills
51
¿Esto resuelve el problema del OP de querer ADDun archivo que está fuera del directorio de contexto? Eso es lo que intento hacer, pero no creo que el uso -fhaga que los archivos externos se puedan agregar.
Sridhar Sarnobat
18
No se puede upvote esto lo suficiente .. en mi ventana acoplable-compose.yml tengo: build: context: .., dockerfile: dir/Dockerfile. ¡Ahora mi contexto de compilación es el directorio principal!
Mike Gleason jr Couturier
51

A menudo me encuentro utilizando la --build-argopción para este propósito. Por ejemplo, después de poner lo siguiente en el Dockerfile:

ARG SSH_KEY
RUN echo "$SSH_KEY" > /root/.ssh/id_rsa

Solo puedes hacer:

docker build -t some-app --build-arg SSH_KEY="$(cat ~/file/outside/build/context/id_rsa)" .

Pero tenga en cuenta la siguiente advertencia de la documentación de Docker :

Advertencia: no se recomienda utilizar variables de tiempo de compilación para pasar secretos como claves github, credenciales de usuario, etc. Los valores de las variables de tiempo de compilación son visibles para cualquier usuario de la imagen con el comando docker history.

usuario2658308
fuente
66
Este es un mal consejo sin una gran advertencia. De la documentación de Docker: "Advertencia: no se recomienda utilizar variables de tiempo de compilación para pasar secretos como claves github, credenciales de usuario, etc. Los valores de las variables de tiempo de compilación son visibles para cualquier usuario de la imagen con el comando de historial de docker". [1] En otras palabras, el ejemplo dado en este ejemplo revela la clave SSH privada en la imagen del acoplador. En algunos contextos, eso podría estar bien. docs.docker.com/engine/reference/builder/#arg
sheldonh
3
Finalmente, para superar este problema de seguridad, puede usar técnicas como aplastar o compilaciones en varias etapas: vsupalov.com/build-docker-image-clone-private-repo-ssh-key
Jojo
46

En Linux, puede montar otros directorios en lugar de simularlos

mount --bind olddir newdir

Consulte /superuser/842642 para obtener más detalles.

No sé si hay algo similar disponible para otros sistemas operativos. También intenté usar Samba para compartir una carpeta y volver a montarla en el contexto de Docker, que también funcionó.

Günter Zöchbauer
fuente
2
Solo root puede enlazar directorios
jjcf89
28

Pasé un buen rato tratando de descubrir un buen patrón y cómo explicar mejor lo que está sucediendo con esta función de soporte. Me di cuenta de que la mejor manera de explicarlo era la siguiente ...

  • Dockerfile: solo verá archivos bajo su propia ruta relativa
  • Contexto: un lugar en el "espacio" donde se copiarán los archivos que desea compartir y su Dockerfile

Entonces, dicho esto, aquí hay un ejemplo del Dockerfile que necesita reutilizar un archivo llamado start.sh

Dockerfile

Se ALWAYScargará desde su ruta relativa, teniendo el directorio actual de sí mismo como localreferencia a las rutas que especifique.

COPY start.sh /runtime/start.sh

Archivos

Teniendo en cuenta esta idea, podemos pensar en tener múltiples copias para los Dockerfiles que compilan elementos específicos, pero todos necesitan acceso al start.sh.

./all-services/
   /start.sh
   /service-X/Dockerfile
   /service-Y/Dockerfile
   /service-Z/Dockerfile
./docker-compose.yaml

Teniendo en cuenta esta estructura y los archivos anteriores, aquí hay un docker-compose.yml

docker-compose.yaml

  • En este ejemplo, su shareddirectorio de contexto es el runtimedirectorio.
    • El mismo modelo mental aquí, piensa que todos los archivos bajo este directorio se mueven al llamado context.
    • Del mismo modo, solo especifique el Dockerfile que desea copiar en ese mismo directorio. Puede especificar eso usando dockerfile.
  • el directorio donde se encuentra su contenido principal es el contexto real que se establecerá.

El docker-compose.ymles el siguiente

version: "3.3"
services:

  service-A
    build:
      context: ./all-service
      dockerfile: ./service-A/Dockerfile

  service-B
    build:
      context: ./all-service
      dockerfile: ./service-B/Dockerfile

  service-C
    build:
      context: ./all-service
      dockerfile: ./service-C/Dockerfile
  • all-servicese establece como contexto, el archivo compartido start.shse copia allí y el Dockerfile especificado por cada uno dockerfile.
  • ¡Cada uno se construye a su manera, compartiendo el archivo de inicio!

¡Salud!

Marcello de Sales
fuente
1
Su punto de Dockerfile no es completamente cierto, según ha apuntado por la respuesta aceptada, si se encuentra en una jerarquía de carpetas a/b/c, a continuación, ejecutar sí docker build .en cno le permitirá el acceso ../file-in-b. Pero, creo que el malentendido general en esto (o al menos el mío fue) es que el contexto se define por la ubicación establecida por el primer argumento del comando de compilación, no por la ubicación del Dockerfile. Entonces, como se indica en la respuesta aceptada: from a: docker build -f a/b/c/Dockerfile . significa que en el Dockerfile .ahora está la carpetaa
β.εηοιτ.βε
1
Citando los documentos de Dockerfile: las rutas de los archivos y directorios se interpretarán como relativos al origen del contexto de la compilación.
Nishant George Agrwal
18

Si lee la discusión en el número 2745, no solo Docker nunca admitirá enlaces simbólicos, sino que nunca admitirá agregar archivos fuera de su contexto. Parece ser una filosofía de diseño que los archivos que entran en la construcción de Docker deberían ser explícitamente parte de su contexto o ser de una URL donde presumiblemente también se implementa con una versión fija para que la construcción sea repetible con URL o archivos bien conocidos enviados con el contenedor acoplable.

Prefiero construir desde una fuente controlada por versión, es decir, docker build -t stuff http://my.git.org/repo , de lo contrario, estoy construyendo desde algún lugar aleatorio con archivos aleatorios.

fundamentalmente, no ... - SvenDowideit, Docker Inc

Solo es mi opinión, pero creo que debería reestructurarse para separar el código y los repositorios de Docker. De esa manera, los contenedores pueden ser genéricos y extraer cualquier versión del código en tiempo de ejecución en lugar de en tiempo de compilación.

Alternativamente, use docker como su artefacto fundamental de implementación de código y luego coloque el dockerfile en la raíz del repositorio de código. si sigue esta ruta, probablemente tenga sentido tener un contenedor acoplable principal para obtener detalles más generales del nivel del sistema y un contenedor secundario para la configuración específica de su código.

Usman Ismail
fuente
¿Por qué usar docker entonces?
lscoughlin
11

Creo que la solución más simple sería cambiar el "contexto" en sí.

Entonces, por ejemplo, en lugar de dar:

docker build -t hello-demo-app .

que establece el directorio actual como contexto, digamos que desea el directorio principal como contexto, solo use:

docker build -t hello-demo-app ..
Anshuman Manral
fuente
66
Creo que esto rompe .dockerignore: - \
NullVoxPopuli
Renuncié a .dockerignore y en su lugar hice la carpeta acoplada administrada Makefile que contiene solo los archivos necesarios para el contexto de compilación ... Solo necesito llamar make buildy extrae todos los archivos necesarios si se actualizaron y luego llama a la construcción de acoplador apropiada ... Necesito hacer un trabajo extra, pero funciona a la perfección porque tengo el control total.
Sahsahae
5

También puede crear un tarball de lo que la imagen necesita primero y usarlo como contexto.

https://docs.docker.com/engine/reference/commandline/build/#/tarball-contexts

Laurent Picquet
fuente
Gran consejo! Descubrí incluso se puede alimentar ventana acoplable construir el paquete de archivos como contexto de la entrada estándar: tar zc /dir1 /dir2 |docker build -. Esto fue muy útil en mi caso.
Tore Olsen
3

Usando docker-compose, logré esto creando un servicio que monta los volúmenes que necesito y confirmando la imagen del contenedor. Luego, en el servicio posterior, confío en la imagen previamente comprometida, que tiene todos los datos almacenados en ubicaciones montadas. Luego tendrá que copiar estos archivos a su destino final, ya que los directorios montados en el host no se comprometen al ejecutar un docker commitcomando

No tiene que usar docker-compose para lograr esto, pero hace la vida un poco más fácil

# docker-compose.yml

version: '3'
  services:
    stage:
      image: alpine
      volumes:
        - /host/machine/path:/tmp/container/path
      command: bash -c "cp -r /tmp/container/path /final/container/path"
    setup:
      image: stage
# setup.sh

# Start "stage" service
docker-compose up stage

# Commit changes to an image named "stage"
docker commit $(docker-compose ps -q stage) stage

# Start setup service off of stage image
docker-compose up setup
Kris Rivera
fuente
1

Tuve este mismo problema con un proyecto y algunos archivos de datos que no pude mover dentro del contexto de repositorio por razones de HIPPA. Terminé usando 2 Dockerfiles. Uno construye la aplicación principal sin las cosas que necesitaba fuera del contenedor y las publica en un repositorio interno. Luego, un segundo dockerfile extrae esa imagen y agrega los datos y crea una nueva imagen que luego se implementa y nunca se almacena en ningún lugar. No es ideal, pero funcionó para mis propósitos de mantener la información confidencial fuera del repositorio.

Annulet Consulting
fuente
1

Una solución fácil podría ser simplemente montar el volumen (usando el indicador -v o --mount) en el contenedor cuando lo ejecute y acceda a los archivos de esa manera.

ejemplo:

docker run -v /path/to/file/on/host:/desired/path/to/file/in/container/ image_name

para más información, consulte: https://docs.docker.com/storage/volumes/

Ben Wex
fuente
Tenga en cuenta que esto solo funciona si el volumen es una dependencia de tiempo de ejecución. Para dependencias de tiempo de compilación, docker runes demasiado tarde.
usuario3735633
1

Como se describe en este problema de GitHub, la compilación realmente sucede /tmp/docker-12345, por lo que una ruta ../relative-add/some-filerelativa es relativa a /tmp/docker-12345. Por lo tanto, buscaría /tmp/relative-add/some-file, lo que también se muestra en el mensaje de error. *

No está permitido incluir archivos desde fuera del directorio de compilación, por lo que esto da como resultado el mensaje "Ruta prohibida".

Rocksn17
fuente
0

Una manera rápida y sucia es configurar el contexto de construcción tantos niveles como sea necesario, pero esto puede tener consecuencias. Si está trabajando en una arquitectura de microservicios que se ve así:

./Code/Repo1
./Code/Repo2
...

Puede establecer el contexto de compilación en el Codedirectorio principal y luego acceder a todo, pero resulta que con una gran cantidad de repositorios, esto puede hacer que la compilación tarde mucho tiempo.

Una situación de ejemplo podría ser que otro equipo mantiene un esquema de base de datos Repo1y el código de su equipo enRepo2 depende de esto. Desea reducir esta dependencia con algunos de sus propios datos semilla sin preocuparse por los cambios de esquema o contaminar el repositorio del otro equipo (dependiendo de cuáles sean los cambios, es posible que tenga que cambiar sus scripts de datos semilla, por supuesto). El segundo enfoque es hacky pero evita el problema de las compilaciones largas:

Cree un script sh (o ps1) ./Code/Repo2para copiar los archivos que necesita e invoque los comandos del acoplador que desee, por ejemplo:

#!/bin/bash
rm -r ./db/schema
mkdir ./db/schema

cp  -r ../Repo1/db/schema ./db/schema

docker-compose -f docker-compose.yml down
docker container prune -f
docker-compose -f docker-compose.yml up --build

En el archivo docker-compose, simplemente configure el contexto como Repo2root y use el contenido del ./db/schemadirectorio en su dockerfile sin preocuparse por la ruta. Tenga en cuenta que correrá el riesgo de comprometer accidentalmente este directorio con el control de origen, pero las acciones de limpieza de secuencias de comandos deberían ser bastante fáciles.

user1007074
fuente
0

En mi caso, mi Dockerfile está escrito como una plantilla que contiene marcadores de posición que estoy reemplazando con un valor real usando mi archivo de configuración.

Por lo tanto, no pude especificar este archivo directamente, pero lo canalizo en la construcción de Docker de esta manera:

sed "s/%email_address%/$EMAIL_ADDRESS/;" ./Dockerfile | docker build -t katzda/bookings:latest . -f -;

Pero debido a la tubería, el COPYcomando no funcionó. Pero la forma anterior lo resuelve -f -(diciendo explícitamente que el archivo no se proporciona). Prescindiendo solo de -la -fbandera, no se proporcionan el contexto Y el Dockerfile, lo cual es una advertencia.

Daniel Katz
fuente
0

El truco consiste en reconocer que puede especificar el contexto en el comando de compilación para incluir archivos del directorio principal si especifica la ruta de Docker, cambiaría mi Dockerfile para que se vea así:

...
COPY ./ /dest/
...

Entonces mi comando de compilación puede verse así:

docker built -t TAG -f DOCKER_FILE_PATH CONTEXT

Desde el directorio del proyecto

docker built -t username/project[:tag] -f ./docker/Dockerfile .

Del proyecto / acoplador

docker built -t username/project[:tag] -f ./docker/Dockerfile ..
daniekpo
fuente