Docker:从镜像中提取层

24

以 whalesay 图像为例。 docker history 显示如下:

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

我想提取标有ADD file:bla的层。是否有工具/方法可以做到这一点?


"提取" 的意思是什么? - Matt Schuchard
根据 https://github.com/docker/docker/blob/master/image/spec/v1.md,一个层是一个 JSON 文件和实际对文件系统的更改。因此理想情况下,提取将给我两者。 - lang2
7个回答

36

在这种特殊情况下,看起来 ADD 命令将基础镜像添加到文件系统中。如果您运行 docker history --no-trunc docker/whalesay 命令,则完整命令如下:

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

docker history 报告特定层的大小为188MB。让我们更详细地查看这些层:

docker history 报告特定层的大小为188MB。让我们更详细地查看这些层:

$ 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
...

看起来是一个相当不错的候选者!现在,您可以提取该层并将文件从中拿出来。

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

如果你想从一个图层中获取特定的文件,但是你不确定是哪一个图层,你可以这样做。首先,提取所有的图层:

$ tar xf whalesay.tar

现在您已经拥有了所有层作为单独的.tar文件。 让我们找一个文件:

$ 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

最后,从您想要的层中提取文件:

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

这将提取该文件的完整路径,相对于当前目录。

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

11

看起来其他人也希望拥有这个功能,但不幸的是目前似乎不存在。

另请参阅此问题以及这里相关请求,但都已被拒绝。

如果您愿意保存完整的docker (docker save) 然后提取一个包含您的层的tarball,则可以实现此操作:

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

现在foobar.tar将包含来自不同时间的文件系统状态。检查这个tarball,在我的例子中会显示一个名为repositories的文件。

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

这表明最新的层是fdf43...。您可以通过以下方式获取包含此层文件系统内容的tarball:

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

有一个工具undocker可以自动化这个过程,但我不确定它是否适用于保存的tar文件的当前格式。


2
虽然不具备提取特定层的能力,但docker-save-last-layer命令行实用程序可用于仅提取最后一层。结合docker build --squash,您可以避免导出基础层。这可能有助于实现您的目标。
它通过使用一个修补过的 docker 守护进程版本在 docker 映像中运行,该进程可以访问主机上的映像。因此,在使用之前不需要执行完整的 docker save。这使得它对于大型基础映像具有高性能。
典型的用法很简单,如下所示:
pip install d-save-last

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

2

我不是很明白你所说的“提取”是什么意思,但如果你想获取有关图像的更多信息,请运行

docker inspect <image_name>

如果您想获取文件,则需要从此镜像中运行容器。请尝试

docker export <container_name> > abc.tar

接下来,解压abc.tar并找到您的文件。


2
这基本上是我想要的,但我希望能够指定一个图层而不是整个东西。 - lang2

1

1
regclient/regclient 项目包含一个 regctl blob/regctl later,可用于获取特定层。
您可以使用 regctl image manifest 列出层。
regctl v0.4.5 起,您甚至可以使用 (PR 296):
  • regctl blob get-file 从层中获取文件
  • regctl image get-file 从镜像层中获取文件
例如:
$ 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,
    ...

0
对于包含一个 layer.tar 的 Docker 镜像,这将起作用。
docker save myimage:latest | tar xO --wildcards '*.tar'  | tar xv myfile

第一步将myimage作为tar提取到标准输出。 在第二步中,通常是layer.tar,位于dockerfile中的'*.tar'被选择并解压缩到标准输出。在第三步中,我们从标准输出获取layer.tar,并解压缩我们想要选择的任何文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接