Copiar archivos del contenedor Docker al host

1709

Estoy pensando en usar Docker para construir mis dependencias en un servidor de Integración Continua (CI), para no tener que instalar todos los tiempos de ejecución y bibliotecas en los propios agentes.

Para lograr esto, necesitaría copiar los artefactos de compilación que se construyen dentro del contenedor nuevamente en el host. ¿Es eso posible?

usuario2668128
fuente
Puede que les guste mi método hacker aquí: stackoverflow.com/a/55876794/990618
colin lamarre
1
La respuesta correcta y real del capitán del acoplador en la parte inferior de las respuestas.
burtsevyg

Respuestas:

2951

Para copiar un archivo de un contenedor al host, puede usar el comando

docker cp <containerId>:/file/path/within/container /host/path/target

Aquí hay un ejemplo:

$ sudo docker cp goofy_roentgen:/out_read.jpg .

Aquí goofy_roentgen es el nombre del contenedor que obtuve del siguiente comando:

$ sudo docker ps

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                                            NAMES
1b4ad9311e93        bamos/openface      "/bin/bash"         33 minutes ago      Up 33 minutes       0.0.0.0:8000->8000/tcp, 0.0.0.0:9000->9000/tcp   goofy_roentgen

También puede usar (parte de) la ID del contenedor . El siguiente comando es equivalente al primero.

$ sudo docker cp 1b4a:/out_read.jpg .
crujir
fuente
42
Aquí es una forma práctica de conseguir en su última contenedor si simplemente usando cargador de muelle para un entorno Linux temp: docker ps -alq.
Josh Habdas
37
este comando cp también funciona tal como está para copiar árboles de directorios (no solo un solo archivo).
ecoe
88
En las versiones más recientes de Docker, puede copiar bidireccionalmente (host a contenedor o contenedor a host) condocker cp ...
Freedom_Ben
99
Necesitaba docker cp -Lcopiar enlaces simbólicos
Harrison Powers
24
NOTA: el contenedor no tiene que estar ejecutándose para usar el comando cp. Práctico si su contenedor se bloquea constantemente.
Martlark
220

No necesitas usar docker run.

Puedes hacerlo con docker create.

De los documentos :

El docker createcomando crea una capa de contenedor grabable sobre la imagen especificada y la prepara para ejecutar el comando especificado. La identificación del contenedor se imprime en STDOUT. Esto es similar a docker run -dexcepto que el contenedor nunca se inicia.

Entonces, puedes hacer:

docker create -ti --name dummy IMAGE_NAME bash
docker cp dummy:/path/to/file /dest/to/file
docker rm -f dummy

Aquí, nunca comienzas el contenedor. Eso me pareció beneficioso.

Ishan Bhatt
fuente
19
Esto necesita más votos a favor. Ideal para cuando solo necesita construir algo en un contenedor y luego copiar los resultados.
Honza Kalfus el
44
@HonzaKalfus Estoy de acuerdo en que esto debe ser más alto. Esto es exactamente lo que estaba buscando. Utilicé esto para poder construir algunos archivos binarios usando un entorno conocido (Amazon Linux en una versión específica). ¡pude hacer un script de shell que construyó completamente el docker y extrajo el binario resultante de él! Perfecto.
Marcar el
1
¿Se -tirequiere y se bashrequiere?
jII
@jII, lo había hecho porque más tarde, docker lo ejecuté. En casos simples, no es necesario, pero tampoco hace daño aquí.
Ishan Bhatt
¿De alguna manera es posible usar comodines? Quiero decir ... No sé el nombre exacto del archivo que necesito copiar porque tiene un número de versión.
juzzlin
87

Monta un "volumen" y copia los artefactos allí:

mkdir artifacts
docker run -i -v ${PWD}/artifacts:/artifacts ubuntu:14.04 sh << COMMANDS
# ... build software here ...
cp <artifact> /artifacts
# ... copy more artifacts into `/artifacts` ...
COMMANDS

Luego, cuando finaliza la compilación y el contenedor ya no se está ejecutando, ya ha copiado los artefactos de la compilación en el artifactsdirectorio del host.

Editar

Advertencia: cuando hace esto, puede encontrarse con problemas con la identificación del usuario de la ventana acoplable que coincide con la identificación del usuario actual. Es decir, los archivos /artifactsse mostrarán como propiedad del usuario con el UID del usuario utilizado dentro del contenedor acoplable. Una forma de evitar esto puede ser usar el UID del usuario llamante:

docker run -i -v ${PWD}:/working_dir -w /working_dir -u $(id -u) \
    ubuntu:14.04 sh << COMMANDS
# Since $(id -u) owns /working_dir, you should be okay running commands here
# and having them work. Then copy stuff into /working_dir/artifacts .
COMMANDS
djhaskin987
fuente
77
En realidad, puede usar el chowncomando para hacer coincidir la identificación del usuario y la identificación del grupo en la máquina host.
Dimchansky
@Frondor Consulte la referencia de configuración de volumen docs.docker.com/compose/compose-file/…
djhaskin987
Ya lo hice, y eso no funcionará. Una vez que el contenedor copió archivos al volumen por primera vez, la próxima vez, el volumen ya no está vacío y los archivos más nuevos no anulan los archivos. El contenedor está dando prioridad a los archivos host (los que se copiaron la primera vez que montó la imagen del contenedor).
Frondor
suena como algo que podría ser su propia pregunta SO @Frondor
djhaskin987
1
Te estoy comprando un compañero de cerveza! ¡Gracias!
Dimitar Vukman
27

Monta un volumen, copia los artefactos, ajusta la identificación del propietario y la identificación del grupo:

mkdir artifacts
docker run -i --rm -v ${PWD}/artifacts:/mnt/artifacts centos:6 /bin/bash << COMMANDS
ls -la > /mnt/artifacts/ls.txt
echo Changing owner from \$(id -u):\$(id -g) to $(id -u):$(id -u)
chown -R $(id -u):$(id -u) /mnt/artifacts
COMMANDS
Dimchansky
fuente
24

TLDR;

$ docker run --rm -iv${PWD}:/host-volume my-image sh -s <<EOF
chown $(id -u):$(id -g) my-artifact.tar.xz
cp -a my-artifact.tar.xz /host-volume
EOF

Descripción

docker runcon un volumen de host, chownel artefacto, cpel artefacto al volumen de host:

$ docker build -t my-image - <<EOF
> FROM busybox
> WORKDIR /workdir
> RUN touch foo.txt bar.txt qux.txt
> EOF
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM busybox
 ---> 00f017a8c2a6
Step 2/3 : WORKDIR /workdir
 ---> Using cache
 ---> 36151d97f2c9
Step 3/3 : RUN touch foo.txt bar.txt qux.txt
 ---> Running in a657ed4f5cab
 ---> 4dd197569e44
Removing intermediate container a657ed4f5cab
Successfully built 4dd197569e44

$ docker run --rm -iv${PWD}:/host-volume my-image sh -s <<EOF
chown -v $(id -u):$(id -g) *.txt
cp -va *.txt /host-volume
EOF
changed ownership of '/host-volume/bar.txt' to 10335:11111
changed ownership of '/host-volume/qux.txt' to 10335:11111
changed ownership of '/host-volume/foo.txt' to 10335:11111
'bar.txt' -> '/host-volume/bar.txt'
'foo.txt' -> '/host-volume/foo.txt'
'qux.txt' -> '/host-volume/qux.txt'

$ ls -n
total 0
-rw-r--r-- 1 10335 11111 0 May  7 18:22 bar.txt
-rw-r--r-- 1 10335 11111 0 May  7 18:22 foo.txt
-rw-r--r-- 1 10335 11111 0 May  7 18:22 qux.txt

Este truco funciona porque la chowninvocación dentro del heredoc toma $(id -u):$(id -g)valores desde fuera del contenedor en ejecución; es decir, el host docker.

Los beneficios son:

  • no tienes que hacerlo docker container run --nameo docker container create --nameantes
  • no tienes que docker container rmdespués
rubicks
fuente
2
Votó a favor de la comparación entre cpy las respuestas basadas en el volumen. Además, para el idtruco de la propiedad, a veces es un verdadero dolor de cabeza
Marc Ghorayeb el
18

La mayoría de las respuestas no indican que el contenedor debe ejecutarse antes de docker cpque funcione:

docker build -t IMAGE_TAG .
docker run -d IMAGE_TAG
CONTAINER_ID=$(docker ps -alq)
# If you do not know the exact file name, you'll need to run "ls"
# FILE=$(docker exec CONTAINER_ID sh -c "ls /path/*.zip")
docker cp $CONTAINER_ID:/path/to/file .
docker stop $CONTAINER_ID
cmcginty
fuente
3
Por cierto, si el contenedor debe / puede estar ejecutándose / detenido / o parece depender del tipo de host / técnica de virtualización . El documento actual de docker dice "El CONTENEDOR puede ser un contenedor en ejecución o detenido". Múltiples lugares en SO, incluido un comentario sobre la respuesta aceptada, dicen "esto también funciona en un contenedor detenido". Bajo Windows Hyper-V, aparentemente es necesaria para detener contenedor antes de copiar un archivo .
ToolmakerSteve
La copia también funciona cuando se detiene el contenedor.
Luke W
17

Si no tiene un contenedor en ejecución, solo una imagen, y suponiendo que desea copiar solo un archivo de texto, puede hacer algo como esto:

docker run the-image cat path/to/container/file.txt > path/to/host/file.txt
cancerbero
fuente
7

Estoy publicando esto para cualquiera que esté usando Docker para Mac. Esto es lo que funcionó para mí:

 $ mkdir mybackup # local directory on Mac

 $ docker run --rm --volumes-from <containerid> \
    -v `pwd`/mybackup:/backup \  
    busybox \                   
    cp /data/mydata.txt /backup 

Tenga en cuenta que cuando monto usando -vese backupdirectorio se crea automáticamente.

Espero que esto sea útil para alguien algún día. :)

Pablo
fuente
Si usa docker-compose, los volúmenes desde quedan en desuso en la versión 3 y posteriores.
mulg0r
Para agregar al comentario de mulg0r, consulte stackoverflow.com/a/45495380/199364 - en la v.3, coloca un volumescomando en la raíz de config.yml, para que varios contenedores puedan acceder a los volúmenes.
ToolmakerSteve
5

Si solo desea extraer un archivo de una imagen (en lugar de un contenedor en ejecución), puede hacer esto:

docker run --rm <image> cat <source> > <local_dest>

Esto abrirá el contenedor, escribirá el nuevo archivo y luego eliminará el contenedor. Sin embargo, un inconveniente es que los permisos del archivo y la fecha de modificación no se conservarán.

sg
fuente
5

Con el lanzamiento de Docker 19.03, puede omitir la creación del contenedor e incluso crear una imagen. Hay una opción con las compilaciones basadas en BuildKit para cambiar el destino de salida. Puede usar esto para escribir los resultados de la compilación en su directorio local en lugar de en una imagen. Por ejemplo, aquí hay una compilación de un binario go:

$ ls
Dockerfile  go.mod  main.go

$ cat Dockerfile
FROM golang:1.12-alpine as dev
RUN apk add --no-cache git ca-certificates
RUN adduser -D appuser
WORKDIR /src
COPY . /src/
CMD CGO_ENABLED=0 go build -o app . && ./app

FROM dev as build
RUN CGO_ENABLED=0 go build -o app .
USER appuser
CMD [ "./app" ]

FROM scratch as release
COPY --from=build /etc/passwd /etc/group /etc/
COPY --from=build /src/app /app
USER appuser
CMD [ "/app" ]

FROM scratch as artifact
COPY --from=build /src/app /app

FROM release

Desde el Dockerfile anterior, estoy construyendo el artifactescenario que solo incluye los archivos que quiero exportar. Y la nueva --outputbandera introducida me permite escribirlos en un directorio local en lugar de una imagen. Esto debe realizarse con el motor BuildKit que viene con 19.03:

$ DOCKER_BUILDKIT=1 docker build --target artifact --output type=local,dest=. .
[+] Building 43.5s (12/12) FINISHED
 => [internal] load build definition from Dockerfile                                                                              0.7s
 => => transferring dockerfile: 572B                                                                                              0.0s
 => [internal] load .dockerignore                                                                                                 0.5s
 => => transferring context: 2B                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/golang:1.12-alpine                                                             0.9s
 => [dev 1/5] FROM docker.io/library/golang:1.12-alpine@sha256:50deab916cce57a792cd88af3479d127a9ec571692a1a9c22109532c0d0499a0  22.5s
 => => resolve docker.io/library/golang:1.12-alpine@sha256:50deab916cce57a792cd88af3479d127a9ec571692a1a9c22109532c0d0499a0       0.0s
 => => sha256:1ec62c064901392a6722bb47a377c01a381f4482b1ce094b6d28682b6b6279fd 155B / 155B                                        0.3s
 => => sha256:50deab916cce57a792cd88af3479d127a9ec571692a1a9c22109532c0d0499a0 1.65kB / 1.65kB                                    0.0s
 => => sha256:2ecd820bec717ec5a8cdc2a1ae04887ed9b46c996f515abc481cac43a12628da 1.36kB / 1.36kB                                    0.0s
 => => sha256:6a17089e5a3afc489e5b6c118cd46eda66b2d5361f309d8d4b0dcac268a47b13 3.81kB / 3.81kB                                    0.0s
 => => sha256:89d9c30c1d48bac627e5c6cb0d1ed1eec28e7dbdfbcc04712e4c79c0f83faf17 2.79MB / 2.79MB                                    0.6s
 => => sha256:8ef94372a977c02d425f12c8cbda5416e372b7a869a6c2b20342c589dba3eae5 301.72kB / 301.72kB                                0.4s
 => => sha256:025f14a3d97f92c07a07446e7ea8933b86068d00da9e252cf3277e9347b6fe69 125.33MB / 125.33MB                               13.7s
 => => sha256:7047deb9704134ff71c99791be3f6474bb45bc3971dde9257ef9186d7cb156db 125B / 125B                                        0.8s
 => => extracting sha256:89d9c30c1d48bac627e5c6cb0d1ed1eec28e7dbdfbcc04712e4c79c0f83faf17                                         0.2s
 => => extracting sha256:8ef94372a977c02d425f12c8cbda5416e372b7a869a6c2b20342c589dba3eae5                                         0.1s
 => => extracting sha256:1ec62c064901392a6722bb47a377c01a381f4482b1ce094b6d28682b6b6279fd                                         0.0s
 => => extracting sha256:025f14a3d97f92c07a07446e7ea8933b86068d00da9e252cf3277e9347b6fe69                                         5.2s
 => => extracting sha256:7047deb9704134ff71c99791be3f6474bb45bc3971dde9257ef9186d7cb156db                                         0.0s
 => [internal] load build context                                                                                                 0.3s
 => => transferring context: 2.11kB                                                                                               0.0s
 => [dev 2/5] RUN apk add --no-cache git ca-certificates                                                                          3.8s
 => [dev 3/5] RUN adduser -D appuser                                                                                              1.7s
 => [dev 4/5] WORKDIR /src                                                                                                        0.5s
 => [dev 5/5] COPY . /src/                                                                                                        0.4s
 => [build 1/1] RUN CGO_ENABLED=0 go build -o app .                                                                              11.6s
 => [artifact 1/1] COPY --from=build /src/app /app                                                                                0.5s
 => exporting to client                                                                                                           0.1s
 => => copying files 10.00MB                                                                                                      0.1s

Una vez completada la compilación, appse exportó el binario:

$ ls
Dockerfile  app  go.mod  main.go

$ ./app
Ready to receive requests on port 8080

Docker tiene otras opciones para el --outputindicador documentadas en su repositorio de BuildKit ascendente: https://github.com/moby/buildkit#output

BMitch
fuente
caché de compilación estándar no utilizado para compilación con salida, es malo
burtsevyg
@burtsevyg Buildkit es un generador diferente, que utiliza un entorno de caché diferente. Es mucho más eficiente en caché.
BMitch
gracias, mejoraré mis nodos de compilación.
burtsevyg
4

Como solución más general, hay un complemento de CloudBees para que Jenkins lo construya dentro de un contenedor Docker . Puede seleccionar una imagen para usar desde un registro de Docker o definir un Dockerfile para compilar y usar.

Montará el espacio de trabajo en el contenedor como un volumen (con el usuario apropiado), lo configurará como su directorio de trabajo, realizará los comandos que solicite (dentro del contenedor). También puede usar el complemento docker-workflow (si prefiere el código sobre la interfaz de usuario) para hacer esto, con el comando image.inside () {}.

Básicamente todo esto, integrado en su servidor CI / CD y algo más.

BobMcGee
fuente
4

Usé PowerShell (Admin) con este comando.

docker cp {container id}:{container path}/error.html  C:\\error.html

Ejemplo

docker cp ff3a6608467d:/var/www/app/error.html  C:\\error.html
Khachornchit Songsaen
fuente
2

Otra buena opción es compilar primero el contenedor y luego ejecutarlo utilizando el indicador -c con el intérprete de shell para ejecutar algunos comandos

docker run --rm -i -v <host_path>:<container_path> <mydockerimage> /bin/sh -c "cp -r /tmp/homework/* <container_path>"

El comando anterior hace esto:

-yo = ejecuta el contenedor en modo interactivo

--rm = eliminó el contenedor después de la ejecución.

-v = compartió una carpeta como volumen desde la ruta del host a la ruta del contenedor.

Finalmente, / bin / sh -c le permite introducir un comando como parámetro y ese comando copiará sus archivos de tarea a la ruta del contenedor.

Espero que esta respuesta adicional te pueda ayudar

Yor Jaggy
fuente
1

Cree un directorio de datos en el sistema host (fuera del contenedor) y móntelo en un directorio visible desde el interior del contenedor. Esto coloca los archivos en una ubicación conocida en el sistema host y facilita que las herramientas y aplicaciones en el sistema host accedan a los archivos

docker run -d -v /path/to/Local_host_dir:/path/to/docker_dir docker_image:tag
Anigbo inocente
fuente
44
Eso le permite inyectar un directorio y sus contenidos desde el host al contenedor. No le permite copiar archivos del contenedor de vuelta al host.
BMitch
¿Lo hace si la carpeta del host tiene permisos muy amplios?
giorgiosironi
0

Cree una ruta donde desee copiar el archivo y luego use:

docker run -d -v hostpath:dockerimag
Chandra Pal
fuente
0

Puede usar en bindlugar de volumesi desea montar solo una carpeta, no crear un almacenamiento especial para un contenedor:

  1. Construye tu imagen con la etiqueta:

    docker build . -t <image>

  2. Ejecute su imagen y enlace el directorio $ (pwd) actual donde se almacena app.py y asígnelo a / root / example / dentro de su contenedor.

    docker run --mount type=bind,source="$(pwd)",target=/root/example/ <image> python app.py

zytfo
fuente
0

Esto también se puede hacer en el SDK, por ejemplo, python. Si ya tiene un contenedor integrado, puede buscar el nombre a través de la consola (docker ps -a ). El nombre parece ser una concatenación de un científico y un adjetivo (es decir, "relax_pasteur").

Echa un vistazo a help(container.get_archive):

Help on method get_archive in module docker.models.containers:

get_archive(path, chunk_size=2097152) method of docker.models.containers.Container instance
    Retrieve a file or folder from the container in the form of a tar
    archive.

    Args:
        path (str): Path to the file or folder to retrieve
        chunk_size (int): The number of bytes returned by each iteration
            of the generator. If ``None``, data will be streamed as it is
            received. Default: 2 MB

    Returns:
        (tuple): First element is a raw tar data stream. Second element is
        a dict containing ``stat`` information on the specified ``path``.

    Raises:
        :py:class:`docker.errors.APIError`
            If the server returns an error.

    Example:

        >>> f = open('./sh_bin.tar', 'wb')
        >>> bits, stat = container.get_archive('/bin/sh')
        >>> print(stat)
        {'name': 'sh', 'size': 1075464, 'mode': 493,
         'mtime': '2018-10-01T15:37:48-07:00', 'linkTarget': ''}
        >>> for chunk in bits:
        ...    f.write(chunk)
        >>> f.close()

Entonces, algo como esto se extraerá de la ruta especificada (/ salida) en el contenedor a su máquina host y desempaquetará el tar.

import docker
import os
import tarfile

# Docker client
client = docker.from_env()
#container object
container = client.containers.get("relaxed_pasteur")
#setup tar to write bits to
f = open(os.path.join(os.getcwd(),"output.tar"),"wb")
#get the bits
bits, stat = container.get_archive('/output')
#write the bits
for chunk in bits:
    f.write(chunk)
f.close()
#unpack
tar = tarfile.open("output.tar")
tar.extractall()
tar.close()
John Drinane
fuente