简体   繁体   中英

docker: extracting a layer from a image

Let's take the whalesay images as an example. docker history shows the following:

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
6b362a9f73eb        17 months ago       /bin/sh -c #(nop) ENV PATH=/usr/local/bin:/us   0 B
<missing>           17 months ago       /bin/sh -c sh install.sh                        30.37 kB
<missing>           17 months ago       /bin/sh -c git reset --hard origin/master       43.27 kB
<missing>           17 months ago       /bin/sh -c #(nop) WORKDIR /cowsay               0 B
<missing>           17 months ago       /bin/sh -c git clone https://github.com/moxie   89.9 kB
<missing>           17 months ago       /bin/sh -c apt-get -y update && apt-get insta   58.58 MB
<missing>           18 months ago       /bin/sh -c #(nop) CMD ["/bin/bash"]             0 B
<missing>           18 months ago       /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/   1.895 kB
<missing>           18 months ago       /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic   194.5 kB
<missing>           18 months ago       /bin/sh -c #(nop) ADD file:f4d7b4b3402b5c53f2   188.1 MB

I'd like to extract the layer that says ADD file:bla . Is there a tool/way for doing this?

In this specific case, it looks like the ADD command added the base image to the file system. If you run docker history --no-trunc docker/whalesay , the full command is:

/bin/sh -c #(nop) ADD file:f4d7b4b3402b5c53f266bb7fdd7e728493d9a17f9ef20c8cb1b4759b6e66b70f in /

docker history reports that particular layer is 188MB. Let's look at these layers in more detail:

$ docker save docker/whalesay -o whalesay.tar
$ tar tvf whalesay.tar

...
-rw-r--r-- 0/0       197181952 2015-05-25 22:04 cc88f763e297503d2407d6b462b2b390a6fd006b30f51c8efa03dd88fa801b89/layer.tar
...

Looks like a pretty good candidate! You can now extract that layer and pull files out of it.

$ tar xf whalesay.tar cc88f763e297503d2407d6b462b2b390a6fd006b30f51c8efa03dd88fa801b89/layer.tar
$ tar xf cc88f763e297503d2407d6b462b2b390a6fd006b30f51c8efa03dd88fa801b89/layer.tar etc/passwd

If you're looking to pull a particular file out of a layer, but you don't know which layer, you could do this. First, extract all the layers:

$ tar xf whalesay.tar

Now you've got all the layers as individual .tar files. Let's find a file:

$ for layer in */layer.tar; do tar -tf $layer | grep docker.cow && echo $layer; done
usr/local/share/cows/docker.cow
0523c5a0c4588dde33d61d171c41c2dc5c829db359f4d56ab896ab1c185ed936/layer.tar
cowsay/cows/docker.cow
40e8ae7bb4e5b9eaac56f5be7aa614ed50f163020c87ba59e905e01ef0af0a4f/layer.tar
cowsay/cows/docker.cow
f9bc8676543761ff3033813257937aeb77e9bc84296eaf025e27fe01643927cf/layer.tar

Finally, extract the file from the layer you want:

$ tar xf 0523c5a0c4588dde33d61d171c41c2dc5c829db359f4d56ab896ab1c185ed936/layer.tar \
      usr/local/share/cows/docker.cow

This will extract that file with the full path relative to the current directory.

$ cat usr/local/share/cows/docker.cow 
##
## Docker Cow
##
$the_cow = <<EOC;
    $thoughts
     $thoughts
      $thoughts     
                    ##        .            
              ## ## ##       ==            
           ## ## ## ##      ===            
       /""""""""""""""""\___/ ===        
  ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~   
       \\______ o          __/            
        \\    \\        __/             
          \\____\\______/   
EOC

It looks like other people would also like to have this feature, but unfortunately right now it does not seem to exist.

See also this issue and here a related request which got rejected.

If you're fine with saving the complete docker ( docker save ) and then extracting a tarball with your layer, then this is possible:

docker run -it <your image>
# do fancy stuff in the container
docker commit <your container> foobar # create image from container
docker history foobar # will show you the layers
docker save -o foobar.tar foobar # dumps container contents to foobar.tar

Now foobar.tar will contain the file system states from different times. Inspecting this tarball shows, in my case, a file repositories with

{"foobar":{"latest":"fdf43d96e691c57e9afb4b85dba2e6745146a7ca9076c7284c6b2e1f93434562"}}

which indicates, that the latest layer is fdf43... . You can get a tarball with the file system contents of this layer via

tar -x fdf43d96e691c57e9afb4b85dba2e6745146a7ca9076c7284c6b2e1f93434562/layer.tar -f foobar.tar

There is a tool, undocker , which automated this process, but I'm not sure whether it will work with the current format of the saved tar file.

I don't really understand what you mean by "extract" but if you want to get further information about image, run

docker inspect <image_name>

You you want to get file, then run container from this image. Try

docker export <container_name> > abc.tar

after that, extract abc.tar and find your file.

The regclient/regclient project does include a regctl blob /regctl later which allows to fetch a specific layer.

You can list layers with regctl image manifest .

Since regctl v0.4.5 , you can even use ( PR 296 ):

  • regctl blob get- file to fetch a file from a layer
  • regctl image get-file to fetch a file from the image layers

Example:

$ regctl image manifest busybox
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
  "config": {
    "mediaType": "application/vnd.docker.container.image.v1+json",
    "size": 1493,
    "digest": "sha256:6858809bf669cc5da7cb6af83d0fae838284d12e1be0182f92f6bd96559873e3"
  },
  "layers": [
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 764618,
      "digest": "sha256:df8698476c65c2ee7ca0e9dbc2b1c8b1c91bce555819a9aaab724ac64241ba67"
    }
  ]
}

$ regctl blob get busybox sha256:6858809bf669cc5da7cb6af83d0fae838284d12e1be0182f92f6bd96559873e3 | jq .
{
  "architecture": "amd64",
  "config": {
    "Hostname": "",
    "Domainname": "",
    "User": "",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    ...

Docker is not capable of saving layers individually, however there is a tool on Github called dlgrab that claims to do it. https://github.com/aidanhs/dlgrab

While not capable of extracting a specific layer, the docker-save-last-layer command line utility is made to extract the last layer only. Combined with docker build --squash you can avoid exporting the base layers. This may help to accomplish your goals.

It works by using a patched version of the docker daemon inside a docker image that can access the images on your host machine. So it doesn't require doing a full docker save before using it. This makes it performant for large base images.

Typical usage is simple and looks like:

pip install d-save-last

docker build --t myimage --squash .
d-save-last myimage -o ./myimage.tar

For docker images that contain one layer.tar, this works.

docker save myimage:latest | tar xO --wildcards '*.tar'  | tar xv myfile

First step extracts myimage as tar to stdout. In the second step the '*.tar', which is in a dockerfile is usually layer.tar is selected and untared to stdout. In the third step we get the layer.tar from stdout and untar whatever file we want to pick

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM