Git Product home page Git Product logo

dockerfile-from-image's Introduction

NOTE

This repo is no longer being maintained. Users are welcome to fork it, but we make no warranty of its functionality.

dockerfile-from-image

Reverse-engineers a Dockerfile from a Docker image.

Similar to how the docker history command works, the dockerfile-from-image script is able to re-create the Dockerfile (approximately) that was used to generate an image using the metadata that Docker stores alongside each image layer.

Usage

The Ruby dockerfile-from-image script is itself packaged as a Docker image so it can easily be executed with the Docker run command:

docker run -v /var/run/docker.sock:/var/run/docker.sock \
  centurylink/dockerfile-from-image <IMAGE_TAG_OR_ID>

The <IMAGE_TAG_OR_ID> parameter can be either an image tag (e.g. ruby) or an image ID (either the truncated form or the complete image ID).

Since the script interacts with the Docker API in order to query the metadata for the various image layers it needs access to the Docker API socket. The -v flag shown above makes the Docker socket available inside the container running the script.

Note that the script only works against images that exist in your local image repository (the stuff you see when you type docker images). If you want to generate a Dockerfile for an image that doesn't exist in your local repo you'll first need to docker pull it.

Example

Here's an example that shows the official Docker ruby image being pulled and the Dockerfile for that image being generated.

$ docker pull ruby
Pulling repository ruby

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

$ docker run --rm -v /run/docker.sock:/run/docker.sock centurylink/dockerfile-from-image ruby
FROM buildpack-deps:latest
RUN useradd -g users user
RUN apt-get update && apt-get install -y bison procps
RUN apt-get update && apt-get install -y ruby
ADD dir:03090a5fdc5feb8b4f1d6a69214c37b5f6d653f5185cddb6bf7fd71e6ded561c in /usr/src/ruby
WORKDIR /usr/src/ruby
RUN chown -R user:users .
USER user
RUN autoconf && ./configure --disable-install-doc
RUN make -j"$(nproc)"
RUN make check
USER root
RUN apt-get purge -y ruby
RUN make install
RUN echo 'gem: --no-rdoc --no-ri' >> /.gemrc
RUN gem install bundler
ONBUILD ADD . /usr/src/app
ONBUILD WORKDIR /usr/src/app
ONBUILD RUN [ ! -e Gemfile ] || bundle install --system

Run it as local command

$ docker pull centurylink/dockerfile-from-image
$ alias dfimage="docker run --rm -v /var/run/docker.sock:/var/run/docker.sock 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
$ dfimage ruby

How Does It Work?

When an image is constructed from a Dockerfile, each instruction in the Dockerfile results in a new layer. You can see all of the image layers by using the docker images command with the (soon-to-deprecated) --tree flag.

$ docker images --tree
Warning: '--tree' is deprecated, it will be removed soon. See usage.
└─511136ea3c5a Virtual Size: 0 B Tags: scratch:latest
  └─1e8abad02296 Virtual Size: 121.8 MB
    └─f106b5d7508a Virtual Size: 121.8 MB
      └─0ae4b97648db Virtual Size: 690.2 MB
        └─a2df34bb17f4 Virtual Size: 808.3 MB Tags: buildpack-deps:latest
          └─86258af941f7 Virtual Size: 808.6 MB
            └─1dc22fbdefef Virtual Size: 846.7 MB
              └─00227c86ea87 Virtual Size: 863.7 MB
                └─564e6df9f1e2 Virtual Size: 1.009 GB
                  └─55a2d383d743 Virtual Size: 1.009 GB
                    └─367e535883e4 Virtual Size: 1.154 GB
                      └─a47bb557ed2a Virtual Size: 1.154 GB
                        └─0d4496202bc0 Virtual Size: 1.157 GB
                          └─5db44b586412 Virtual Size: 1.446 GB
                            └─bef6f00c8d6d Virtual Size: 1.451 GB
                              └─5f9bee597a47 Virtual Size: 1.451 GB
                                └─bb98b84e0658 Virtual Size: 1.452 GB
                                  └─6556c531b6c1 Virtual Size: 1.552 GB
                                    └─569e14fd7575 Virtual Size: 1.552 GB
                                      └─fc3a205ba3de Virtual Size: 1.555 GB
                                        └─5fd3b530d269 Virtual Size: 1.555 GB
                                          └─6bdb3289ca8b Virtual Size: 1.555 GB
                                            └─011aa33ba92b Virtual Size: 1.555 GB Tags: ruby:2, ruby:2.1, ruby:2.1.1, ruby:latest

Each one of these layers is the result of executing an instruction in a Dockerfile. In fact, if you do a docker inspect on any one of these layers you can see the instruction that was used to generate that layer.

$ docker inspect 011aa33ba92b
[{
  . . .
  "ContainerConfig": {
    "Cmd": [
        "/bin/sh",
        "-c",
        "#(nop) ONBUILD RUN [ ! -e Gemfile ] || bundle install --system"
    ],
    . . .
}]

The output above has been truncated, but nested within the ContainerConfig data you'll find the Dockerfile command that generated this layer (in this case it was an ONBUILD instruction).

The dockerfile-from-image script works by simply walking backward through the layer tree and collecting the commands stored with each layer. When the script reaches the first tagged layer (or the root of the tree) it stops and displays the (reversed) list of commands. If you want to generate the commands going all the way back to the root image layer you can use the -f flag to walk the entire tree.

Limitations

As the dockerfile-from-image script walks the list of layers contained in the image it stops when it reaches the first tagged layer. It is assumed that a layer which has been tagged represents a distinct image with its own Dockerfile so the script will output a FROM directive with the tag name.

In the example above, the ruby image contained a layer in the local image repository which had been tagged with buildpack-deps (though it wasn't shown in the example, this likely means that buildpack-deps:latest was also pulled at some point). If the buildpack-deps layer had not been tagged, the dockerfile-from-image script would have continued outputing Dockerfile directives until it reached the root layer.

Also note that the output generated by the script won't match exactly the original Dockerfile if either the COPY or ADD directives (like the example above) are used. Since we no longer have access to the build context that was present when the original docker build command was executed all we can see is that some directory or file was copied to the image's filesystem (you'll see the file/directory checksum and the destination it was copied to).

dockerfile-from-image's People

Contributors

bdehamer avatar jumping avatar ozbillwang avatar patocox avatar rossjimenez avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dockerfile-from-image's Issues

To do list

@bdehamer

This is my own to-do list, if you think that's good idea, then I will work on them. Or you can help directly, let me know.

  1. Add the function if the test image is not exist, pull it directly
  2. Remove the useless space from output. For example:
RUN apt-get update && apt-get install -y --no-install-recommends        bzr         git         mercurial       openssh-client      subversion  && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y --no-install-recommends        autoconf        automake        bzip2       file        g++         gcc         imagemagick         libbz2-dev      libc6-dev       libcurl4-openssl-dev        libevent-dev        libffi-dev      libglib2.0-dev      libjpeg-dev         liblzma-dev         libmagickcore-dev       libmagickwand-dev       libmysqlclient-dev      libncurses-dev      libpq-dev       libreadline-dev         libsqlite3-dev      libssl-dev      libtool         libwebp-dev         libxml2-dev         libxslt-dev         libyaml-dev         make        patch       xz-utils        zlib1g-dev  && rm -rf /var/lib/apt/lists/*

to

RUN apt-get update && apt-get install -y --no-install-recommends bzr git mercurial openssh-client subversion && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y --no-install-recommends autoconf automake bzip2 file g++ gcc imagemagick libbz2-dev libc6-dev libcurl4-openssl-dev libevent-dev libffi-dev libglib2.0-dev libjpeg-dev liblzma-dev libmagickcore-dev libmagickwand-dev libmysqlclient-dev libncurses-dev libpq-dev libreadline-dev libsqlite3-dev libssl-dev libtool libwebp-dev libxml2-dev libxslt-dev libyaml-dev make patch xz-utils zlib1g-dev && rm -rf /var/lib/apt/lists/*

can't get the FROM image name.

When run this docker command, I got list of Dockerfile commands, but there is no FROM part on the top.

So how can I know the image start from?

$ docker run -v /var/run/docker.sock:/var/run/docker.sock centurylink/dockerfile-from-image selenium/base

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

"Cannot start container " when trying to reverse engineer Dockerfile

The reverse engineering of Dockerfile from Docker image seems to fail inconsistently. The test case used is "dockerfile-from-image" itself. Seems to be somehow related to failure in unmounting image.

Details below .. Any clues?

ubuntu@ip-10-0-0-168:~$ docker --version
Docker version 1.4.1, build 5bc2ff8

ubuntu@ip-10-0-0-168:~$ sudo docker images
REPOSITORY                          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centurylink/dockerfile-from-image   latest              00c8dfdef4c3        5 months ago        340.2 MB
...
...

ubuntu@ip-10-0-0-168:~$ sudo docker run -v /var/run/docker.sock:/var/run/docker.sock   centurylink/dockerfile-from-image centurylink/dockerfile-from-image
FATA[0000] Error response from daemon: Cannot start container babcf5872ed314d119f5bd12036d0793960ba83a1faa3ea36f8487e265ae8eec: mkdir /sys/fs/cpuset: no such file or directory 

ubuntu@ip-10-0-0-168:~$ sudo docker run -v /var/run/docker.sock:/var/run/docker.sock   centurylink/dockerfile-from-image centurylink/dockerfile-from-imageFATA[0000] Error response from daemon: Cannot start container c6e0aebf3a986215b5199d3b5cba52ac8978671f6873b60f00289abec8d9e7bf: mkdir /sys/fs/cpu: no such file or directory 

Thanks!

Invalid CMD and ENTRYPOINT produced

I'm using Docker 1.10, not sure if this is version specific. If I have a Dockerfile that looks like this:

CMD ["echo","hello"]
ENTRYPOINT ["echo","hello"]

The reverse engineered version looks like this:

CMD ["echo" "hello"]
ENTRYPOINT &{["echo" "hello"]}

Neither of these actually work like the original Dockerfile commands. Both would need a comma separating the quoted strings, and additionally ENTRYPOINT needs the&{ and } removed.

Failed to run

Could you please help to resolve the error?

/usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/unix_socket.rb:14:in `connect_nonblock': Connection refused - connect(2) for /var/run/docker.sock (Errno::ECONNREFUSED) (Excon::Errors::SocketError)
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/unix_socket.rb:14:in `connect'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/socket.rb:28:in `initialize'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/connection.rb:385:in `new'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/connection.rb:385:in `socket'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/connection.rb:106:in `request_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/mock.rb:47:in `request_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/instrumentor.rb:22:in `request_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:15:in `request_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:15:in `request_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:15:in `request_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:15:in `request_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/connection.rb:233:in `request'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/idempotent.rb:26:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:10:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:10:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:10:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/connection.rb:256:in `rescue in request'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/connection.rb:204:in `request'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/idempotent.rb:26:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:10:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:10:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:10:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/connection.rb:256:in `rescue in request'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/connection.rb:204:in `request'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/idempotent.rb:26:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:10:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:10:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/middlewares/base.rb:10:in `error_call'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/connection.rb:256:in `rescue in request'
    from /usr/lib/ruby/gems/2.2.0/gems/excon-0.45.4/lib/excon/connection.rb:204:in `request'
    from /usr/lib/ruby/gems/2.2.0/gems/docker-api-1.24.1/lib/docker/connection.rb:40:in `request'
    from /usr/lib/ruby/gems/2.2.0/gems/docker-api-1.24.1/lib/docker/connection.rb:65:in `block (2 levels) in <class:Connection>'
    from /usr/lib/ruby/gems/2.2.0/gems/docker-api-1.24.1/lib/docker/image.rb:172:in `all'
    from /usr/src/app/dockerfile-from-image.rb:32:in `<main>'

Docker info:
docker info
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 496
Server Version: 1.11.1
Storage Driver: aufs
Root Dir: /var/lib/docker/aufs
Backing Filesystem: extfs
Dirs: 660
Dirperm1 Supported: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: host bridge null
Kernel Version: 3.13.0-85-generic
Operating System: Ubuntu 14.04.4 LTS
OSType: linux
Architecture: x86_64
CPUs: 8
Total Memory: 15.37 GiB
Name: XXX
ID: FMZO:5B63:3PIT:K463:SYKZ:CTIW:OYXI:FXVZ:HDYI:JZKE:BOEO:QDZT
Docker Root Dir: /var/lib/docker
Debug mode (client): false
Debug mode (server): false
Username: XXX
Registry: https://index.docker.io/v1/
WARNING: No swap limit support

Output of dockerfile-from-image only shows last command

Using Docker version 1.9.1, build ab77bde/1.9.1, only the last command is displayed instead of the entire recipe:

docker run --rm -v /var/run/docker.sock:/var/run/docker.sock centurylink/dockerfile-from-image -f xxxxxxxxxxxx
CMD ["sh"]

bug about the USER directive

a buglet

When launching

docker run -v /var/run/docker.sock:/var/run/docker.sock centurylink/dockerfile-from-image k3ck3c/captvty

I get, among other things

USER [gg]

when it should be

USER gg

of course the created Dockerfile does not work, as this is not a "legal" user.

Thanks for this great tool.

doesn't work with docker 1.10

getting the following when running with docker 1.10:

 docker run -v /var/run/docker.sock:/var/run/docker.sock centurylink/dockerfile-from-image cloudera/quickstart:latest
/usr/lib/ruby/gems/2.2.0/gems/docker-api-1.24.1/lib/docker/connection.rb:42:in `rescue in request': 400 Bad Request: malformed Host header (Docker::Error::ClientError)
        from /usr/lib/ruby/gems/2.2.0/gems/docker-api-1.24.1/lib/docker/connection.rb:38:in `request'
        from /usr/lib/ruby/gems/2.2.0/gems/docker-api-1.24.1/lib/docker/connection.rb:65:in `block (2 levels) in <class:Connection>'
        from /usr/lib/ruby/gems/2.2.0/gems/docker-api-1.24.1/lib/docker/image.rb:172:in `all'
        from /usr/src/app/dockerfile-from-image.rb:32:in `<main>

Bad Request: malformed Host header (Docker::Error::ClientError)

I'm getting this error. I tried with other images, and finally with the dockerfile-from-image itself

$ alias dfimage="docker run --rm -v /var/run/docker.sock:/var/run/docker.sock centurylink/dockerfile-from-image"
$ dfimage -f centurylink/dockerfile-from-image
/usr/lib/ruby/gems/2.2.0/gems/docker-api-1.24.1/lib/docker/connection.rb:42:in `rescue in request': 400 Bad Request: malformed Host header (Docker::Error::ClientError)
from /usr/lib/ruby/gems/2.2.0/gems/docker-api-1.24.1/lib/docker/connection.rb:38:in `request'
from /usr/lib/ruby/gems/2.2.0/gems/docker-api-1.24.1/lib/docker/connection.rb:65:in `block (2 levels) in <class:Connection>'
from /usr/lib/ruby/gems/2.2.0/gems/docker-api-1.24.1/lib/docker/image.rb:172:in `all'
from /usr/src/app/dockerfile-from-image.rb:32:in `<main>'

The computer is a Ubuntu Server 16.04 with Docker 1.12.5. May you help me, please?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.