【问题标题】:How to generate a Dockerfile from an image?如何从图像生成 Dockerfile?
【发布时间】:2026-01-24 08:20:05
【问题描述】:

是否可以从图像生成 Dockerfile?我想知道有两个原因:

  1. 我可以从存储库下载图像,但想查看生成它们的配方。

  2. 我喜欢保存快照的想法,但是一旦我完成后,最好有一个结构化的格式来查看所做的事情。

【问题讨论】:

  • 您可以使用 Portainer.io portainer.io 这是一个运行在 docker 容器内的 Web 应用程序,用于管理有关您的容器的所有(几乎)内容。甚至图像接收。

标签: image repository docker


【解决方案1】:

什么是 image2df

image2df 是通过镜像生成 Dockerfile 的工具。

当你只有 docker 镜像并且需要生成一个 Dockerfile 时,这个工具非常有用。

它是如何工作的

通过图像的历史信息进行反向解析。

如何使用这张图片

# Command alias
echo "alias image2df='docker run -v /var/run/docker.sock:/var/run/docker.sock --rm cucker/image2df'" >> ~/.bashrc
. ~/.bashrc

# Excute command
image2df <IMAGE>
  • 查看帮助

    docker run --rm cucker/image2df --help
    
  • 例如

    $ echo "alias image2df='docker run -v /var/run/docker.sock:/var/run/docker.sock --rm cucker/image2df'" >> ~/.bashrc
    $ . ~/.bashrc
    $ docker pull mysql
    $ image2df mysql
    
    ========== Dockerfile ==========
    FROM mysql:latest
    RUN groupadd -r mysql && useradd -r -g mysql mysql
    RUN apt-get update && apt-get install -y --no-install-recommends gnupg dirmngr && rm -rf /var/lib/apt/lists/*
    ENV GOSU_VERSION=1.12
    RUN set -eux; \
        savedAptMark="$(apt-mark showmanual)"; \
        apt-get update; \
        apt-get install -y --no-install-recommends ca-certificates wget; \
        rm -rf /var/lib/apt/lists/*; \
        dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
        wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
        wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
        export GNUPGHOME="$(mktemp -d)"; \
        gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
        gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
        gpgconf --kill all; \
        rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
        apt-mark auto '.*' > /dev/null; \
        [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
        apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
        chmod +x /usr/local/bin/gosu; \
        gosu --version; \
        gosu nobody true
    RUN mkdir /docker-entrypoint-initdb.d
    RUN apt-get update && apt-get install -y --no-install-recommends \
            pwgen \
            openssl \
            perl \
            xz-utils \
        && rm -rf /var/lib/apt/lists/*
    RUN set -ex; \
        key='A4A9406876FCBD3C456770C88C718D3B5072E1F5'; \
        export GNUPGHOME="$(mktemp -d)"; \
        gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
        gpg --batch --export "$key" > /etc/apt/trusted.gpg.d/mysql.gpg; \
        gpgconf --kill all; \
        rm -rf "$GNUPGHOME"; \
        apt-key list > /dev/null
    ENV MYSQL_MAJOR=8.0
    ENV MYSQL_VERSION=8.0.24-1debian10
    RUN echo 'deb http://repo.mysql.com/apt/debian/ buster mysql-8.0' > /etc/apt/sources.list.d/mysql.list
    RUN { \
            echo mysql-community-server mysql-community-server/data-dir select ''; \
        echo mysql-community-server mysql-community-server/root-pass password ''; \
        echo mysql-community-server mysql-community-server/re-root-pass password ''; \
        echo mysql-community-server mysql-community-server/remove-test-db select false; \
        } | debconf-set-selections \
        && apt-get update \
        && apt-get install -y \
            mysql-community-client="${MYSQL_VERSION}" \
            mysql-community-server-core="${MYSQL_VERSION}" \
        && rm -rf /var/lib/apt/lists/* \
        && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql /var/run/mysqld \
        && chown -R mysql:mysql /var/lib/mysql /var/run/mysqld \
        && chmod 1777 /var/run/mysqld /var/lib/mysql
    VOLUME [/var/lib/mysql]
    COPY dir:2e040acc386ebd23b8571951a51e6cb93647df091bc26159b8c757ef82b3fcda in /etc/mysql/
    COPY file:345a22fe55d3e6783a17075612415413487e7dba27fbf1000a67c7870364b739 in /usr/local/bin/
    RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat
    ENTRYPOINT ["docker-entrypoint.sh"]
    EXPOSE 3306 33060
    CMD ["mysqld"]
    
  • reference

【讨论】:

  • 这与上一个答案的工作方式相同,只是图像不同。同样奇怪的是,这将FROM 部分显示为同一图像。然而,先前的答案甚至没有返回 FROM 部分。
  • 要获得准确的basc-image tag,您应该构建一个库,用于使用文件 sha256 值映射图像标签。因为图像历史信息中只有一个与图像标签相关的命令,如ADD file:f278386b0cef68136129f5f58c52445590a417b624d62bca158d4dc926c340df in /
【解决方案2】:

如果您对 Docker 中心注册表中的映像感兴趣并想查看 Dockerfile?。

例子:

如果你想查看镜像“jupyter/datascience-notebook”的Dockerfile,如下图在浏览器地址栏输入“Dockerfile”。

https://hub.docker.com/r/jupyter/datascience-notebook/

https://hub.docker.com/r/jupyter/datascience-notebook/Dockerfile

注意: 并非所有镜像都有 Dockerfile,例如,https://hub.docker.com/r/redislabs/redisinsight/Dockerfile 有时这种方式比在 Github 中搜索 Dockerfile 要快得多。

【讨论】:

    【解决方案3】:

    如何从图像生成或反转 Dockerfile?

    你可以。大部分。

    注意:它不会生成可以直接与docker build一起使用的Dockerfile,输出仅供参考。

    alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage"
    dfimage -sV=1.36 nginx:latest
    

    它会自动拉取目标docker镜像并导出Dockerfile。参数-sV=1.36 并不总是必需的。

    参考:https://hub.docker.com/repository/docker/alpine/dfimage

    如果您选择特定标签,现在 hub.docker.com 会直接显示带有详细命令的图像层。

    奖金

    如果你想知道每一层有哪些文件发生了变化

    alias dive="docker run -ti --rm  -v /var/run/docker.sock:/var/run/docker.sock wagoodman/dive"
    dive nginx:latest
    

    左边是每一层的命令,右边(用tab跳转),黄线是该层中一些文件被改变的文件夹

    (使用SPACE折叠目录)

    旧答案

    下面是旧答案,它不再起作用了。

    $ 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
    

    【讨论】:

    • 这是 Docker 方式,应该标记为选择的答案。
    • @jenson 不完全相同,可以覆盖 95%。但不适用于压缩图像。
    • @BMW 您能否帮助解决从您的示例运行图像的问题? /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/unix_socket.rb:14:in `connect_nonblock':连接被拒绝 - /var/run/docker 的连接(2) .sock (Errno::ECONNREFUSED) (Excon::Errors::SocketError)
    • Centurylink/dockerfile-from-image 不适用于新版本的 docker。这个对我有用:hub.docker.com/r/chenzj/dfimage
    • imagelayers.io 似乎坏了。它找不到任何图像,包括它的演示图像
    【解决方案4】:

    2018 年 12 月更新到宝马的回答

    chenzj/dfimage - 如 hub.docker.com 上所述,从其他图像重新生成 Dockerfile。所以你可以按如下方式使用它:

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

    【讨论】:

    • 似乎不适用于 19.03.8:docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"cc6cb8df58e2\": executable file not found in $PATH": unknown.
    • @TrevorHickey 我遇到了同样的问题。您是否将 chenzj/dfimage 重命名为您请求的 docker 映像?
    • 还有这个的 git repo 在哪里?
    【解决方案5】:

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

    下面是 dfimage 命令的输出:

    $ dfimage 0f1947a021ce
    
    FROM node:8
    WORKDIR /usr/src/app
    
    COPY file:e76d2e84545dedbe901b7b7b0c8d2c9733baa07cc821054efec48f623e29218c in ./
    RUN /bin/sh -c npm install
    COPY dir:a89a4894689a38cbf3895fdc0870878272bb9e09268149a87a6974a274b2184a in .
    
    EXPOSE 8080
    CMD ["npm" "start"]
    

    【讨论】:

      【解决方案6】:

      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
      

      分步说明:

      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)
      

      例子:

       ~  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"]
      

      【讨论】:

      • 最直接的解决方案。谢谢!
      • 这在分解多行 RUN 语句时不会添加尾部反斜杠。受此启发,我添加了自己的答案。
      • tac 在 mac 上不可用,因此您可以使用 awk,如下所示: | awk '{打印 NR,$0}' |排序-nr | sed 's/^[0-9]* //'|
      • 我投赞成票是因为它可以与 podman 一起使用(并且系统中没有安装 docker)
      【解决方案7】:

      这源自@fallino 的答案,通过使用docker history 的输出格式选项进行了一些调整和简化。由于 macOS 和 Gnu/Linux 具有不同的命令行实用程序,因此 Mac 需要不同的版本。如果您只需要其中一个,则可以使用这些行。

      #!/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
      

      【讨论】:

        【解决方案8】:

        不知何故,我完全错过了接受答案中的实际命令,所以这里再次出现,在它自己的段落中更明显,看看有多少人像我一样

        $ docker history --no-trunc <IMAGE_ID>
        

        【讨论】:

        • 那么为什么我们需要ub.docker.com/r/chenzj/dfimage?这甚至是一个更新的答案。
        • 我猜是因为 docker history 以相反的顺序打印 Dockerfile 行并且它删除了 RUN 指令(你只得到命令本身,而不是它前面的 RUN keyworkd)和其他的东西,所以你需要手动编辑它以获得可构建的 Dockerfile。其他工具可能会自动为您进行此编辑(我没有尝试过,所以我不知道。)
        • @user7610 您的命令仅显示从集线器拉取图像的历史记录。我如何在 docker 镜像上看到我的命令?
        • @BarzanHayati 您能否将其作为一个新问题提出并在此处链接?问的时候要非常具体。显示启动映像的命令,然后发出一些您想稍后看到的命令,例如,然后停止容器(如果这是您在现实中实际执行的操作),然后询问如何检索发出的命令。谢谢。
        • @user7610 我可以问它,但我一问就必须删除它,因为其他用户重复问题给我减分。
        【解决方案9】:

        要了解如何构建 docker 映像,请使用 docker history --no-trunc 命令。

        您可以从图像构建 docker 文件,但它不会包含您想要完全了解图像是如何生成的所有内容。您可以合理地提取 dockerfile 的 MAINTAINER、ENV、EXPOSE、VOLUME、WORKDIR、ENTRYPOINT、CMD 和 ONBUILD 部分。

        以下脚本应该适合您:

        #!/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"
        

        我使用它作为脚本的一部分来将正在运行的容器重建为图像: https://github.com/docbill/docker-scripts/blob/master/docker-rebase

        如果您希望能够重新打包映像,Dockerfile 主要是有用的。

        要记住的是,docker 映像实际上可以只是真实或虚拟机的 tar 备份。我用这种方式制作了几个 docker 图像。甚至构建历史也显示我导入一个巨大的 tar 文件作为创建图像的第一步......

        【讨论】:

        • 它让我明白了:json: 无法将数组解组为 types.ContainerJSON 类型的 Go 值
        • 你能更详细地描述你最后的评论吗?一切都在/只是正常吗?还是有特殊情况?
        • 我认为这是一个 6 岁的答案,但我收到了 Error response from daemon: page not found
        【解决方案10】:

        目前不可能(除非图像的作者明确包含 Dockerfile)。

        但是,它绝对是有用的!有两件事将有助于获得此功能。

        1. 受信任的构建(详细信息在this docker-dev discussion
        2. 构建过程生成的连续图像中的更详细的元数据。从长远来看,元数据应该表明是哪个构建命令生成了映像,这意味着可以从一系列映像重建 Dockerfile。

        【讨论】: