Docker - como posso copiar um arquivo de uma imagem para um host?

115

Minha pergunta está relacionada a esta questão sobre como copiar arquivos de contêineres para hosts; Eu tenho um Dockerfile que busca dependências, compila um artefato de compilação da fonte e executa um executável. Também quero copiar o artefato de construção (no meu caso, é .zipproduzido por sbt distem '../ target / `, mas acho que esta questão também se aplica a jars, binários, etc.

docker cpfunciona em contêineres, não em imagens; eu preciso iniciar um contêiner apenas para obter um arquivo dele? Em um script, tentei executar /bin/bashno modo interativo em segundo plano, copiando o arquivo e, em seguida, encerrando o contêiner, mas isso parece confuso. Existe uma maneira melhor?

Por outro lado, gostaria de evitar descompactar um .tararquivo depois de executar docker save $IMAGENAMEapenas para retirar um arquivo (mas essa parece ser a opção mais simples, embora a mais lenta agora).

Eu usaria volumes do docker, por exemplo:

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

mas estou executando boot2dockerem OSX e não sei como escrever diretamente no meu sistema de arquivos host mac (volumes de leitura e gravação estão montados dentro da minha VM boot2docker, o que significa que não posso compartilhar facilmente um script para extrair blah.zipde uma imagem com outros. Pensamentos?

Marca
fonte

Respostas:

172

Para copiar um arquivo de uma imagem, crie um contêiner temporário, copie o arquivo dele e exclua-o:

id=$(docker create image-name)
docker cp $id:path - > local-tar-file
docker rm -v $id
Igor Bukanov
fonte
Qual versão do docker foi o createcomando adicionado / removido (não está presente no 1.01)
ThorSummoner
2
@ThorSummoner docker createfoi apresentado no docker 1.3, blog.docker.com/2014/10/…
Igor Bukanov
1
Não sei por que essa não foi selecionada como a resposta correta.
CentAu
1
Definitivamente, a resposta certa !!! Não depende de nada dentro do container ... Para raspadinhas Golang, esta é a única maneira possível!
Marcello de Sales
2
algum motivo para copiar para stdout e direcioná-lo para um arquivo local? quando eu fiz isso, ele despejou um monte de caracteres de controle antes e depois do conteúdo do arquivo. executá-lo diretamente como docker cp $id:path > local-tar-filefuncionou perfeitamente.
Yonatan
61

Infelizmente, não parece haver uma maneira de copiar arquivos diretamente das imagens do Docker. Você precisa primeiro criar um contêiner e, em seguida, copiar o arquivo do contêiner.

No entanto, se sua imagem contiver um catcomando (e fará em muitos casos), você pode fazê-lo com um único comando:

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

Caso sua imagem não contenha cat, basta criar um container e usar o docker cpcomando conforme sugerido na resposta de Igor.

fons
fonte
1
Solução fantástica. Não foi possível acessar meu contêiner, pois ele travou um segundo após a inicialização, mas precisava pegar um arquivo dentro dele. Funcionou perfeitamente.
Mirodinho 02 de
23

Uma opção muito mais rápida é copiar o arquivo do contêiner em execução para um volume montado:

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

0m0.446s reais

** VS **

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

0m9.014s reais

Mikko
fonte
Isso provavelmente tem mais a ver com o sistema de arquivos subjacente realizando uma cópia lenta / superficial do arquivo (pense em copiar na gravação) no primeiro exemplo, em vez de realmente copiar os bytes do arquivo no segundo exemplo. Um teste útil seria ver se cat a >bvs cp a btêm tempos semelhantes aos mostrados aqui. Além disso, se o caminho de origem e o caminho de destino residirem em sistemas de arquivos diferentes, ambos os exemplos levarão a uma cópia byte a byte completa.
KevinOrr
14

O comentário dos pais já mostrou como usar o gato . Você também pode usar o alcatrão de maneira semelhante:

docker run yourimage tar -c -C /my/directory subfolder | tar x
wedesoft
fonte
1
Essa resposta é copiar diretórios em vez de arquivos, conforme a pergunta original. Porém, +1 porque também funciona com arquivos e vem com um recurso extra: permissão e preservação do proprietário. Ótimo!
caligari
5
Na verdade, eu usodocker run --rm --entrypoint tar _image_ cC _img_directory_ . | tar xvC _host_directory_
caligari
6
docker cp $(docker create registry.example.com/ansible-base:latest):/home/ansible/.ssh/id_rsa ./hacked_ssh_key

queria fornecer uma solução de uma linha

editar: o contêiner nem precisa ser executado nesta solução

Elytscha Smith
fonte
1

Outra (curta) resposta para este problema:

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

Estou usando boot2docker no MacOS. Posso garantir que os scripts baseados em "docker cp" são portáteis. Porque qualquer comando é retransmitido dentro do boot2docker, mas o fluxo binário é retransmitido de volta para o cliente da linha de comando do docker em execução no seu mac. Portanto, as operações de gravação do cliente docker são executadas dentro do servidor e gravadas de volta na instância do cliente em execução!

Estou compartilhando um script de backup para volumes do docker com qualquer contêiner do docker que forneço e meus scripts de backup são testados no Linux e no MacOS com boot2docker. Os backups podem ser facilmente trocados entre plataformas. Basicamente, estou executando o seguinte comando dentro do meu 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

Executa um novo contêiner do busybox e monta o volume do meu contêiner jenkins com o nome jenkins_jenkins_1. Todo o volume é gravado no arquivo backups / JenkinsBackup-2015-07-09-14-26-15.tar

Já movi os arquivos entre o contêiner linux e meu contêiner mac sem nenhum ajuste no script de backup ou restauração. Se é isso que você quer, você encontra o script completo em um tutorial aqui: blacklabelops / jenkins

blacklabelops
fonte
0

Você pode vincular um caminho local no host a um caminho no contêiner e, em seguida, cpos arquivos desejados a esse caminho no final do seu script.

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

Então, não há necessidade de copiar depois.

Ryanjdillon
fonte