Como gerar um Dockerfile a partir de uma imagem?

241

É possível gerar um Dockerfile a partir de uma imagem? Eu quero saber por dois motivos:

  1. Posso baixar imagens do repositório, mas gostaria de ver a receita que as gerou.

  2. Eu gosto da idéia de salvar instantâneos, mas, quando terminar, seria bom ter um formato estruturado para revisar o que foi feito.

user1026169
fonte
Você pode usar Portainer.io portainer.io É uma aplicação web que roda dentro de um recipiente janela de encaixe usado para gerenciar tudo (quase) coisas sobre seus recipientes. Até imagens são recebidas.
18768 Vincent Vincent

Respostas:

98

Atualizar:

Citação do comentário de @ aleung:

centurylink/dockerfile-from-imagenão funciona com a nova versão docker. Este funciona para mim:hub.docker.com/r/chenzj/dfimage

Como gerar um Dockerfile a partir de uma imagem?

Você pode.

Primeira maneira

$ docker pull centurylink/dockerfile-from-image
$ alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm centurylink/dockerfile-from-image"
$ dfimage --help
Usage: dockerfile-from-image.rb [options] <image_id>
    -f, --full-tree                  Generate Dockerfile for all parent layers
    -h, --help                       Show this message

Aqui está o exemplo para gerar o Dockerfile a partir de uma imagem existente selenium/node-firefox-debug

core@core-01 ~ $ docker pull centurylink/dockerfile-from-image
core@core-01 ~ $ alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm centurylink/dockerfile-from-image"
core@core-01 ~ $ dfimage selenium/node-firefox-debug
ADD file:b43bf069650bac07b66289f35bfdaf474b6b45cac843230a69391a3ee342a273 in /
RUN echo '#!/bin/sh' > /usr/sbin/policy-rc.d    && echo 'exit 101' >> /usr/sbin/policy-rc.d     && chmod +x /usr/sbin/policy-rc.d       && dpkg-divert --local --rename --add /sbin/initctl     && cp -a /usr/sbin/policy-rc.d /sbin/initctl    && sed -i 's/^exit.*/exit 0/' /sbin/initctl         && echo 'force-unsafe-io' > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup         && echo 'DPkg::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' > /etc/apt/apt.conf.d/docker-clean   && echo 'APT::Update::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' >> /etc/apt/apt.conf.d/docker-clean   && echo 'Dir::Cache::pkgcache ""; Dir::Cache::srcpkgcache "";' >> /etc/apt/apt.conf.d/docker-clean      && echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/docker-no-languages      && echo 'Acquire::GzipIndexes "true"; Acquire::CompressionTypes::Order:: "gz";' > /etc/apt/apt.conf.d/docker-gzip-indexes
RUN sed -i 's/^#\s*\(deb.*universe\)$/\1/g' /etc/apt/sources.list
CMD ["/bin/bash"]
MAINTAINER Selenium <[email protected]>
RUN echo "deb http://archive.ubuntu.com/ubuntu trusty main universe\n" > /etc/apt/sources.list && echo "deb http://archive.ubuntu.com/ubuntu trusty-updates main universe\n" >> /etc/apt/sources.list
RUN apt-get update -qqy && apt-get -qqy --no-install-recommends install ca-certificates openjdk-7-jre-headless unzip wget && rm -rf /var/lib/apt/lists/* && sed -i 's/\/dev\/urandom/\/dev\/.\/urandom/' ./usr/lib/jvm/java-7-openjdk-amd64/jre/lib/security/java.security
RUN mkdir -p /opt/selenium && wget --no-verbose http://selenium-release.storage.googleapis.com/2.46/selenium-server-standalone-2.46.0.jar -O /opt/selenium/selenium-server-standalone.jar
RUN sudo useradd seluser --shell /bin/bash --create-home && sudo usermod -a -G sudo seluser && echo 'ALL ALL = (ALL) NOPASSWD: ALL' >> /etc/sudoers && echo 'seluser:secret' | chpasswd
MAINTAINER Selenium <[email protected]>
ENV DEBIAN_FRONTEND=noninteractive
ENV DEBCONF_NONINTERACTIVE_SEEN=true
ENV TZ=US/Pacific
RUN echo "US/Pacific" | sudo tee /etc/timezone && dpkg-reconfigure --frontend noninteractive tzdata
RUN apt-get update -qqy && apt-get -qqy install xvfb && rm -rf /var/lib/apt/lists/*
COPY file:335d2f6f9bfe311d2b38034ceab3b2ae2a1e07b9b203b330cac9857d6e17c148 in /opt/bin/entry_point.sh
RUN chmod +x /opt/bin/entry_point.sh
ENV SCREEN_WIDTH=1360
ENV SCREEN_HEIGHT=1020
ENV SCREEN_DEPTH=24
ENV DISPLAY=:99.0
USER [seluser]
CMD ["/opt/bin/entry_point.sh"]
MAINTAINER Selenium <[email protected]>
USER [root]
RUN apt-get update -qqy && apt-get -qqy --no-install-recommends install firefox && rm -rf /var/lib/apt/lists/*
COPY file:52a2a815e3bb6b85c5adfbceaabb5665b63f63ef0fb0e3f774624ee399415f84 in /opt/selenium/config.json
USER [seluser]
MAINTAINER Selenium <[email protected]>
USER [root]
RUN apt-get update -qqy && apt-get -qqy install x11vnc && rm -rf /var/lib/apt/lists/* && mkdir -p ~/.vnc && x11vnc -storepasswd secret ~/.vnc/passwd
ENV LANGUAGE=en_US.UTF-8
ENV LANG=en_US.UTF-8
RUN locale-gen en_US.UTF-8 && dpkg-reconfigure --frontend noninteractive locales && apt-get update -qqy && apt-get -qqy --no-install-recommends install language-pack-en && rm -rf /var/lib/apt/lists/*
RUN apt-get update -qqy && apt-get -qqy --no-install-recommends install fonts-ipafont-gothic xfonts-100dpi xfonts-75dpi xfonts-cyrillic xfonts-scalable && rm -rf /var/lib/apt/lists/*
RUN apt-get update -qqy && apt-get -qqy install fluxbox && rm -rf /var/lib/apt/lists/*
COPY file:90e3a7f757c3df44d541b59234ad4ca996f799455eb8d426218619b244ebba68 in /opt/bin/entry_point.sh
RUN chmod +x /opt/bin/entry_point.sh
EXPOSE 5900/tcp

Segunda via

Aqui você não precisa puxar a imagem para local e nenhum comando precisa ser executado.

Use a imagem acima como exemplo, você pode obter comandos do Dockerfile através do URL abaixo:

https://imagelayers.io/?images=selenium%2Fnode-firefox-debug:latest

Aguarde um pouco, haverá duas janelas, a janela para cima lista as camadas, a janela para baixo lista o comando no Dockerfile

captura de tela imagelayers.io

O formato do URL é:

https://imagelayers.io/?images=<USER>%2F<IMAGE>:<TAG>

De fato, o imagelayers.io é construído por Centurylink

BMW
fonte
3
Essa é a maneira do Docker e deve ser marcada como resposta escolhida.
precisa saber é
2
@jenson não é exatamente o mesmo, pode cobrir 95%. Mas não funciona com imagem esmagada.
BMW
5
@BMW Você poderia ajudar a resolver esse problema executando a imagem do seu exemplo? /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/unix_socket.rb:14:in `connect_nonblock ': Conexão recusada - connect (2) for / var / run / docker .sock (Errno :: ECONNREFUSED) (Excon :: Erros :: SocketError)
longo
8
O centurylink / dockerfile-from-image não funciona com a nova versão docker. Este funciona para mim: hub.docker.com/r/chenzj/dfimage #
aleung
6
imagelayers.io parece estar quebrado. Ele não consegue encontrar nenhuma imagem, incluindo as de demonstração
Robert Lugg
165

Para entender como uma imagem do Docker foi criada, use o docker history --no-trunccomando

Você pode criar um arquivo de janela de encaixe a partir de uma imagem, mas ele não conterá tudo o que você deseja entender completamente como a imagem foi gerada. Razoavelmente, o que você pode extrair é as partes MANUTENÇÃO, ENV, EXPOSE, VOLUME, WORKDIR, ENTRYPOINT, CMD e ONBUILD do dockerfile.

O script a seguir deve funcionar para você:

#!/bin/bash
docker history --no-trunc "$1" | \
sed -n -e 's,.*/bin/sh -c #(nop) \(MAINTAINER .*[^ ]\) *0 B,\1,p' | \
head -1
docker inspect --format='{{range $e := .Config.Env}}
ENV {{$e}}
{{end}}{{range $e,$v := .Config.ExposedPorts}}
EXPOSE {{$e}}
{{end}}{{range $e,$v := .Config.Volumes}}
VOLUME {{$e}}
{{end}}{{with .Config.User}}USER {{.}}{{end}}
{{with .Config.WorkingDir}}WORKDIR {{.}}{{end}}
{{with .Config.Entrypoint}}ENTRYPOINT {{json .}}{{end}}
{{with .Config.Cmd}}CMD {{json .}}{{end}}
{{with .Config.OnBuild}}ONBUILD {{json .}}{{end}}' "$1"

Uso isso como parte de um script para reconstruir contêineres em execução como imagens: https://github.com/docbill/docker-scripts/blob/master/docker-rebase

O Dockerfile é principalmente útil se você deseja reembalar uma imagem.

O que você deve ter em mente é que uma imagem do docker pode realmente ser apenas o backup do tar de uma máquina real ou virtual. Fiz várias imagens do docker dessa maneira. Até o histórico da compilação mostra como importar um arquivo tar enorme como o primeiro passo na criação da imagem ...

user6856
fonte
1
Para obter mais informações, consulte: json: can unmarshal array in Go value of type types.conttainerJSON
Mohsen
Você pode descrever seu último comentário com mais detalhes? Está tudo dentro / apenas marcado como normal? Ou existem casos especiais?
Robert Lugg
Acho que é uma resposta de 6 anos, mas estou recebendoError response from daemon: page not found
João Ciocca
53

De alguma forma, eu absolutamente perdi o comando real na resposta aceita, então aqui está novamente, um pouco mais visível em seu próprio parágrafo, para ver quantas pessoas são como eu

$ docker history --no-trunc <IMAGE_ID>
user7610
fonte
1
Então, por que precisamos ub.docker.com/r/chenzj/dfimage? É ainda uma resposta mais recente.
Lucid_dreamer 26/01/18
3
Eu acho que porque docker historyimprime as linhas do Dockerfile em uma ordem inversa e solta as RUNinstruções (você obtém apenas o comando em si, não o RUNkeyworkd na frente dele) e outras coisas, então você precisa editá-lo manualmente para obter um Dockerfile montável. Essa outra ferramenta pode fazer essa edição automaticamente para você (eu não tentei, então não sei.)
user7610
@ user7610 seu comando apenas mostra o histórico da imagem retirada do hub. Como eu pude ver meus comandos nas imagens do docker?
BarzanHayati
@BarzanHayati Você pode fazer uma pergunta nova e vinculá-la aqui? Seja muito específico ao perguntar. Mostre o comando para iniciar a imagem, em seguida, emita alguns comandos que você deseja ver posteriormente, como exemplo, pare o contêiner (se é isso que você realmente faz na realidade) e pergunte como recuperar os comandos emitidos. Obrigado.
user7610
1
@ user7610 Eu poderia perguntar, mas assim que perguntei, devo excluí-lo porque outros usuários me dão menos pontos por perguntas repetidas.
BarzanHayati
34

Uma solução bash:

docker history --no-trunc $argv  | tac | tr -s ' ' | cut -d " " -f 5- | sed 's,^/bin/sh -c #(nop) ,,g' | sed 's,^/bin/sh -c,RUN,g' | sed 's, && ,\n  & ,g' | sed 's,\s*[0-9]*[\.]*[0-9]*\s*[kMG]*B\s*$,,g' | head -n -1

Explicações passo a passo:

tac : reverse the file
tr -s ' '                                       trim multiple whitespaces into 1
cut -d " " -f 5-                                remove the first fields (until X months/years ago)
sed 's,^/bin/sh -c #(nop) ,,g'                  remove /bin/sh calls for ENV,LABEL...
sed 's,^/bin/sh -c,RUN,g'                       remove /bin/sh calls for RUN
sed 's, && ,\n  & ,g'                           pretty print multi command lines following Docker best practices
sed 's,\s*[0-9]*[\.]*[0-9]*\s*[kMG]*B\s*$,,g'      remove layer size information
head -n -1                                      remove last line ("SIZE COMMENT" in this case)

Exemplo:

 ~  dih ubuntu:18.04
ADD file:28c0771e44ff530dba3f237024acc38e8ec9293d60f0e44c8c78536c12f13a0b in /
RUN set -xe
   &&  echo '#!/bin/sh' > /usr/sbin/policy-rc.d
   &&  echo 'exit 101' >> /usr/sbin/policy-rc.d
   &&  chmod +x /usr/sbin/policy-rc.d
   &&  dpkg-divert --local --rename --add /sbin/initctl
   &&  cp -a /usr/sbin/policy-rc.d /sbin/initctl
   &&  sed -i 's/^exit.*/exit 0/' /sbin/initctl
   &&  echo 'force-unsafe-io' > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup
   &&  echo 'DPkg::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' > /etc/apt/apt.conf.d/docker-clean
   &&  echo 'APT::Update::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' >> /etc/apt/apt.conf.d/docker-clean
   &&  echo 'Dir::Cache::pkgcache ""; Dir::Cache::srcpkgcache "";' >> /etc/apt/apt.conf.d/docker-clean
   &&  echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/docker-no-languages
   &&  echo 'Acquire::GzipIndexes "true"; Acquire::CompressionTypes::Order:: "gz";' > /etc/apt/apt.conf.d/docker-gzip-indexes
   &&  echo 'Apt::AutoRemove::SuggestsImportant "false";' > /etc/apt/apt.conf.d/docker-autoremove-suggests
RUN rm -rf /var/lib/apt/lists/*
RUN sed -i 's/^#\s*\(deb.*universe\)$/\1/g' /etc/apt/sources.list
RUN mkdir -p /run/systemd
   &&  echo 'docker' > /run/systemd/container
CMD ["/bin/bash"]
Fallino
fonte
1
A solução mais direta. Obrigado!
user3576508
Isso não adiciona uma barra invertida à direita quando quebra instruções RUN com várias linhas. Eu adicionei minha própria resposta inspirada nisso.
Scott Centoni
O tac não está disponível no mac, então você pode usar o awk como abaixo: | awk '{print NR, $ 0}' | ordenar -nr | sed / ^ [0-9] * // '|
phulei
11

Não é possível neste momento (a menos que o autor da imagem inclua explicitamente o Dockerfile).

No entanto, é definitivamente algo útil! Há duas coisas que ajudarão a obter esse recurso.

  1. Construções confiáveis ​​(detalhadas em nesta discussão sobre docker-dev
  2. Metadados mais detalhados nas imagens sucessivas produzidas pelo processo de criação. A longo prazo, os metadados devem indicar qual comando de construção produziu a imagem, o que significa que será possível reconstruir o Dockerfile a partir de uma sequência de imagens.
jpetazzo
fonte
8

Atualizar dez 2018 para a resposta da BMW

docker pull chenzj/dfimage
alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm chenzj/dfimage"
dfimage IMAGE_ID > Dockerfile
AndrewD
fonte
6

Isso é derivado da resposta do @ fallino, com alguns ajustes e simplificações usando a opção de formato de saída para o histórico do docker . Como o macOS e o Gnu / Linux têm diferentes utilitários de linha de comando, uma versão diferente é necessária para o Mac. Se você precisar apenas de um ou de outro, basta usar essas linhas.

#!/bin/bash
case "$OSTYPE" in
    linux*)
        docker history --no-trunc --format "{{.CreatedBy}}" $1 | # extract information from layers
        tac                                                    | # reverse the file
        sed 's,^\(|3.*\)\?/bin/\(ba\)\?sh -c,RUN,'             | # change /bin/(ba)?sh calls to RUN
        sed 's,^RUN #(nop) *,,'                                | # remove RUN #(nop) calls for ENV,LABEL...
        sed 's,  *&&  *, \\\n \&\& ,g'                           # pretty print multi command lines following Docker best practices
    ;;
    darwin*)
        docker history --no-trunc --format "{{.CreatedBy}}" $1 | # extract information from layers
        tail -r                                                | # reverse the file
        sed -E 's,^(\|3.*)?/bin/(ba)?sh -c,RUN,'               | # change /bin/(ba)?sh calls to RUN
        sed 's,^RUN #(nop) *,,'                                | # remove RUN #(nop) calls for ENV,LABEL...
        sed $'s,  *&&  *, \\\ \\\n \&\& ,g'                      # pretty print multi command lines following Docker best practices
    ;;
    *)
        echo "unknown OSTYPE: $OSTYPE"
    ;;
esac
Scott Centoni
fonte
5

docker pull chenzj/dfimage


alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm chenzj/dfimage"

dfimage image_id

abaixo está a saída do comando dfimage: -

$ dfimage 0f1947a021ce

Nó FROM: 8 WORKDIR / usr / src / app

Arquivo COPY: e76d2e84545dedbe901b7b7b0c8d2c9733baa07cc821054efec48f623e29218c in ./

Instalação RUN / bin / sh -c npm

Dir. COPY: a89a4894689a38cbf3895fdc0870878272bb9e09268149a87a6974a274b2184a em.

EXPOSIÇÃO 8080

CMD ["npm" "start"]

user128364
fonte