Докер - как скопировать файл из образа на хост?

120

Мой вопрос связан с этим вопросом о копировании файлов из контейнеров на хосты; У меня есть Dockerfile, который извлекает зависимости, компилирует артефакт сборки из источника и запускает исполняемый файл. Я также хочу скопировать артефакт сборки (в моем случае он .zipсоздается sbt distв '../ target / `, но я думаю, что этот вопрос также относится к банкам, двоичным файлам и т. Д.

docker cpработает с контейнерами, а не с изображениями; мне нужно запускать контейнер только для того, чтобы получить из него файл? В сценарии я пробовал работать /bin/bashв интерактивном режиме в фоновом режиме, копируя файл, а затем убивая контейнер, но это кажется глупым. Есть ли способ лучше?

С другой стороны, я бы хотел избежать распаковки .tarфайла после запуска docker save $IMAGENAMEтолько для того, чтобы получить один файл (но сейчас это кажется самым простым, хотя и самым медленным вариантом).

Я бы использовал тома докеров, например:

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

но я работаю boot2dockerв OSX, и я не знаю, как напрямую писать в мою файловую систему хоста Mac (тома для чтения и записи монтируются внутри моей виртуальной машины boot2docker, что означает, что я не могу легко поделиться сценарием для извлечения blah.zipиз образа с др. мысли?

отметка
источник

Ответы:

172

Чтобы скопировать файл из изображения, создайте временный контейнер, скопируйте из него файл и затем удалите его:

id=$(docker create image-name)
docker cp $id:path - > local-tar-file
docker rm -v $id
Игорь Буканов
источник
В какой версии докера была createдобавлена ​​/ удалена команда (ее нет в 1.01)
ThorSummoner
2
@ThorSummoner docker createбыл представлен в docker 1.3, blog.docker.com/2014/10/…
Игорь Буканов
1
Я не знаю, почему это не правильный ответ.
CentAu
1
Однозначно правильный ответ !!! Не полагается ни на что внутри контейнера ... Для скретч-изображений Golang это единственный способ!
Марчелло де Сейлз,
2
есть ли причина копировать в стандартный вывод, а затем направлять его в локальный файл? когда я это сделал, он сбросил кучу управляющих символов до и после содержимого файла. запускал его напрямую, как docker cp $id:path > local-tar-fileработал отлично.
Йонатан
63

К сожалению, похоже, что нет способа копировать файлы непосредственно из образов Docker. Сначала вам нужно создать контейнер, а затем скопировать файл из контейнера.

Однако, если ваше изображение содержит catкоманду (а она будет работать во многих случаях), вы можете сделать это с помощью одной команды:

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

Если ваше изображение не содержит cat, просто создайте контейнер и используйте docker cpкоманду, предложенную в ответе Игоря.

фоны
источник
1
Фантастическое решение. Не удалось получить доступ к моему контейнеру, так как он разбился через секунду после запуска, но нужно было захватить файл в нем. Это сработало отлично.
Mirodinho 02
23

Гораздо более быстрый вариант - скопировать файл из запущенного контейнера на смонтированный том:

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

реальный 0m0,446s

** VS **

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

реальный 0m9.014s

Микко
источник
Вероятно, это больше связано с базовой файловой системой, выполняющей ленивую / неглубокую копию файла (подумайте о копировании при записи) в первом примере, а не фактическом копировании байтов файла во втором примере. Полезно проверить, имеют ли cat a >bvs cp a bтакие же тайминги, как показано здесь. Кроме того, если исходный путь и конечный путь находятся в разных файловых системах, то оба примера приведут к полной побайтовой копии.
KevinOrr
14

В комментариях родителей уже было показано, как использовать cat . Вы также можете использовать tar аналогичным образом:

docker run yourimage tar -c -C /my/directory subfolder | tar x
Wedesoft
источник
1
Этот ответ заключается в том, чтобы копировать каталоги вместо файлов, о чем спрашивает исходный вопрос. Однако +1, потому что он также работает с файлами и имеет дополнительную функцию: разрешение и сохранение владельца. Большой!
Калигари
5
На самом деле, я используюdocker run --rm --entrypoint tar _image_ cC _img_directory_ . | tar xvC _host_directory_
Калигари
7
docker cp $(docker create registry.example.com/ansible-base:latest):/home/ansible/.ssh/id_rsa ./hacked_ssh_key

хотел предоставить однострочное решение, основанное на чистой функциональности докеров (bash не нужен)

изменить: контейнер даже не нужно запускать в этом решении

Элича Смит
источник
1

Другой (краткий) ответ на эту проблему:

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

Обновление - как отметил @Elytscha Smith, это работает только в том случае, если ваше изображение имеет встроенный bash

Freedev
источник
1
* еще один короткий ответ для изображений, содержащих исполняемый файл bash. alpine не по умолчанию, а образы, созданные с нуля, тоже нет
Elytscha Smith
0

Я использую boot2docker на MacOS. Могу вас заверить, что скрипты на базе "docker cp" переносимы. Потому что любая команда передается внутри boot2docker, но затем двоичный поток ретранслируется обратно клиенту командной строки docker, запущенному на вашем Mac. Таким образом, операции записи из докер-клиента выполняются внутри сервера и записываются обратно в исполняющий экземпляр клиента!

Я делюсь сценарием резервного копирования для томов докеров с любым контейнером докеров, который я предоставляю, и мои сценарии резервного копирования протестированы как на Linux, так и на MacOS с помощью boot2docker. Резервными копиями можно легко обмениваться между платформами. В основном я выполняю следующую команду внутри своего скрипта:

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

Запускает новый контейнер busybox и монтирует том моего контейнера jenkins с именем jenkins_jenkins_1. Весь том записывается в файл backup / JenkinsBackup-2015-07-09-14-26-15.tar

Я уже перемещал архивы между контейнером linux и контейнером Mac без каких-либо изменений в сценарии резервного копирования или восстановления. Если это то, что вы хотите, вы можете найти здесь весь сценарий и руководство: blacklabelops / jenkins

Blacklabelops
источник
0

Вы можете привязать локальный путь на хосте к пути в контейнере, а затем cpжелаемый файл (ы) к этому пути в конце вашего скрипта.

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

Тогда копировать потом не нужно.

ryanjdillon
источник