Como criar um Dockerfile a partir de uma imagem existente

0
25


As imagens do Docker são criadas criando Dockerfiles. O processo de compilação executa as instruções no Dockerfile para criar as camadas do sistema de arquivos que compõem a imagem final.

E se você já tiver uma imagem? Você pode recuperar o Dockerfile do qual ele foi criado? Neste artigo, veremos dois métodos que podem fazer isso.

O objetivo

Ao criar suas próprias imagens do Docker, você deve armazenar seus Dockerfiles como arquivos com controle de versão em seu repositório de origem. Essa prática garante que você sempre possa recuperar as instruções usadas para montar suas imagens.

No entanto, às vezes você não terá acesso a um Dockerfile. Talvez você esteja usando uma imagem que está em um registro público, mas tem um repositório de origem inacessível. Ou você pode estar trabalhando com instantâneos de imagem que não correspondem diretamente a um Dockerfile com versão. Nesses casos, você precisa de uma técnica que possa criar um Dockerfile a partir de uma imagem em sua máquina.

O Docker não oferece nenhuma funcionalidade interna para fazer isso. As imagens de compilação não têm uma associação com o Dockerfile a partir do qual foram criadas. No entanto, você pode fazer engenharia reversa do processo de compilação para produzir uma boa aproximação do Dockerfile de uma imagem sob demanda.

O comando de histórico do Docker

a docker history O comando revela o histórico da camada de uma imagem. Ele mostra o comando usado para construir cada camada sucessiva do sistema de arquivos, tornando-o um bom ponto de partida ao reproduzir um Dockerfile.

Aqui está um Dockerfile simples para um aplicativo Node.js:

FROM node:16
COPY app.js .
RUN app.js --init
CMD ["app.js"]

Construa a imagem usando docker build:

$ docker build -t node-app:latest .

Agora inspecione o histórico da camada da imagem com docker history:

$ docker history node-app:last IMAGE CREATED BY SIZE COMMENT c06fc21a8eed 8 segundos atrás /bin/sh -c# (nop) CMD ["app.js"]               0B 74d58e07103b 8 segundos atrás /bin/sh -c ./app.js --init 0B 22ea63ef9389 19 segundos atrás /bin/sh -c # (nop) COPY file: 0c0828d0765af4dd ... 50B 424bc28f998d 4 dias atrás /bin/sh -c # (não) CMD ["node"]                 0B  4 dias atrás /bin/sh -c# (nop) PONTO DE ENTRADA ["docker-entry...   0B        
...

The history includes the complete list of layers in the image, including those inherited from the node:16 base image. Layers are ordered so the most recent one is first. You can spot where the layers created by the sample Dockerfile begin based on the creation time. These show Docker’s internal representation of the COPY and CMD instructions used in the Dockerfile.

The docker history output is more useful when the table’s limited to just showing each layer’s command. You can disable truncation too to view the full command associated with each layer:

$ docker history node-app:latest --format "{{.CreatedBy}}" --no-trunc
/bin/sh -c #(nop)  CMD ["app.js"]/bin/sh -c ./app.js --init /bin/sh -c # (nop) COPIAR arquivo: 0c0828d0765af4dd87b893f355e5dff77d6932d452f5681dfb98fd9cf05e8eb1 em .  /bin/sh -c # (não) CMD ["node"]/bin/sh -c # (nop) PONTO DE ENTRADA ["docker-entrypoint.sh"]...

A partir desta lista de comandos, você pode obter uma visão geral das etapas realizadas para montar a imagem. Para imagens simples como essa, essas informações podem ser suficientes para reproduzir com precisão um Dockerfile.

copiar comandos de docker history é um processo trabalhoso. Também é necessário remover o /bin/sh -c no início de cada linha, já que o Docker tratou cada instrução como um comentário Bash sem operações.

Felizmente, existem ferramentas da comunidade disponíveis que podem automatizar a criação de um Dockerfile a partir do histórico de camadas de uma imagem. Para os propósitos deste artigo, vamos nos concentrar no Whaler, que está incluído no alpine/dfimage (Dockerfile-from-Image) Imagem do Docker da organização Alpine.

executando o dfimage image e fornecer uma tag do Docker gerará um Dockerfile que pode ser usado para reproduzir a imagem referenciada. Você precisa vincular o soquete Docker do seu host no dfimage container para que você possa acessar sua lista de imagens e extrair a tag se necessário.

$ docker run --rm 
    -v /var/run/docker.sock:/var/run/docker.sock 
    alpine/dfimage node-app:latest

Analyzing node-app:latest
Docker Version: 20.10.13
GraphDriver: overlay2
Environment Variables
|PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|NODE_VERSION=16.14.2
|YARN_VERSION=1.22.18

Image user
|User is root

Dockerfile:
...
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["node"]
COPY file:bcbc3d5784a8f1017653685866d30e230cae61d0da13dae32525b784383ac75f in .
    app.js

RUN ./app.js --init
CMD ["app.js"]

O Dockerfile criado contém tudo o que você precisa para ir scratch (um sistema de arquivos vazio) para a camada final da imagem especificada. Inclui todas as camadas que vêm da imagem base. Você pode vê-los no primeiro ENTRYPOINT S CMD instruções na saída de exemplo acima (as outras camadas de imagem base foram omitidas por questões de brevidade).

Com a exceção de COPY, as instruções específicas em nossa imagem correspondem ao que foi escrito no Dockerfile original. Agora você pode copiar estas instruções para um novo Dockerfileseja usando todo o dfimage saída ou pegando apenas a parte que pertence à imagem final. A última opção só é uma possibilidade se você souber a identidade da imagem base original para poder adicionar uma FROM instruções no topo do arquivo.

limitações

Em muitos casos dfimage você poderá montar um Dockerfile utilizável. No entanto, não é perfeito e uma correspondência exata não é garantida. A extensão das discrepâncias em comparação com o Dockerfile original na imagem varia de acordo com as instruções usadas.

Nem todas as instruções são capturadas no histórico da camada. Os que não são compatíveis serão perdidos e não há como determinar quais eram. A melhor precisão é obtida com instruções de comando e metadados como RUN, ENV, WORKDIR, ENTRYPOINTS CMD. RUN as instruções ainda podem estar faltando se o seu comando não resultar em alterações no sistema de arquivos, o que significa que uma nova camada de imagem não foi criada.

COPY S ADD As instruções apresentam desafios únicos. O histórico não contém o caminho do arquivo host que foi copiado para o contêiner. Você pode ver que ocorreu uma cópia, mas o caminho de origem se refere ao hash do arquivo que foi copiado para a imagem do contexto de compilação.

Ao chegar ao destino final, isso pode ser suficiente para ajudá-lo a determinar o que foi copiado e por quê. Você pode usar essas informações para interpolar um novo caminho de origem no Dockerfile que pode ser usado para compilações futuras. Em outros casos, inspecionar o arquivo dentro da imagem pode ajudar a revelar a finalidade da cópia para que você possa determinar um nome de arquivo significativo para o caminho do host.

Resumo

As imagens do Docker não incluem uma maneira direta de retornar ao Dockerfile a partir do qual foram criadas. No entanto, ainda é possível reconstruir o processo de construção. Para imagens simples com poucas instruções, muitas vezes você pode descobrir as instruções manualmente observando o CREATED BY coluna na docker history saída do comando.

Imagens maiores com processos de construção mais complexos são melhor analisadas com ferramentas como dfimage. Isso faz o trabalho duro de analisar os detalhes docker history saída para você, produzindo um novo Dockerfile que provavelmente é uma correspondência melhor para o original.

Os esforços de engenharia reversa não são perfeitos e algumas instruções do Dockerfile são perdidas ou corrompidas durante o processo de compilação. Consequentemente, você não deve presumir que os Dockerfiles criados dessa maneira são uma representação precisa do original. Você pode ter que fazer alguns ajustes manuais para ADD S COPY instruções também, ressuscitando caminhos de arquivo host que foram convertidos para criar referências de contexto.