Docker - как я могу скопировать файл с картинки на хост?

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

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

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

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

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

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

13 ответов

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

id=$(docker create image-name)
docker cp $id:path - > local-tar-file
docker rm -v $id
docker cp $(docker create registry.example.com/ansible-base:latest):/home/ansible/.ssh/id_rsa ./hacked_ssh_key

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

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

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

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

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

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

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

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

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

В.С.

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

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

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

docker run yourimage tar -c -C /my/directory subfolder | tar x

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

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

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

Первое извлечение образа докера с помощью docker pull

      docker pull <IMG>:<TAG>

Затем создайте контейнер с помощью команды docker create и сохраните идентификатор контейнера как переменную.

      img_id=$(docker create <IMG>:<TAG>)

Теперь запуститеdocker cpкоманда для копирования папок и файлов из контейнера докеров на хост

      docker cp $img_id:/path/in/container /path/in/host

После перемещения файлов/папок удалите контейнер с помощью docker rm.

      docker rm -v $img_id

Это не прямой ответ на детали вопроса, но в целом, как только вы вытащили изображение, оно сохраняется в вашей системе, как и все его файлы. AFAIK, эти файлы обычно можно найти в (требуется root-доступ). После вытягивания изображения сделайте например внутри которого будут отображаться самые последние слои вверху. В одном из этих каталогов внутри каталог, можно найти нужный файл.

Так что теоретически создавать временный контейнер не нужно. Конечно, это решение крайне неудобно. Возможно, этот подход можно улучшить, но я не знаю, есть ли простой способ выяснить, какой слой / каталог находится под каталог принадлежит какому изображению.

Обновление - вот лучшая версия без файла tar:

      $id = & docker create image-name
docker cp ${id}:path .
docker rm -v $id

Старый ответ Вариант PowerShell ответа Игоря Буканова:

      $id = & docker create image-name
docker cp ${id}:path - > local-file.tar
docker rm -v $id

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

Это скопирует файлы из /inside/container/ к вашей машине в /path/to/hostdir/.

      docker run --rm -v /path/to/hostdir:/mnt/out "$IMAGENAME" /bin/cp -r /inside/container/ /mnt/out/

Если ваш двоичный файл создан из пользовательского образа на основе файла docker, то:

      docker build -t name-of-the-image -f custom.dockerfile .
id=$(docker create name-of-the-image)
docker cp $id:/path/to/binary-file - > binary-file
docker rm -v $id

Вышеупомянутое также является альтернативой поэтапным сборкам, если они не поддерживаются используемой вами версией Docker (например, 1.13).
Вы создаете двоичный файл и копируете его на хост, а на втором этапе вы создаете новый образ, который копирует этот двоичный файл с хоста.

Я использую 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. Весь том записан в файл backups/JenkinsBackup-2015-07-09-14-26-15.tar

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

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

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

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

Другие вопросы по тегам