Copie de fichiers du conteneur Docker vers l'hôte

1709

Je pense utiliser Docker pour créer mes dépendances sur un serveur d'intégration continue (CI), afin de ne pas avoir à installer tous les runtimes et bibliothèques sur les agents eux-mêmes.

Pour y parvenir, je devrais copier les artefacts de construction qui sont construits à l'intérieur du conteneur dans l'hôte. Est-ce possible?

user2668128
la source
vous aimerez peut-être ma méthode de piratage ici: stackoverflow.com/a/55876794/990618
colin lamarre
1
La réponse correcte et réelle du capitaine docker au bas des réponses.
burtsevyg

Réponses:

2951

Pour copier un fichier d'un conteneur vers l'hôte, vous pouvez utiliser la commande

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

Voici un exemple:

$ sudo docker cp goofy_roentgen:/out_read.jpg .

Ici goofy_roentgen est le nom du conteneur que j'ai obtenu de la commande suivante:

$ 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

Vous pouvez également utiliser (une partie de) l' ID de conteneur . La commande suivante est équivalente à la première

$ sudo docker cp 1b4a:/out_read.jpg .
creack
la source
42
Voici un moyen pratique pour obtenir votre dernier conteneur si vous êtes tout simplement à l' aide docker pour une température environnement Linux: docker ps -alq.
Josh Habdas
37
cette commande cp fonctionne aussi telle quelle pour copier les arborescences de répertoires (pas seulement un seul fichier).
ecoe
88
Dans les nouvelles versions de docker, vous pouvez copier bidirectionnellement (hôte vers conteneur ou conteneur vers hôte) avecdocker cp ...
Freedom_Ben
9
J'avais besoin docker cp -Lde copier des liens symboliques
Harrison Powers
24
REMARQUE: il n'est pas nécessaire que le conteneur soit en cours d'exécution pour utiliser la commande cp. Pratique si votre conteneur se bloque constamment.
Martlark
220

Vous n'avez pas besoin d'utiliser docker run.

Vous pouvez le faire avec docker create.

De la documentation :

La docker createcommande crée une couche conteneur inscriptible sur l'image spécifiée et la prépare pour l'exécution de la commande spécifiée. L'ID du conteneur est ensuite imprimé sur STDOUT. Ceci est similaire au fait docker run -dque le conteneur n'est jamais démarré.

Vous pouvez donc:

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

Ici, vous ne démarrez jamais le conteneur. Cela m'a paru bénéfique.

Ishan Bhatt
la source
19
Cela nécessite plus de votes positifs. Idéal lorsque vous avez juste besoin de créer quelque chose dans un conteneur, puis de copier les sorties.
Honza Kalfus
4
@HonzaKalfus Je suis d'accord que cela doit être plus élevé. C'est exactement ce que je recherchais. Je l'ai utilisé pour pouvoir créer des fichiers binaires en utilisant un environnement connu (amazon linux dans une version spécifique). a pu créer un script shell qui a entièrement construit le docker et en a extrait le binaire résultant! Parfait.
Mark
1
Est -tirequis et bashrequis?
juillet
@jII, je l'avais fait parce que plus tard, je fais du docker dessus. Dans les cas simples, ce n'est pas nécessaire mais cela ne fait pas de mal ici aussi.
Ishan Bhatt
Est-il possible d'utiliser des caractères génériques d'une manière ou d'une autre? Je veux dire ... Je ne connais pas le nom exact du fichier que je dois copier car il y a un numéro de version dessus.
juzzlin
87

Montez un "volume" et copiez-y les artefacts:

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

Ensuite, lorsque la génération se termine et que le conteneur n'est plus en cours d'exécution, il a déjà copié les artefacts de la génération dans le artifactsrépertoire de l'hôte.

Éditer

Avertissement: lorsque vous effectuez cette opération, vous pouvez rencontrer des problèmes avec l'ID utilisateur de l'utilisateur docker correspondant à l'ID utilisateur de l'utilisateur en cours d'exécution. Autrement dit, les fichiers dans /artifactsseront affichés comme appartenant à l'utilisateur avec l'UID de l'utilisateur utilisé dans le conteneur Docker. Un moyen de contourner cela peut être d'utiliser l'UID de l'utilisateur appelant:

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
la source
7
En fait, vous pouvez utiliser la chowncommande pour faire correspondre l'ID utilisateur et l'ID de groupe sur la machine hôte.
Dimchansky
@Frondor Voir la référence de configuration de volume docs.docker.com/compose/compose-file/…
djhaskin987
Déjà fait, et cela ne fonctionnera pas. Une fois que le conteneur a copié les fichiers sur le volume pour la première fois, la prochaine fois, le volume n'est plus vide et les fichiers ne sont pas remplacés par les plus récents. Le conteneur donne la priorité aux fichiers hôtes (ceux copiés la première fois que vous avez monté l'image du conteneur).
Frondor
sonne comme quelque chose qui pourrait être sa propre question SO @Frondor
djhaskin987
1
Je t'achète une bière! Merci!
Dimitar Vukman
27

Montez un volume, copiez les artefacts, ajustez l'ID du propriétaire et l'ID du groupe:

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
la source
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

La description

docker runavec un volume hôte, chownl'artefact, cpl'artefact au volume hôte:

$ 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

Cette astuce fonctionne parce que l' chowninvocation dans l' hérédoc prend des $(id -u):$(id -g)valeurs à l'extérieur du conteneur en cours d'exécution; c'est-à-dire l'hôte docker.

Les avantages sont les suivants:

  • vous n'êtes pas obligé docker container run --nameou docker container create --nameavant
  • vous n'avez pas docker container rmaprès
rubicks
la source
2
Surévalué pour la comparaison entre les cpréponses basées sur le volume. Aussi, pour l' idastuce pour la propriété, c'est parfois un vrai casse-tête
Marc Ghorayeb
18

La plupart des réponses n'indiquent pas que le conteneur doit fonctionner avant de docker cpfonctionner:

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
la source
3
BTW, si le conteneur doit / peut être en cours d'exécution / arrêté / l'un ou l'autre semble dépendre du type d'hôte / de la virtualisation-technique . Le document Docker actuel indique que "le CONTENEUR peut être un conteneur en cours d'exécution ou arrêté". Plusieurs endroits sur SO, y compris un commentaire sur la réponse acceptée, disent "cela fonctionne aussi sur un conteneur arrêté". Sous Windows Hyper-V, il est apparemment nécessaire d' arrêter le conteneur avant de copier un fichier .
ToolmakerSteve
La copie fonctionne également lorsque le conteneur est arrêté.
Luke W
17

Si vous n'avez pas de conteneur en cours d'exécution, juste une image, et en supposant que vous souhaitez copier uniquement un fichier texte, vous pouvez faire quelque chose comme ceci:

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

Je poste ceci pour toute personne utilisant Docker pour Mac. C'est ce qui a fonctionné pour moi:

 $ mkdir mybackup # local directory on Mac

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

Notez que lorsque je monte en utilisant -vce backuprépertoire est automatiquement créé.

J'espère que cela sera utile à quelqu'un un jour. :)

Paul
la source
Si vous utilisez docker-compose, volumes-from est déconseillé dans la version 3 et les versions ultérieures.
mulg0r
Pour ajouter au commentaire de mulg0r, voir stackoverflow.com/a/45495380/199364 - dans la v.3, vous placez une volumescommande à la racine de config.yml, pour que les volumes soient accessibles par plusieurs conteneurs.
ToolmakerSteve
5

Si vous souhaitez simplement extraire un fichier d'une image (au lieu d'un conteneur en cours d'exécution), vous pouvez le faire:

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

Cela fera apparaître le conteneur, écrit le nouveau fichier, puis supprimera le conteneur. Un inconvénient, cependant, est que les autorisations de fichier et la date de modification ne seront pas conservées.

sg
la source
5

Avec la sortie de Docker 19.03, vous pouvez ignorer la création du conteneur et même la création d'une image. Il y a une option avec les builds basés sur BuildKit pour changer la destination de sortie. Vous pouvez l'utiliser pour écrire les résultats de la construction dans votre répertoire local plutôt que dans une image. Par exemple, voici une construction d'un go binaire:

$ 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

À partir du Dockerfile ci-dessus, je construis la artifactscène qui inclut uniquement les fichiers que je souhaite exporter. Et le --outputdrapeau nouvellement introduit me permet de les écrire dans un répertoire local au lieu d'une image. Cela doit être effectué avec le moteur BuildKit fourni avec 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

Une fois la construction terminée, le appbinaire a été exporté:

$ ls
Dockerfile  app  go.mod  main.go

$ ./app
Ready to receive requests on port 8080

Docker a d'autres options pour l' --outputindicateur documentées dans leur référentiel BuildKit en amont: https://github.com/moby/buildkit#output

BMitch
la source
cache de build standard non utilisé pour la build avec sortie, c'est mauvais
burtsevyg
@burtsevyg Buildkit est un générateur différent, utilisant un environnement de cache différent. C'est beaucoup plus efficace pour le cache.
BMitch
merci je vais améliorer mes nœuds de build.
burtsevyg
4

En tant que solution plus générale, il existe un plugin CloudBees pour Jenkins à construire dans un conteneur Docker . Vous pouvez sélectionner une image à utiliser dans un registre Docker ou définir un Dockerfile à créer et à utiliser.

Il montera l'espace de travail dans le conteneur en tant que volume (avec l'utilisateur approprié), le définira comme votre répertoire de travail, exécutera toutes les commandes que vous demandez (à l'intérieur du conteneur). Vous pouvez également utiliser le plugin docker-workflow (si vous préférez le code à l'interface utilisateur) pour ce faire, avec la commande image.inside () {}.

Fondamentalement, tout cela, intégré à votre serveur CI / CD, puis certains.

BobMcGee
la source
4

J'ai utilisé PowerShell (Admin) avec cette commande.

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

Exemple

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

Une autre bonne option est tout d'abord de construire le conteneur puis de l'exécuter en utilisant l'indicateur -c avec l'interpréteur de shell pour exécuter certaines virgules

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

La commande ci-dessus fait ceci:

-i = exécuter le conteneur en mode interactif

--rm = a supprimé le conteneur après l'exécution.

-v = a partagé un dossier en tant que volume de votre chemin d'hôte vers le chemin du conteneur.

Enfin, / bin / sh -c vous permet d'introduire une commande en tant que paramètre et cette commande copiera vos fichiers de devoirs dans le chemin du conteneur.

J'espère que cette réponse supplémentaire pourra vous aider

Yor Jaggy
la source
1

Créez un répertoire de données sur le système hôte (en dehors du conteneur) et montez-le dans un répertoire visible de l'intérieur du conteneur. Cela place les fichiers dans un emplacement connu sur le système hôte et permet aux outils et applications du système hôte d'accéder facilement aux fichiers.

docker run -d -v /path/to/Local_host_dir:/path/to/docker_dir docker_image:tag
Innocent Anigbo
la source
4
Cela vous permet d'injecter un répertoire et son contenu depuis l'hôte dans le conteneur. Il ne vous permet pas de copier des fichiers du conteneur vers l'hôte.
BMitch
Il le fait si le dossier hôte a des autorisations très larges?
giorgiosironi
0

Créez un chemin où vous souhaitez copier le fichier, puis utilisez:

docker run -d -v hostpath:dockerimag
Chandra Pal
la source
0

Vous pouvez utiliser à la bindplace de volumesi vous souhaitez monter un seul dossier, et non créer un stockage spécial pour un conteneur:

  1. Construisez votre image avec le tag:

    docker build . -t <image>

  2. Exécutez votre image et liez le répertoire $ (pwd) actuel où app.py stocke et mappez-le à / root / example / à l'intérieur de votre conteneur.

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

zytfo
la source
0

Cela peut également être fait dans le SDK par exemple python. Si vous avez déjà construit un conteneur, vous pouvez rechercher le nom via console ( docker ps -a). Le nom semble être une concaténation d'un scientifique et d'un adjectif (c'est-à-dire "relax_pasteur").

Découvrez 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()

Donc, quelque chose comme ça va sortir du chemin spécifié (/ sortie) dans le conteneur vers votre machine hôte et décompresser le 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
la source