Копирование файлов из контейнера Docker на хост

1709

Я думаю об использовании Docker для построения моих зависимостей на сервере Continuous Integration (CI), чтобы мне не пришлось устанавливать все среды выполнения и библиотеки на самих агентах.

Для этого мне нужно скопировать артефакты сборки, которые встроены в контейнер, обратно в хост. Это возможно?

user2668128
источник
Вам, ребята, может понравиться мой хакерский метод здесь: stackoverflow.com/a/55876794/990618
Колин Ламарр,
1
Правильный и актуальный ответ от капитана докера внизу ответов.
бурцевыйг

Ответы:

2951

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

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

Вот пример:

$ sudo docker cp goofy_roentgen:/out_read.jpg .

Здесь goofy_roentgen - это имя контейнера, которое я получил от следующей команды:

$ 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

Вы также можете использовать (часть) ID контейнера . Следующая команда эквивалентна первой

$ sudo docker cp 1b4a:/out_read.jpg .
creack
источник
42
Вот удобный способ получить в вашем последний контейнер , если вы просто используете докер для среды Linux темпа: docker ps -alq.
Джош Хабдас
37
Эта команда cp также работает как есть для копирования деревьев каталогов (не только одного файла).
Эко
88
В новых версиях Docker вы можете копировать в двух направлениях (хост-контейнер или контейнер-хост) с помощьюdocker cp ...
Freedom_Ben
9
Мне нужно docker cp -Lбыло скопировать символические ссылки
Харрисон Пауэрс
24
ПРИМЕЧАНИЕ: контейнер не должен быть запущен, чтобы использовать команду cp. Удобно, если ваш контейнер постоянно падает.
Мартларк
220

Вам не нужно использовать docker run.

Вы можете сделать это с docker create.

Из документов :

Команда docker createсоздает доступный для записи слой контейнера над указанным изображением и подготавливает его для выполнения указанной команды. Идентификатор контейнера затем печатается в STDOUT. Это похоже на то, docker run -dчто контейнер никогда не запускается.

Итак, вы можете сделать:

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

Здесь вы никогда не запускаете контейнер. Это выглядело выгодно для меня.

Ишан Бхатт
источник
19
Это требует больше голосов. Отлично подходит для случаев, когда вам просто нужно создать что-то в контейнере, а затем скопировать результаты.
Хонза Кальфус
4
@HonzaKalfus Я согласен, что это должно быть выше. Это именно то, что я был после. Я использовал это так, чтобы я мог создавать некоторые двоичные файлы, используя известную среду (amazon linux в определенной версии). смог создать сценарий оболочки, который полностью собрал докер и извлек из него полученный бинарный файл! Отлично.
Марк
1
Является -tiобязательным и bashтребуется?
ИСИ
@jII, я сделал это, потому что позже я запускаю на нем докер. В простых случаях это не нужно, но и здесь не вредит.
Ишан Бхатт
Можно ли как-то использовать подстановочные знаки? Я имею в виду ... Я не знаю точное имя файла, который мне нужно скопировать, потому что на нем есть номер версии.
Juzzlin
87

Смонтируйте «том» и скопируйте туда артефакты:

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

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

редактировать

Предостережение: при этом вы можете столкнуться с проблемами с идентификатором пользователя докера, совпадающим с идентификатором текущего запущенного пользователя. То есть файлы в /artifactsбудут показаны как принадлежащие пользователю с UID пользователя, использованного внутри контейнера докера. Обходным путем может быть использование UID вызывающего пользователя:

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
источник
7
На самом деле вы можете использовать chownкоманду для сопоставления идентификатора пользователя и идентификатора группы на хост-компьютере.
Димчанский
@Frondor См опорный объем конфигурации docs.docker.com/compose/compose-file/...
djhaskin987
Уже сделал, и это не сработает. После того как контейнер скопировал файлы на том в первый раз, в следующий раз том больше не будет пустым, и файлы не будут перезаписаны более новыми. Контейнер отдает приоритет файлам хоста (те, которые были скопированы при первом подключении образа контейнера).
Фрондор
звучит как что-то, что может быть своим собственным вопросом @Frondor
djhaskin987
1
Я покупаю тебе приятель пива! Спасибо!
Димитар Вукман
27

Смонтируйте том, скопируйте артефакты, настройте идентификатор владельца и идентификатор группы:

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
источник
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

Описание

docker runс томом хоста, chownартефактом, cpартефактом для тома хоста:

$ 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

Этот трюк работает, потому что chownвызов внутри heredoc принимает$(id -u):$(id -g) значения извне работающего контейнера; т.е. хост докера.

Преимущества:

  • Вы не должны docker container run --nameилиdocker container create --name раньше
  • вам не нужно docker container rmпосле
rubicks
источник
2
Проголосовал за сравнение cpи ответы на основе объема. Кроме того, для idтрюка за владение, это иногда настоящая головная боль
Марк Горайб
18

Большинство ответов не указывают на то, что контейнер должен работать до того docker cp, как сработает:

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
источник
3
Кстати, вопрос о том, должен ли контейнер / должен быть запущен / остановлен / зависит от типа хоста / технологии виртуализации . Текущий докер говорит, что «КОНТЕЙНЕР может быть работающим или остановленным контейнером». Множество мест на SO, включая комментарий к принятому ответу, говорят «это также работает на остановленном контейнере». Под Windows Hyper-Vэто, по- видимому необходимо , чтобы остановить контейнер перед копированием файла .
ToolmakerSteve
Копирование также работает, когда контейнер остановлен.
Люк W
17

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

docker run the-image cat path/to/container/file.txt > path/to/host/file.txt
cancerbero
источник
7

Я публикую это для всех, кто использует Docker для Mac. Вот что сработало для меня:

 $ mkdir mybackup # local directory on Mac

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

Обратите внимание, что при монтировании с использованием -vэтого backupкаталога автоматически создается.

Надеюсь, это кому-нибудь пригодится. :)

Павел
источник
Если вы используете docker-compose, volume-from устарела в версии 3 и после.
Mulg0r
Чтобы добавить комментарий к mulg0r, см. Stackoverflow.com/a/45495380/199364 - в v.3 вы помещаете volumesкоманду в корень config.yml, чтобы тома были доступны для нескольких контейнеров.
ToolmakerSteve
5

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

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

Это вызовет контейнер, запишет новый файл, затем удалит контейнер. Однако один недостаток заключается в том, что права доступа к файлу и дата изменения не будут сохранены.

SG
источник
5

С выпуском Docker 19.03 вы можете пропустить создание контейнера и даже создание образа. Существует вариант с сборками на основе BuildKit для изменения выходного назначения. Вы можете использовать это для записи результатов сборки в локальный каталог, а не в изображение. Например, вот сборка 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

Из приведенного выше Dockerfile я создаю artifactэтап, который включает только файлы, которые я хочу экспортировать. И недавно введенный --outputфлаг позволяет мне записывать их в локальный каталог вместо изображения. Это необходимо выполнить с помощью движка BuildKit, который поставляется с 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

После завершения сборки appбинарный файл был экспортирован:

$ ls
Dockerfile  app  go.mod  main.go

$ ./app
Ready to receive requests on port 8080

У Docker есть другие опции для --outputфлага, задокументированные в их репозитории BuildKit: https://github.com/moby/buildkit#output

BMitch
источник
стандартный
сборочный
@burtsevyg Buildkit - это другой компоновщик, использующий другую среду кеширования. Это намного эффективнее кеша.
BMitch
спасибо, я улучшу мои узлы сборки.
бурцевыг
4

В качестве более общего решения есть плагин CloudBees, который Jenkins может встроить в контейнер Docker . Вы можете выбрать изображение для использования из реестра Docker или определить Dockerfile для сборки и использования.

Он будет монтировать рабочее пространство в контейнер как том (с соответствующим пользователем), устанавливать его в качестве рабочего каталога, выполнять любые команды, которые вы запрашиваете (внутри контейнера). Вы также можете использовать плагин docker-workflow (если вы предпочитаете код над пользовательским интерфейсом) для этого с помощью команды image.inside () {}.

В основном все это запекается на вашем CI / CD-сервере, а затем и на некоторых.

BobMcGee
источник
4

Я использовал PowerShell (Admin) с этой командой.

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

пример

docker cp ff3a6608467d:/var/www/app/error.html  C:\\error.html
Хачорнчит Songsaen
источник
2

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

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

Приведенная выше команда делает это:

= запустить контейнер в интерактивном режиме

--rm = удалил контейнер после выполнения.

-v = общая папка как том от пути вашего хоста до пути контейнера.

Наконец, / bin / sh -c позволяет вам ввести команду в качестве параметра, и эта команда скопирует ваши файлы домашней работы в путь контейнера.

Я надеюсь, что этот дополнительный ответ может помочь вам

Йор Джагги
источник
1

Создайте каталог данных в хост-системе (вне контейнера) и подключите его к каталогу, видимому изнутри контейнера. Это размещает файлы в известном месте в хост-системе и упрощает доступ инструментов к инструментам и приложениям в хост-системе.

docker run -d -v /path/to/Local_host_dir:/path/to/docker_dir docker_image:tag
Иннокентий Анигбо
источник
4
Это позволяет вам внедрить каталог и его содержимое с хоста в контейнер. Это не позволяет вам копировать файлы из контейнера обратно на хост.
BMitch
Это делает, если папка хоста имеет очень широкие разрешения?
Георгиосирони
0

Создайте путь, куда вы хотите скопировать файл, а затем используйте:

docker run -d -v hostpath:dockerimag
Чандра Пал
источник
0

Вы можете использовать bindвместо, volumeесли вы хотите смонтировать только одну папку, а не создавать специальное хранилище для контейнера:

  1. Создайте свое изображение с тегом:

    docker build . -t <image>

  2. Запустите ваше изображение и привяжите текущий каталог $ (pwd), где хранится app.py, и сопоставьте его с / root / example / внутри вашего контейнера.

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

zytfo
источник
0

Это также может быть сделано в SDK, например, Python. Если у вас уже есть встроенный контейнер, вы можете найти имя через консоль (docker ps -a ), имя, кажется, представляет собой некоторую конкатенацию ученого и прилагательного (то есть «relaxed_pasteur»).

Проверьте 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()

Тогда что-то вроде этого вытащит из указанного пути (/ output) в контейнере на ваш хост-компьютер и распакует 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()
Джон Дринэйн
источник