二、Docker镜像和仓库(上)
发布时间:

二、Docker镜像和仓库(上)

Docker镜像是由文件系统叠加而成。最底层是一个引导文件系统bootfs,但是Docker用户几乎永远不会和这个bootfs有交互,因为容器启动之后,引导文件系统会被卸载。Docker镜像的第二层是root文件系统,位于bootfs之上,rootfs可以是一种或者多种操作

1 什么是Docker镜像

Docker镜像是由文件系统叠加而成。 最底层是一个引导文件系统bootfs,但是Docker用户几乎永远不会和这个bootfs有交互,因为容器启动之后,引导文件系统会被卸载。Docker镜像的第二层是root文件系统,位于bootfs之上,rootfs可以是一种或者多种操作系统(例如Debain, Ubuntu等)。

基本上bootfs和rootfs是所有镜像通用的层,再往上的镜像层就是用户自定义创建的,例如增加apache服务器,nginx等等,形成不同应用镜像

Docker使用联合加载(union mount)技术加载各个文件系统,指的是一次加载多个文件系统(从bootfs到上层的应用文件系统),这样最终的文件系统会包含所有底层文件和目录。而这样的文件系统就是Docker镜像。

Docker启动一个容器的时候,首先会加载镜像,当所有镜像层被加载完成只有,这些镜像层都是只读的状态,Docker会在最上面加一层可读可写层,容器所做的所有操作都只会保留在最上的container层,如果使用commit命令的话则会把容器的修改固化为一个新的镜像层。

既然Docker容器除了最上层其他都是只读状态,那么如果需要修改底层一些文件怎么办呢(例如需要修改rootfs层的一些系统配置)?这个时候就需要提到Docker使用Copy-On-Write模型了,当遇到需要修改底层只读文件的情况,Docker会从对应的只读层复制该文件到最上层的读写层,然后其进行修改。本质上,底层只读层的该文件没有发生任何变动,只是被上层的文件副本所隐藏了。 所以对于一个Docker镜像来说,一旦其被创建,以后镜像内容永远不会发生改变。当创建一个新的容器的时候,Docker会构建一个镜像栈,并在栈顶创建一个读写层,这个读写层加上下面的镜像只读层就构成了一个容器。

2. 列出镜像

可以使用images命令列出当前存在的镜像。

sudo docker images

镜像被下载下来之后存在于宿主机的/var/lib/docker目录下,Docker使用存储驱动,就在对应的存储驱动名的目录下面,例如我的就在下面overlay2里面。

3. 拉取镜像

在使用docker run命令运行容器的时候,如果镜像不在本地,会自动从仓库中拉取镜像。但是也可以先手动的pull镜像,操作命令如下:

sudo docker pull <镜像名>:<tag>

同一个镜像也可以打上不同的tag,tag可以很好的区分不同的镜像。

不指定tag的时候,默认pull lastest版本的镜像

4. 查找镜像

当我们需要查找自己不太熟悉的镜像的时候,可以使用search命令来进行查找,查找的范围主要是Docker Hub上的公共可用镜像。命令如下:

sudo docker search <镜像名>

5. 构建镜像

我们学会了如何拉取其他人或者其他组织的镜像,那么我们如何创建自己的镜像呢? 创建Docker镜像的方式一般有两种:

  • 从运行中的容器commit形成一个镜像
  • 使用Dockerfile文件直接构建镜像

如同之前介绍的,Docker镜像就是一层有一层的只读层堆叠起来的,而容器则是在镜像的基础上增加了一层可读可写的动态层。所以,如果我们将最上层的动态层(container层)给固化,或者叫冻结,使其变成只读的状态,那么就完成了从容器创建镜像的过程。所以在本质上,docker commit命令只会向仓库提交最上层container层的修改,而下层的只读层都是已经存在的,所以commit相当于增量式提交,每次提交的数据量相当小。

但是现在主流构建镜像的方式都是Dockerfile,并不推荐使用commit的方式构建镜像

commit命令如下:

sudo docker commit <容器ID> <用户名>/<镜像名>

容器ID可以使用ps命令查看,而用户名就是登陆DockerHub的用户名,镜像名相当于就是commit之后的新镜像的名称。commit命令也可以使用-m 参数附带提交信息(类似git commit -m参数的用法),以及-a参数指定作者信息

构建镜像的第二种方式:Dockerfile由于比较重要,内容也比较多,所以会单独拆分出一篇文章来仔细介绍。