share - imagen - Cómo montar volúmenes de host en contenedores docker en Dockerfile durante la compilación
guardar imagen docker (8)
Pregunta original: ¿Cómo usar la instrucción VOLUME en Dockerfile?
La pregunta real que quiero resolver es: cómo montar volúmenes de host en contenedores docker en Dockerfile durante la compilación, es decir, hacer que el
docker run -v /export:/export
capacidad
docker run -v /export:/export
durante la
docker build
.
La razón detrás de esto, para mí, es cuando construyo cosas en Docker, no quiero esas cachés (
apt-get install
) bloqueadas en una sola ventana acoplable, sino compartirlas / reutilizarlas.
Esa es la razón principal por la que hago esta pregunta.
Última actualización:
Antes de docker v18.09, la respuesta correcta debería ser la que comienza con:
Hay una manera de montar un volumen durante una compilación, pero no involucra Dockerfiles.
Sin embargo, esa fue una respuesta mal planteada, organizada y respaldada. Cuando estaba reinstalando mi docker contiene, me topé con el siguiente artículo:
Dockerize un servicio apt-cacher-ng
https://docs.docker.com/engine/examples/apt-cacher-ng/
Esa es la solución de Docker para esta / mi pregunta, no directa sino indirectamente. Es la forma ortodoxa que Docker sugiere que hagamos. Y admito que es mejor que el que estaba tratando de preguntar aquí.
Otra forma es la respuesta recién aceptada , por ejemplo, el Buildkit en v18.09.
Elige lo que más te convenga.
Era: había habido una solución: rocker, que no era de Docker, pero ahora que se ha suspendido el rocker, revierto la respuesta a "No es posible" nuevamente.
Actualización anterior: Entonces la respuesta es "No es posible". Puedo aceptarlo como respuesta, ya que sé que el problema se ha discutido ampliamente en https://github.com/docker/docker/issues/3156 . Puedo entender que la portabilidad es un problema primordial para el desarrollador de Docker; pero como usuario de Docker, debo decir que estoy muy decepcionado con esta característica que falta. Permítanme cerrar mi argumento con una cita de la discusión antes mencionada: " Me gustaría usar Gentoo como imagen base, pero definitivamente no quiero que> 1 GB de datos del árbol de Portage estén en ninguna de las capas una vez que la imagen ha sido construida. podría tener algunos buenos y compactos contenedores si no fuera porque el gigantesco árbol de portage tiene que aparecer en la imagen durante la instalación. "Sí, puedo usar wget o curl para descargar lo que necesito, pero el hecho es simplemente una consideración de portabilidad ahora me obliga a descargar> 1 GB de árbol de Portage cada vez que construyo una imagen base de Gentoo, no es eficiente ni fácil de usar. Además, el repositorio de paquetes SIEMPRE estará bajo / usr / portage, por lo tanto SIEMPRE PORTÁTIL bajo Gentoo. Nuevamente, respeto la decisión, pero permítame expresar mi decepción también mientras tanto. Gracias.
Pregunta original en detalles:
De
Compartir directorios a través de volúmenes
http://docker.readthedocs.org/en/v0.7.3/use/working_with_volumes/
dice que la función de volúmenes de datos "ha estado disponible desde la versión 1 de la API remota de Docker". Mi docker es de la versión 1.2.0, pero encontré que el ejemplo dado en el artículo anterior no funciona:
# BUILD-USING: docker build -t data .
# RUN-USING: docker run -name DATA data
FROM busybox
VOLUME ["/var/volume1", "/var/volume2"]
CMD ["/usr/bin/true"]
¿Cuál es la forma correcta en Dockerfile de montar volúmenes montados en host en contenedores de Docker, a través del comando VOLUME?
$ apt-cache policy lxc-docker
lxc-docker:
Installed: 1.2.0
Candidate: 1.2.0
Version table:
*** 1.2.0 0
500 https://get.docker.io/ubuntu/ docker/main amd64 Packages
100 /var/lib/dpkg/status
$ cat Dockerfile
FROM debian:sid
VOLUME ["/export"]
RUN ls -l /export
CMD ls -l /export
$ docker build -t data .
Sending build context to Docker daemon 2.56 kB
Sending build context to Docker daemon
Step 0 : FROM debian:sid
---> 77e97a48ce6a
Step 1 : VOLUME ["/export"]
---> Using cache
---> 59b69b65a074
Step 2 : RUN ls -l /export
---> Running in df43c78d74be
total 0
---> 9d29a6eb263f
Removing intermediate container df43c78d74be
Step 3 : CMD ls -l /export
---> Running in 8e4916d3e390
---> d6e7e1c52551
Removing intermediate container 8e4916d3e390
Successfully built d6e7e1c52551
$ docker run data
total 0
$ ls -l /export | wc
20 162 1131
$ docker -v
Docker version 1.2.0, build fa7b24f
A medida que ejecuta el contenedor, se crea un directorio en su host y se monta en el contenedor. Puedes averiguar con qué directorio es esto
$ docker inspect --format "{{ .Volumes }}" <ID>
map[/export:/var/lib/docker/vfs/dir/<VOLUME ID...>]
Si desea montar un directorio desde su host dentro de su contenedor, debe usar el parámetro
-v
y especificar el directorio.
En su caso esto sería:
docker run -v /export:/export data
Así que usarías la carpeta hosts dentro de tu contenedor.
ACTUALIZACIÓN: Alguien simplemente no tomará un no como respuesta, y me gusta mucho, especialmente a esta pregunta en particular.
BUENAS NOTICIAS, ahora hay un camino:
La solución es Rocker: https://github.com/grammarly/rocker
John Yani said : "OMI, resuelve todos los puntos débiles de Dockerfile, lo que lo hace adecuado para el desarrollo".
Balancín
https://github.com/grammarly/rocker
Al introducir nuevos comandos, Rocker tiene como objetivo resolver los siguientes casos de uso, que son dolorosos con Docker:
- Monte volúmenes reutilizables en la etapa de compilación, por lo que las herramientas de administración de dependencias pueden usar caché entre compilaciones.
- Comparta claves ssh con build (para extraer repositorios privados, etc.), sin dejarlas en la imagen resultante.
- Cree y ejecute aplicaciones en diferentes imágenes, pueda pasar fácilmente un artefacto de una imagen a otra, idealmente tenga esta lógica en un solo Dockerfile.
- Etiquete / empuje imágenes directamente desde Dockerfiles.
- Pase las variables del comando de compilación de shell para que puedan sustituirse por un Dockerfile.
Y más. Estos son los problemas más críticos que estaban bloqueando nuestra adopción de Docker en Grammarly.
Actualización: Rocker ha sido descontinuado, según el repositorio oficial del proyecto en Github
A principios de 2018, el ecosistema de contenedores es mucho más maduro que hace tres años cuando se inició este proyecto. Ahora, algunas de las características críticas y sobresalientes de rocker se pueden cubrir fácilmente con Docker Build u otras herramientas bien compatibles, aunque algunas características siguen siendo exclusivas de Rocker. Consulte https://github.com/grammarly/rocker/issues/199 para obtener más detalles.
Aquí hay una versión simplificada del enfoque de 2 pasos usando build y commit, sin scripts de shell. Implica:
- Construyendo la imagen parcialmente, sin volúmenes
- Ejecutar un contenedor con volúmenes , realizar cambios, luego confirmar el resultado y reemplazar el nombre de la imagen original.
Con cambios relativamente menores, el paso adicional agrega solo unos segundos al tiempo de construcción.
Básicamente:
docker build -t image-name . # your normal docker build
# Now run a command in a throwaway container that uses volumes and makes changes:
docker run -v /some:/volume --name temp-container image-name /some/post-configure/command
# Replace the original image with the result:
# (reverting CMD to whatever it was, otherwise it will be set to /some/post-configure/command)
docker commit --change="CMD bash" temp-container image-name
# Delete the temporary container:
docker rm temp-container
En mi caso de uso, quiero generar previamente un archivo maven toolchains.xml, pero mis muchas instalaciones JDK están en un volumen que no está disponible hasta el tiempo de ejecución. Algunas de mis imágenes no son compatibles con todos los JDKS, por lo que debo probar la compatibilidad en el momento de la compilación y rellenar toolchains.xml condicionalmente. Tenga en cuenta que no necesito que la imagen sea portátil, no la publico en Docker Hub.
Creo que puede hacer lo que quiere hacer ejecutando la compilación a través de un comando docker que se ejecuta dentro de un contenedor docker. Ver Docker ahora puede ejecutarse en Docker | Docker Blog . Se utilizó una técnica como esta, pero que en realidad accedía a la ventana acoplable externa desde un contenedor, por ejemplo, al explorar cómo crear el contenedor Docker más pequeño posible | Blog de Xebia .
Otro artículo relevante es Optimizing Docker Images | CenturyLink Labs , que explica que si termina descargando cosas durante una compilación, puede evitar que se desperdicie espacio en la imagen final descargando, compilando y eliminando la descarga todo en un solo paso de EJECUTAR.
Es feo, pero logré una apariencia de esto así:
Dockerfile:
FROM foo
COPY ./m2/ /root/.m2
RUN stuff
imageBuild.sh:
docker build . -t barImage
container="$(docker run -d barImage)"
rm -rf ./m2
docker cp "$container:/root/.m2" ./m2
docker rm -f "$container"
Tengo una compilación de Java que descarga el universo en /root/.m2, y lo hice
cada vez
.
imageBuild.sh
copia el contenido de esa carpeta en el host después de la compilación, y
Dockerfile
copia nuevamente en la imagen para la próxima compilación.
Esto es algo como cómo funcionaría un volumen (es decir, persiste entre compilaciones).
Hay una manera de montar un volumen durante una compilación, pero no involucra Dockerfiles.
La técnica sería
crear un contenedor
desde cualquier base que quisiera usar (montar sus volúmenes en el contenedor con la opción
-v
), ejecutar un script de shell para hacer su trabajo de creación de imágenes, luego
confirmar el contenedor
como una imagen cuando termine.
Esto no solo dejará de lado el exceso de archivos que no desea (también es bueno para archivos seguros, como los archivos SSH), sino que también crea una sola imagen. Tiene desventajas: el comando commit no admite todas las instrucciones de Dockerfile, y no le permite retomarlo cuando lo dejó si necesita editar su script de compilación.
ACTUALIZAR:
Por ejemplo,
CONTAINER_ID=$(docker run -dit ubuntu:16.04)
docker cp build.sh $CONTAINER_ID:/build.sh
docker exec -t $CONTAINER_ID /bin/sh -c ''/bin/sh /build.sh''
docker commit $CONTAINER_ID $REPO:$TAG
docker stop $CONTAINER_ID
No es posible usar la instrucción
VOLUME
para decirle a Docker
qué
montar.
Eso seriamente rompería la portabilidad.
Esta instrucción le dice a Docker que el contenido de esos directorios no va en imágenes y se puede acceder desde otros contenedores utilizando el
--volumes-from
línea de comando
--volumes-from
.
Debe ejecutar el contenedor utilizando
-v /path/on/host:/path/in/container
para acceder a los directorios desde el host.
No es posible montar volúmenes de host durante la compilación. No hay una construcción privilegiada y el montaje del host también degradaría seriamente la portabilidad. Es posible que desee intentar usar wget o curl para descargar lo que necesite para la compilación y ponerlo en su lugar.
Primero, para responder "¿por qué no funciona
VOLUME
?"
Cuando define un
VOLUME
en el Dockerfile, solo puede definir el objetivo, no la fuente del volumen.
Durante la compilación, solo obtendrá un volumen anónimo de esto.
Ese volumen anónimo se montará en cada comando
RUN
, se rellenará previamente con el contenido de la imagen y luego se descartará al final del comando
RUN
.
Solo se guardan los cambios en el contenedor, no los cambios en el volumen.
Desde que se hizo esta pregunta, se han lanzado algunas características que pueden ayudar. Primero, las compilaciones de varias etapas que le permiten construir una primera etapa ineficiente en el espacio del disco y copiar solo la salida necesaria a la etapa final que envía. Y la segunda característica es Buildkit, que está cambiando drásticamente la forma en que se crean las imágenes y se agregan nuevas capacidades a la construcción.
Para una compilación de varias etapas, tendría varias líneas
FROM
, cada una de las cuales comenzaría la creación de una imagen separada.
Solo la última imagen está etiquetada de forma predeterminada, pero puede copiar archivos de etapas anteriores.
El uso estándar es tener un entorno de compilador para construir un artefacto de aplicación binario u otro, y un entorno de tiempo de ejecución como la segunda etapa que copia sobre ese artefacto.
Podrías tener:
FROM debian:sid as builder
COPY export /export
RUN compile command here >/result.bin
FROM debian:sid
COPY --from=builder /result.bin /result.bin
CMD ["/result.bin"]
Eso daría como resultado una compilación que solo contiene el binario resultante, y no el directorio completo / exportar.
Buildkit sale de experimental en 18.09.
Es un rediseño completo del proceso de compilación, incluida la capacidad de cambiar el analizador frontend.
Uno de esos cambios en el analizador ha implementado la opción
RUN --mount
que le permite montar un directorio de caché para sus comandos de ejecución.
Por ejemplo, aquí hay uno que monta algunos de los directorios de Debian (con una reconfiguración de la imagen de Debian, esto podría acelerar la reinstalación de paquetes):
# syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/var/lib/apt/lists,type=cache /
--mount=target=/var/cache/apt,type=cache /
apt-get update /
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends /
git
Debería ajustar el directorio de caché para cualquier caché de aplicaciones que tenga, por ejemplo, $ HOME / .m2 para maven, o /root/.cache para golang.
TL; DR: la respuesta está aquí:
con esa sintaxis
RUN --mount
, también puede enlazar directorios de solo lectura de montaje desde el contexto de compilación.
La carpeta debe existir en el contexto de compilación y no está asignada de nuevo al host o al cliente de compilación:
# syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/export,type=bind,source=export /
process export directory here...
Tenga en cuenta que debido a que el directorio está montado desde el contexto, también está montado de solo lectura, y no puede enviar los cambios al host o al cliente.
Cuando compile, querrá una instalación de 18.09 o más reciente y habilitará el kit de compilación con
export DOCKER_BUILDKIT=1
.