Docker: ¿cómo puedo copiar un archivo de una imagen a un host?

117

Mi pregunta está relacionada con esta pregunta sobre cómo copiar archivos de contenedores a hosts; Tengo un Dockerfile que busca dependencias, compila un artefacto de compilación desde la fuente y ejecuta un ejecutable. También quiero copiar el artefacto de compilación (en mi caso, es .zipproducido por sbt disten '../ target / `, pero creo que esta pregunta también se aplica a jar, binarios, etc.

docker cptrabaja en contenedores, no en imágenes; ¿Necesito iniciar un contenedor solo para sacar un archivo? En un script, intenté ejecutarlo /bin/bashen modo interactivo en segundo plano, copiando el archivo y luego matando el contenedor, pero esto parece tonto. ¿Existe una forma mejor?

Por otro lado, me gustaría evitar desempaquetar un .tararchivo después de ejecutarlo docker save $IMAGENAMEsolo para sacar un archivo (pero esa parece la opción más simple, aunque más lenta, en este momento).

Usaría volúmenes de docker, por ejemplo:

docker run -v hostdir:out $IMAGENAME /bin/cp/../blah.zip /out

pero estoy corriendo boot2dockeren OSX y no sé cómo escribir directamente en mi sistema de archivos de host mac (los volúmenes de lectura y escritura se montan dentro de mi máquina virtual boot2docker, lo que significa que no puedo compartir fácilmente un script para extraer blah.zipde una imagen con otros ¿Pensamientos?

marca
fuente

Respuestas:

172

Para copiar un archivo de una imagen, cree un contenedor temporal, copie el archivo y luego elimínelo:

id=$(docker create image-name)
docker cp $id:path - > local-tar-file
docker rm -v $id
Igor Bukanov
fuente
¿Qué versión de la ventana acoplable fue el createcomando agregado / eliminado (no está presente en 1.01)
ThorSummoner
2
@ThorSummoner docker createse introdujo en Docker
Igor Bukanov
1
No sé por qué esto no está seleccionado como la respuesta correcta.
CentAu
1
Definitivamente la respuesta correcta !!! No depende de nada dentro del contenedor ... ¡Para las imágenes scratch de Golang, esta es la única forma posible!
Marcello de Sales
2
¿Alguna razón para copiar a stdout y luego dirigirlo a un archivo local? cuando hice esto, arrojó un montón de caracteres de control antes y después del contenido del archivo. ejecutarlo directamente como docker cp $id:path > local-tar-filefuncionó perfectamente.
Yonatan
62

Desafortunadamente, no parece haber una forma de copiar archivos directamente desde las imágenes de Docker. Primero debe crear un contenedor y luego copiar el archivo del contenedor.

Sin embargo, si su imagen contiene un catcomando (y lo hará en muchos casos), puede hacerlo con un solo comando:

docker run --rm --entrypoint cat yourimage  /path/to/file > path/to/destination

Si su imagen no contiene cat, simplemente cree un contenedor y use el docker cpcomando como se sugiere en la respuesta de Igor.

fons
fuente
1
Fantástica solución. No pude acceder a mi contenedor porque se bloqueó un segundo después del lanzamiento, pero necesitaba tomar un archivo dentro de él. Esto funcionó perfectamente.
Mirodinho
23

Una opción mucho más rápida es copiar el archivo del contenedor en ejecución a un volumen montado:

docker run -v $PWD:/opt/mount --rm --entrypoint cp image:version /data/libraries.tgz /opt/mount/libraries.tgz

0m0.446s reales

** VS **

docker run --rm --entrypoint cat image:version /data/libraries.tgz > libraries.tgz

0m9.014s reales

Mikko
fuente
Esto probablemente tenga más que ver con el sistema de archivos subyacente que realiza una copia diferida / superficial del archivo (piense en copiar al escribir) en el primer ejemplo, en lugar de copiar realmente los bytes del archivo en el segundo ejemplo. Una prueba útil sería ver si cat a >bvs cp a btienen tiempos similares a los que se muestran aquí. Además, si la ruta de origen y la ruta de destino residen en diferentes sistemas de archivos, ambos ejemplos conducirán a una copia completa byte por byte.
KevinOrr
14

El comentario de los padres ya mostró cómo usar cat . También puede usar alquitrán de manera similar:

docker run yourimage tar -c -C /my/directory subfolder | tar x
wedesoft
fuente
1
Esta respuesta es copiar directorios en lugar de archivos como se pregunta en la pregunta original. Sin embargo, +1 porque también funciona con archivos y viene con una característica adicional: permiso y conservación del propietario. ¡Excelente!
Caligari
5
De hecho, yo usodocker run --rm --entrypoint tar _image_ cC _img_directory_ . | tar xvC _host_directory_
caligari
6
docker cp $(docker create registry.example.com/ansible-base:latest):/home/ansible/.ssh/id_rsa ./hacked_ssh_key

quería suministrar una solución de una línea

editar: el contenedor ni siquiera tiene que ejecutarse en esta solución

Elytscha Smith
fuente
1

Otra respuesta (corta) a este problema:

docker run -v $PWD:/opt/mount --rm -ti image:version bash -c "cp /source/file /opt/mount/"
freedev
fuente
0

Estoy usando boot2docker en MacOS. Puedo asegurarles que los scripts basados ​​en "docker cp" son portátiles. Debido a que cualquier comando se transmite dentro de boot2docker, pero luego la secuencia binaria se transmite de nuevo al cliente de línea de comandos de Docker que se ejecuta en su Mac. Por lo tanto, las operaciones de escritura desde el cliente de la ventana acoplable se ejecutan dentro del servidor y se vuelven a escribir en la instancia del cliente en ejecución.

Estoy compartiendo un script de respaldo para los volúmenes de la ventana acoplable con cualquier contenedor de la ventana acoplable que proporcione y mis scripts de respaldo se prueban tanto en Linux como en MacOS con boot2docker. Las copias de seguridad se pueden intercambiar fácilmente entre plataformas. Básicamente, estoy ejecutando el siguiente comando dentro de mi script:

docker run --name=bckp_for_volume --rm --volumes-from jenkins_jenkins_1 -v /Users/github/jenkins/backups:/backup busybox tar cf /backup/JenkinsBackup-2015-07-09-14-26-15.tar /jenkins

Ejecuta un nuevo contenedor busybox y monta el volumen de mi contenedor jenkins con el nombre jenkins_jenkins_1. Todo el volumen se escribe en las copias de seguridad de archivos / JenkinsBackup-2015-07-09-14-26-15.tar

Ya moví archivos entre el contenedor de Linux y mi contenedor de mac sin ningún ajuste en el script de copia de seguridad o restauración. Si esto es lo que desea, encontrará el script completo y un tutorial aquí: blacklabelops / jenkins

blacklabelops
fuente
0

Puede vincular una ruta local en el host a una ruta en el contenedor, y luego cplos archivos deseados a esa ruta al final de su script.

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

Entonces no es necesario copiar después.

Ryanjdillon
fuente