技巧114 从镜像中提取文件
从镜像中复制文件使用 docker cp
命令很容易完成。但是不常见的情况下,可能你想从镜像中提取文件,但你没有运行干净的容器来进行复制。在这类情况下,可以人工运行一个该镜像的容器,执行 docker cp
,然后移除这一容器。这已经是3条命令了,然后可能会遇到问题,比如,镜像有一个需要传某种有意义的参数的默认入口点。
本技巧提供了一种单条命令的别名(alias),可放置在shell的启动脚本里来用一条命令两个参数来做这些事情。
问题
想要从镜像中复制文件到宿主机。
使用别名从带有入口点的镜像运行容器,用 cat
命令把某个文件的内容输出到宿主机上的文件中。
我们先展示如何构建一个 docker run
命令从该镜像中提取文件,然后展示如何从方便的角度把它转换成一个别名,如代码清单16-4所示。
代码清单16-4 使用docker run从镜像中提取文件
$ docker run --rm \ ⇽--- 使用--rm标志在执行此命令后立刻删除容器
-i \ ⇽--- 使用-i标志来让容器可互动
-t \ ⇽--- 使用-t标志来给容器一个可写入的虚拟终端
--entrypoint=cat \ ⇽--- 把容器的入口文件设置为cat
ubuntu \ ⇽--- 你想要从中提取文件的镜像的名字
/etc/os-release \ ⇽--- 要输出的文件名
> ubuntu_os-release ⇽--- 把文件内容重定向到宿主机上的本地文件
$ cat ubuntu_os-release
NAME="Ubuntu"
VERSION="16.04.1 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.1 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
$ cat /etc/os-release ⇽--- 为了强调这一点,展示/etc/os- release在宿主机上不存在
cat: /etc/os-release: No such file or directory
读者可能会有疑问,为何这里要用 entrypoint
,而不是简单执行 cat
命令来输出文件。这是因为有些镜像可能已经设置了入口点。如果是这样,docker会把 cat
当作 entrypoint
命令的参数,导致不想要的后果。
方便起见,你可能想把这些命令设置为别名,如代码清单16-5所示。
代码清单16-5 使用别名来从镜像中提取文件
$ alias imagecat='docker run --rm -i -t --entrypoint=cat' ⇽--- 把命令存为别名imagecat,包含清单16.4中的所有内容直到镜像和文件参数
$ imagecat ubuntu /etc/os-release ⇽--- 使用两个参数来调用imagecat(镜像和文件名)
NAME="Ubuntu"
VERSION="16.04.1 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.1 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
讨论
本技巧假设容器中有 cat
命令。如果用技巧58来构建小型容器,可能就不是这种情况了,容器中只有你的二进制文件——没有标准Linux工具。
如果是这种情况,那么可以考虑使用技巧73中的 docker export
命令,但是与其把它们发送到其他机器,不如你只是从其中提取想要的文件。记住,容器不一定要成功启动了你才能把它公开出来——你可以尝试用容器中不存在的命令来运行,然后公开停止运行的容器(或者直接使用 docker create
命令,它会将容器准备好运行但不进行启动)。