获取镜像
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
# 示例
[HesjdeMacBook-Pro]:~ hesj$ docker pull redis
Using default tag: latest
latest: Pulling from library/redis
4d0d76e05f3c: Pull complete
cfbf30a55ec9: Pull complete
82648e31640d: Pull complete
fb7ace35d550: Pull complete
497bf119bebf: Pull complete
89340f6074da: Pull complete
Digest: sha256:4aed8ea5a5fc4cf05c8d5341b4ae4a4f7c0f9301082a74f6f9a5f321140e0cd3
Status: Downloaded newer image for redis:latest
[HesjdeMacBook-Pro]:~ hesj$
上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub 获取镜像。
从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 sha256
的摘要,以确保下载一致性。
列出镜像
[HesjdeMacBook-Pro]:~ hesj$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql latest a8a59477268d 3 weeks ago 445MB
redis latest bfcb1f6df2db 4 weeks ago 107MB
nginx latest ae513a47849c 4 weeks ago 109MB
另外一个需要注意的问题是,docker image ls
列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。
你可以通过以下命令来便捷的查看镜像、容器、数据卷所占用的空间。
[HesjdeMacBook-Pro]:~ hesj$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 3 0 605.2MB 605.2MB (100%)
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0B 0B
中间层镜像
为了加速镜像构建、重复利用资源,Docker 会利用 中间层镜像。所以在使用一段时间后,可能会看到一些依赖的中间层镜像。默认的 docker image ls
列表中只会显示顶层镜像,如果希望显示包括中间层镜像在内的所有镜像的话,需要加 -a
参数。
docker image ls -a
这样会看到很多无标签的镜像,与之前的虚悬镜像不同,这些无标签的镜像很多都是中间层镜像,是其它镜像所依赖的镜像。这些无标签镜像不应该删除,否则会导致上层镜像因为依赖丢失而出错。实际上,这些镜像也没必要删除,相同的层只会存一遍,而这些镜像是别的镜像的依赖,因此并不会因为它们被列出来而多存了一份,无论如何你也会需要它们。只要删除那些依赖它们的镜像后,这些依赖的中间层镜像也会被连带删除。
列出部分镜像
不加任何参数的情况下,docker image ls
会列出所有顶级镜像,但是有时候我们只希望列出部分镜像。docker image ls
有好几个参数可以帮助做到这个事情。
# 根据仓库名列出镜像
docker image ls redis
# 列出特定的某个镜像,也就是说指定仓库名和标签
docker image ls redis:3.0
删除镜像
$ docker image rm [选项] <镜像1> [<镜像2> ...]
其中,<镜像>
可以是 镜像短 ID
、镜像长 ID
、镜像名
或者 镜像摘要
。但是我们通常使用镜像短 ID
、镜像名
、或者结合命令做多个镜像删除
$ docker image rm a8a59477268d
$ docker image rm $(docker image ls -q redis)
利用 commit 理解镜像构成(不推荐)
注意: docker commit
命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现场等。但是,不要使用 docker commit
定制镜像,定制镜像应该使用 Dockerfile
来完成。
以定制一个 Web 服务器为例子,来讲解镜像是如何构建的。
[HesjdeMacBook-Pro]:~ hesj$ docker run --name webserver -d -p 80:80 nginx
fad3caa5cd60dc745cea56ea545b8ffbcad555bec9506b525e6aa8222041d5e0
直接用浏览器访问http://localhost的话,我们会看到默认的 Nginx 欢迎页面。
做一个简单的修改,修改这个欢迎页。
# 交互式终端方式进入 webserver 容器,并执行了 bash 命令,也就是获得一个可操作的 Shell。
[HesjdeMacBook-Pro]:~ hesj$ docker exec -it webserver bash
# 用 <h1>Hello, Docker!</h1> 覆盖了 /usr/share/nginx/html/index.html 的内容。
root@fad3caa5cd60:/# echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
root@fad3caa5cd60:/# exit
exit
现在我们再刷新浏览器的话,会发现内容被改变了。
我们修改了容器的文件,也就是改动了容器的存储层。我们可以通过 docker diff
命令看到具体的改动。
[HesjdeMacBook-Pro]:~ hesj$ docker diff webserver
C /root
A /root/.bash_history
C /run
A /run/nginx.pid
C /usr/share/nginx/html/index.html
C /var/cache/nginx
A /var/cache/nginx/client_temp
A /var/cache/nginx/fastcgi_temp
A /var/cache/nginx/proxy_temp
A /var/cache/nginx/scgi_temp
A /var/cache/nginx/uwsgi_temp
定制好了变化,我们希望能将其保存下来形成镜像。
# docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
[HesjdeMacBook-Pro]:~ hesj$ docker commit --author "西瓜糖 <sssss.gmail.com>" --message "修改默认网页" webserver nginx:v2
sha256:55efe5ea0e8a34ccf204989f44749363a8b78d6d8892ca4f686046faa780917b
# 查看镜像
[HesjdeMacBook-Pro]:~ hesj$ docker image ls nginx
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v2 55efe5ea0e8a 48 seconds ago 109MB
nginx latest ae513a47849c 4 weeks ago 109MB
#查看修改历史
[HesjdeMacBook-Pro]:~ hesj$ docker history nginx:v2
IMAGE CREATED CREATED BY SIZE COMMENT
55efe5ea0e8a About a minute ago nginx -g daemon off; 102B 修改默认网页
ae513a47849c 4 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 4 weeks ago /bin/sh -c #(nop) STOPSIGNAL [SIGTERM] 0B
<missing> 4 weeks ago /bin/sh -c #(nop) EXPOSE 80/tcp 0B
<missing> 4 weeks ago /bin/sh -c ln -sf /dev/stdout /var/log/nginx… 22B
<missing> 4 weeks ago /bin/sh -c set -x && apt-get update && apt… 53.7MB
<missing> 4 weeks ago /bin/sh -c #(nop) ENV NJS_VERSION=1.13.12.0… 0B
<missing> 4 weeks ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.13.12… 0B
<missing> 4 weeks ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
<missing> 4 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 4 weeks ago /bin/sh -c #(nop) ADD file:ec5be7eec56a74975… 55.3MB
新的镜像定制好后,我们可以来运行这个镜像。
docker run --name web2 -d -p 81:80 nginx:v2
这里我们命名为新的服务为 web2
,并且映射到 81
端口。直接访问 http://localhost:81 看到结果,其内容应该和之前修改后的 webserver
一样。
为什么不推荐使用?
仔细观察之前的 docker diff webserver
的结果,你会发现除了真正想要修改的 /usr/share/nginx/html/index.html
文件外,由于命令的执行,还有很多文件被改动或添加了。这还仅仅是最简单的操作,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿。
此外,使用 docker commit
意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体在操作的。