Copiando arquivos do contêiner do Docker para o host

1709

Estou pensando em usar o Docker para criar minhas dependências em um servidor de Integração Contínua (CI), para que eu não precise instalar todos os tempos de execução e bibliotecas nos próprios agentes.

Para conseguir isso, eu precisaria copiar os artefatos de construção que são criados dentro do contêiner de volta para o host. Isso é possível?

user2668128
fonte
vocês podem gostar do meu método hacker aqui: stackoverflow.com/a/55876794/990618
colin lamarre
1
A resposta correta e real do capitão do docker na parte inferior das respostas.
burtsevyg

Respostas:

2951

Para copiar um arquivo de um contêiner para o host, você pode usar o comando

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

Aqui está um exemplo:

$ sudo docker cp goofy_roentgen:/out_read.jpg .

Aqui goofy_roentgen é o nome do contêiner que obtive do seguinte comando:

$ 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

Você também pode usar (parte) o ID do contêiner . O comando a seguir é equivalente ao primeiro

$ sudo docker cp 1b4a:/out_read.jpg .
creack
fonte
42
Aqui está uma maneira prática de chegar ao seu mais recente recipiente se você está simplesmente usando janela de encaixe para um ambiente Linux temporário: docker ps -alq.
Josh Habdas
37
esse comando cp funciona como está para copiar árvores de diretório também (não apenas um único arquivo).
eCoE
88
Em versões mais recentes do estivador você pode copiar bidirectionally (host para a embalagem ou recipiente para host) comdocker cp ...
Freedom_Ben
9
Eu precisava docker cp -Lcopiar links simbólicos
Harrison Powers
24
NOTA: o contêiner não precisa estar em execução para usar o comando cp. Útil se o seu contêiner trava constantemente.
Martlark
219

Você não precisa usar docker run.

Você pode fazer isso com docker create.

Dos documentos :

O docker createcomando cria uma camada de contêiner gravável sobre a imagem especificada e a prepara para executar o comando especificado. O ID do contêiner é impresso para STDOUT. É semelhante a, docker run -dexceto que o contêiner nunca é iniciado.

Então, você pode fazer:

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

Aqui, você nunca inicia o contêiner. Isso me pareceu benéfico.

Ishan Bhatt
fonte
19
Isso precisa de mais votos. Ótimo para quando você só precisa criar algo em um contêiner e depois copiar as saídas.
Honza Kalfus
4
@HonzaKalfus Concordo que isso precisa ser maior. Isto é exatamente o que eu estava procurando. Usei isso para criar alguns arquivos binários usando um ambiente conhecido (amazon linux em uma versão específica). foi capaz de criar um script de shell que construiu completamente o docker e extraiu dele o binário resultante! Perfeito.
Mark
1
É -tiobrigatório e bashobrigatório?
jII 18/10/19
@ jII, eu tinha feito isso porque mais tarde, eu uso o docker nele. Em casos simples, não é necessário, mas não prejudica aqui também.
Ishan Bhatt
De alguma forma, é possível usar caracteres curinga? Quero dizer ... não sei o nome exato do arquivo que preciso copiar, porque ele tem um número de versão.
juzzlin
87

Monte um "volume" e copie os artefatos para lá:

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

Então, quando a construção terminar e o contêiner não estiver mais em execução, ele já terá copiado os artefatos da construção para o artifacts diretório no host.

Editar

Advertência: Ao fazer isso, você pode ter problemas com o ID do usuário do docker que corresponde ao ID do usuário atual. Ou seja, os arquivos /artifactsinseridos serão mostrados como pertencentes ao usuário com o UID do usuário usado dentro do contêiner do docker. Uma maneira de contornar isso pode ser usar o UID do usuário que está chamando:

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
fonte
7
Na verdade, você pode usar o chowncomando para corresponder ao ID do usuário e ao ID do grupo na máquina host.
Dimchansky
@Frondor Consulte a referência de configuração de volume docs.docker.com/compose/compose-file/…
djhaskin987
Já funcionou, e isso não vai funcionar. Depois que o contêiner copia os arquivos para o volume pela primeira vez, na próxima vez, o volume não está mais vazio e os arquivos não estão sendo substituídos pelos mais novos. O contêiner está dando prioridade aos arquivos do host (aqueles copiados na primeira vez em que você montou a imagem do contêiner).
Frondor
soa como algo que poderia ser a sua própria pergunta SO @Frondor
djhaskin987
1
Estou comprando uma cerveja para você! Obrigado!
Dimitar Vukman
27

Monte um volume, copie os artefatos, ajuste a identificação do proprietário e a identificação do grupo:

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
fonte
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

Descrição

docker runcom um volume do host, chowno artefato, cpo artefato no volume do host:

$ 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

Esse truque funciona porque a chowninvocação no heredoc leva $(id -u):$(id -g)valores de fora do contêiner em execução; ou seja, o host do docker.

Os benefícios são:

  • você não precisa docker container run --nameou docker container create --nameantes
  • você não precisa docker container rmdepois
rubicks
fonte
2
Voto positivo para a comparação entre cpe respostas baseadas em volume. Além disso, para o idtruque para a posse, que é uma verdadeira dor de cabeça às vezes
Marc Ghorayeb
18

A maioria das respostas não indica que o contêiner deve ser executado antes docker cpque funcione:

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
fonte
3
BTW, se o contêiner deve / pode estar em execução / parado / parece depender do tipo de host / técnica de virtualização . O documento da janela de encaixe atual diz "O CONTAINER pode ser um contêiner em execução ou parado.". Vários locais no SO, incluindo um comentário sobre a resposta aceita, dizem "isso também funciona em um contêiner parado". Sob Windows Hyper-V, parece que é necessário para parar recipiente antes de copiar um arquivo .
Página Inicial>
A cópia também funciona quando o contêiner está parado.
Luke W
17

Se você não possui um contêiner em execução, apenas uma imagem e supondo que deseja copiar apenas um arquivo de texto, você pode fazer algo assim:

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

Estou postando isso para qualquer pessoa que esteja usando o Docker para Mac. Isto é o que funcionou para mim:

 $ mkdir mybackup # local directory on Mac

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

Note que quando eu monto usando -vissobackup diretório é criado automaticamente.

Espero que isso seja útil para alguém algum dia. :)

Paulo
fonte
Se você usar o docker-compose, os volumes-from serão descontinuados na versão 3 e depois.
precisa saber é
Para adicionar ao comentário do mulg0r, consulte stackoverflow.com/a/45495380/199364 - na v.3, você coloca um volumescomando na raiz do config.yml, para que os volumes sejam acessíveis por vários contêineres.
Home
5

Se você deseja apenas extrair um arquivo de uma imagem (em vez de um contêiner em execução), faça o seguinte:

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

Isso abrirá o contêiner, escreva o novo arquivo e remova o contêiner. Uma desvantagem, no entanto, é que as permissões de arquivo e a data de modificação não serão preservadas.

sg
fonte
5

Com o lançamento do Docker 19.03, você pode pular a criação do contêiner e até criar uma imagem. Existe uma opção com builds baseadas no BuildKit para alterar o destino da saída. Você pode usar isso para gravar os resultados da compilação no diretório local, e não em uma imagem. Por exemplo, aqui está uma compilação de um binário 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

No Dockerfile acima, estou construindo o artifactestágio que inclui apenas os arquivos que quero exportar. E o --outputsinalizador recém-introduzido permite que eu os escreva em um diretório local em vez de em uma imagem. Isso precisa ser executado com o mecanismo BuildKit que acompanha a versão 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

Após a conclusão da construção, o appbinário foi exportado:

$ ls
Dockerfile  app  go.mod  main.go

$ ./app
Ready to receive requests on port 8080

O Docker tem outras opções para o --outputsinalizador documentado em seu repo BuildKit upstream: https://github.com/moby/buildkit#output

BMitch
fonte
cache de compilação padrão não usado para compilação com saída, é ruim
burtsevyg
O @burtsevyg Buildkit é um construtor diferente, usando um ambiente de cache diferente. É muito mais eficiente em cache.
BMitch 10/12/19
obrigado vou melhorar meus nós de construção.
burtsevyg
4

Como uma solução mais geral, há um plug-in do CloudBees para o Jenkins construir dentro de um contêiner do Docker . Você pode selecionar uma imagem para usar em um registro do Docker ou definir um Dockerfile para criar e usar.

Ele montará a área de trabalho no contêiner como um volume (com usuário apropriado), configurará como seu diretório de trabalho, executará os comandos solicitados (dentro do contêiner). Você também pode usar o plug-in docker-workflow (se preferir código à interface do usuário) para fazer isso, com o comando image.inside () {}.

Basicamente, tudo isso, inserido no servidor de CI / CD e mais.

BobMcGee
fonte
4

Eu usei o PowerShell (Admin) com este comando.

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

Exemplo

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

Outra boa opção é criar o contêiner e executá-lo usando o sinalizador -c com o interpretador de shell para executar algumas vírgulas

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

O comando acima faz isso:

-Eu = executa o contêiner no modo interativo

--rm = removeu o contêiner após a execução.

-v = compartilhou uma pasta como volume do caminho do host para o caminho do contêiner.

Finalmente, o / bin / sh -c permite introduzir um comando como parâmetro e esse comando copiará os arquivos de trabalhos de casa para o caminho do contêiner.

Espero que esta resposta adicional possa ajudá-lo

Yor Jaggy
fonte
1

Crie um diretório de dados no sistema host (fora do contêiner) e monte-o em um diretório visível de dentro do contêiner. Isso coloca os arquivos em um local conhecido no sistema host e facilita o acesso às ferramentas e aplicativos no sistema host.

docker run -d -v /path/to/Local_host_dir:/path/to/docker_dir docker_image:tag
Anigbo inocente
fonte
4
Isso permite que você injete um diretório e seu conteúdo do host no contêiner. Não permite copiar arquivos do contêiner de volta para o host.
BMitch 16/05
Isso acontece se a pasta host tiver permissões muito amplas?
Giorgiosironi
0

Crie um caminho para o qual você deseja copiar o arquivo e use:

docker run -d -v hostpath:dockerimag
Chandra Pal
fonte
0

Você pode usar em bindvez de, volumese desejar montar apenas uma pasta, não criar armazenamento especial para um contêiner:

  1. Crie sua imagem com a tag:

    docker build . -t <image>

  2. Execute sua imagem e vincule o diretório $ (pwd) atual onde app.py armazena e mapeie-o para / root / example / dentro de seu contêiner.

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

zytfo
fonte
0

Isso também pode ser feito no SDK, por exemplo, python. Se você já possui um contêiner, pode procurar o nome pelo console (docker ps -a ). O nome parece ser uma concatenação de um cientista e um adjetivo (por exemplo, "relaxing_pasteur").

Confira 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()

Então, algo assim sairá do caminho especificado (/ saída) no contêiner para a máquina host e descompactará o alcatrão.

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
fonte