Docker - Comment puis-je copier un fichier d'une image vers un hôte?

118

Ma question est liée à cette question sur la copie de fichiers depuis des conteneurs vers des hôtes; J'ai un Dockerfile qui récupère les dépendances, compile un artefact de construction à partir de la source et exécute un exécutable. Je veux aussi copier l'artefact de construction (dans mon cas, c'est un .zipproduit par sbt distdans '../ target / `, mais je pense que cette question s'applique également aux jars, binaires, etc.

docker cpfonctionne sur des conteneurs, pas sur des images; dois-je démarrer un conteneur juste pour en extraire un fichier? Dans un script, j'ai essayé de courir /bin/bashen mode interactif en arrière-plan, de copier le fichier, puis de tuer le conteneur, mais cela semble kludgey. Y a-t-il un meilleur moyen?

D'un autre côté, je voudrais éviter de décompresser un .tarfichier après l'exécution docker save $IMAGENAMEjuste pour en extraire un (mais cela semble être l'option la plus simple, bien que la plus lente, pour le moment).

J'utiliserais des volumes de docker, par exemple:

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

mais j'exécute sous boot2dockerOSX et je ne sais pas comment écrire directement sur mon système de fichiers hôte mac (les volumes en lecture-écriture sont montés dans ma VM boot2docker, ce qui signifie que je ne peux pas facilement partager un script pour extraire blah.zipd'une image avec d'autres.

marque
la source

Réponses:

172

Pour copier un fichier à partir d'une image, créez un conteneur temporaire, copiez-en le fichier puis supprimez-le:

id=$(docker create image-name)
docker cp $id:path - > local-tar-file
docker rm -v $id
Igor Bukanov
la source
Quelle version de docker la createcommande a-t-elle été ajoutée / supprimée (elle n'est pas présente dans la version 1.01)
ThorSummoner
2
@ThorSummoner a docker createété introduit dans docker 1.3, blog.docker.com/2014/10/…
Igor Bukanov
1
Je ne sais pas pourquoi ce n'est pas la bonne réponse.
CentAu
1
Certainement la bonne réponse !!! Ne repose sur rien à l'intérieur du conteneur ... Pour les images à gratter Golang, c'est le seul moyen possible!
Marcello de Sales
2
une raison de copier sur stdout puis de le diriger vers un fichier local? quand j'ai fait cela, il a vidé un tas de caractères de contrôle avant et après le contenu du fichier. l'exécuter directement comme docker cp $id:path > local-tar-filefonctionnait parfaitement.
Yonatan
62

Malheureusement, il ne semble pas y avoir de moyen de copier des fichiers directement à partir d'images Docker. Vous devez d'abord créer un conteneur, puis copier le fichier à partir du conteneur.

Cependant, si votre image contient une catcommande (et cela fera l'affaire dans de nombreux cas), vous pouvez le faire avec une seule commande:

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

Si votre image ne contient pas cat, créez simplement un conteneur et utilisez la docker cpcommande comme suggéré dans la réponse d'Igor.

fons
la source
1
Solution fantastique. Je n'ai pas pu accéder à mon conteneur car il s'est écrasé une seconde après le lancement, mais il a fallu récupérer un fichier à l'intérieur. Cela a parfaitement fonctionné.
Mirodinho
23

Une option beaucoup plus rapide consiste à copier le fichier du conteneur en cours d'exécution vers un volume monté:

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

réel 0m0.446s

** CONTRE **

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

réel 0m9.014s

Mikko
la source
Cela a probablement plus à voir avec le système de fichiers sous-jacent effectuant une copie paresseuse / superficielle du fichier (pensez à la copie sur écriture) dans le premier exemple, par rapport à la copie en fait des octets du fichier dans le deuxième exemple. Un test utile serait de voir si cat a >bvs cp a bont des horaires similaires, comme indiqué ici. De plus, si le chemin source et le chemin de destination résident sur des systèmes de fichiers différents, les deux exemples conduiront à une copie octet par octet complète.
KevinOrr le
14

Le commentaire d'un parent a déjà montré comment utiliser cat . Vous pouvez également utiliser tar de la même manière:

docker run yourimage tar -c -C /my/directory subfolder | tar x
wedesoft
la source
1
Cette réponse consiste à copier des répertoires à la place des fichiers comme la question d'origine le demande. Cependant, +1 car il fonctionne également avec les fichiers et est livré avec une fonctionnalité supplémentaire: l'autorisation et la préservation du propriétaire. Génial!
caligari
5
En fait, j'utilisedocker run --rm --entrypoint tar _image_ cC _img_directory_ . | tar xvC _host_directory_
caligari
7
docker cp $(docker create registry.example.com/ansible-base:latest):/home/ansible/.ssh/id_rsa ./hacked_ssh_key

voulait fournir une solution à une ligne

edit: le conteneur n'a même pas besoin d'être exécuté dans cette solution

Elytscha Smith
la source
1

Une autre (courte) réponse à ce problème:

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

Mise à jour - comme indiqué par @Elytscha Smith, cela ne fonctionne que si votre image est intégrée à bash

Freedev
la source
1
* une autre réponse courte pour les images contenant l'exécutable bash alpine ne le fait pas par défaut et les images construites à partir de zéro ne le font pas non plus
Elytscha Smith il y a
0

J'utilise boot2docker sur MacOS. Je peux vous assurer que les scripts basés sur "docker cp" sont portables. Parce que toute commande est relayée dans boot2docker mais que le flux binaire est ensuite retransmis au client de ligne de commande docker exécuté sur votre mac. Les opérations d'écriture depuis le client docker sont donc exécutées à l'intérieur du serveur et réécrites sur l'instance client en cours d'exécution!

Je partage un script de sauvegarde pour les volumes Docker avec n'importe quel conteneur Docker que je fournis et mes scripts de sauvegarde sont testés à la fois sur Linux et MacOS avec boot2docker. Les sauvegardes peuvent être facilement échangées entre les plates-formes. En gros, j'exécute la commande suivante dans mon 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

Exécute un nouveau conteneur busybox et monte le volume de mon conteneur jenkins avec le nom jenkins_jenkins_1. L'ensemble du volume est écrit dans le fichier backups / JenkinsBackup-2015-07-09-14-26-15.tar

J'ai déjà déplacé des archives entre le conteneur Linux et mon conteneur Mac sans aucun ajustement du script de sauvegarde ou de restauration. Si c'est ce que vous voulez, vous trouverez l'ensemble du script un tutoriel ici: blacklabelops / jenkins

blacklabelops
la source
0

Vous pouvez lier un chemin local sur l'hôte à un chemin sur le conteneur, puis cple (s) fichier (s) souhaité (s) à ce chemin à la fin de votre script.

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

Ensuite, il n'est pas nécessaire de copier par la suite.

Ryanjdillon
la source