Docker 支持通过扩展现有镜像创建新的镜像。DockerHub 中 99% 的镜像都是通过在base镜像的基础上安装和配置需要的软件所构建出来的。
镜像分层最大的一个好处就是共享资源。
多个镜像均以同一个 base 镜像为基础构建而来,而Docker Host 只需在磁盘上保存一份 base 镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了,且镜像的每一层都可以被共享。
如果多个容器共享一份基础镜像,当某个容器修改了基础镜像的内容,比如 /etc 下的文件,这时其他容器的 /etc 是不会被修改的,修改只会被限制在单个容器内,这就是容器 Copy-on-Write 特性。
当容器启动时,一个新的可写层被加载到镜像的顶部,这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
所有对容器的改动,无论添加、删除还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。
镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件,比如/a
,上层的/a
会覆盖下层的/a
,也就是说用户只能访问到上层中的文件/a
。在容器层中,用户看到的是一个叠加之后的文件系统。
只有当需要修改时才复制一份数据,这种特性被称作 Copy-on-Write。可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改。
这样就解释了我们前面提出的问题:容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。
格式
Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
参数
Options:
-a 作者(例如,“along along@along.com”)
-c 修改Dockerfile指令应用于创建的镜像
-m 提交消息(可以理解是注释)
-p 在提交期间暂停容器(默认为true)
[root@johnnyg03 ~]# docker run -it --name b1 busybox:latest /bin/sh
/ # mkdir /data/html -p
/ # echo "hello world" > /data/html/index.html
/ # cat /data/html/index.html
hello world
[root@johnnyg03 ~]# docker commit -a "johnnyg johnnyg@test.com" -p b1
sha256:0b187f35d8cbe31aa845c2247dd25ee968b45cc20e76a54390904d45e5884a41
[root@johnnyg03 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 0b187f35d8cb 4 seconds ago 1.24MB
[root@johnnyg03 ~]# docker tag 0b187f35d8cb johnnyg/busybox:latest
[root@johnnyg03 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
johnnyg/busybox v1 0b187f35d8cb 58 seconds ago 1.24MB
[root@johnnyg03 ~]# docker run -it --name b2 johnnyg/busybox:v1 /bin/sh
/ # ls
bin data dev etc home proc root sys tmp usr var
/ # cat /data/html/index.html
hello world
[root@johnnyg03 ~]# docker commit -a "johnnyg " -c 'CMD ["/bin/httpd","-f","-h","/data/html"]' -p b1 johnnyg/busybox:v2
[root@johnnyg03 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
johnnyg/busybox v2 6601a1526ad0 6 seconds ago 1.24MB
[root@johnnyg03 ~]# docker run -d --name b3 johnnyg/busybox:v2
a0e5fc970f34cbe2097a0a57b90f34ce7616285310cf05c48ff61332ac0bf6ec
[root@johnnyg03 ~]# docker inspect b3 | grep IPA
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.4",
"IPAMConfig": null,
"IPAddress": "172.17.0.4",
[root@johnnyg03 ~]# curl 172.17.0.4
hello world
格式
[root@johnnyg03 ~]# docker export --help
Usage: docker export [OPTIONS] CONTAINER
参数
Options:
-o 输出(可以用>取代)后面跟路径文件名
演示
[root@johnnyg03 ~]# docker export b1 > docker_image_b1.tar
or
[root@johnnyg03 ~]# docker export b1 -o docker_image_b1.tar
[root@johnnyg03 ~]# ll docker_image_b1.tar
-rw-r--r-- 1 root root 1467392 Aug 13 17:23 docker_image_b1.tar
格式
[root@johnnyg03 ~]# docker import --help
Usage: docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
参数
Options:
-c 应用docker 指令创建镜像;
-m 提交时的说明文字(注释)
演示
[root@johnnyg03 ~]# docker import docker_image_b1.tar johnnyg/busybox:v3
[root@johnnyg03 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
johnnyg/busybox v3 c74a65ab0d71 6 seconds ago 1.24MB
[root@johnnyg03 ~]# docker run -it --name b4 johnnyg/busybox:v3 /bin/sh
/ # ls
bin data dev etc home proc root sys tmp usr var
/ # cat /data/html/index.html
hello world
格式
[root@johnnyg03 ~]# docker save --help
Usage: docker save [OPTIONS] IMAGE [IMAGE...]
参数
Options:
-o 输出(可以用>取代)后面跟路径文件名
[root@johnnyg03 ~]# docker save johnnyg/busybox > docker_image_b1_2.tar
or
[root@johnnyg03 ~]# docker save johnnyg/busybox -o docker_image_b1_2.tar
[root@johnnyg03 ~]# ll docker_image_b1_2.tar
-rw-r--r-- 1 root root 2956288 Aug 13 18:11 docker_image_b1_2.tar
【注:如果需要load(加载),操作是一样的】
[root@johnnyg03 ~]# docker save busybox:latest nginx:latest > docker_images_busybox_nginx.tar
[1]+ Done docker save busybox:latest nginx:latest > docker_images_busybox
[root@johnnyg03 ~]# ll docker_images_busybox_nginx.tar
-rw-r--r-- 1 root root 147370496 Aug 13 19:11 docker_images_busybox_nginx.tar
格式
[root@johnnyg03 ~]# docker load --help
Usage: docker load [OPTIONS]
参数
Options:
-i 指定导入的文件,代替 STDIN(标准输入“<”)
-q 精简输出信息
演示
[root@johnnyg03 ~]# docker load < docker_image_b1_2.tar
or
[root@johnnyg03 ~]# docker load -i docker_image_b1_2.tar
特别注意:两种方法不可混用。
如果使用 import 导入 save 产生的文件,虽然导入不提示错误,但是启动容器时会提示失败,会出现类似"docker: Error response from daemon: Container command not found or does not exist"的错误。
export 导出的镜像文件体积小于 save 保存的镜像
docker import 可以为镜像指定新名称
docker load 不能对载入的镜像重命名
docker export 不支持
docker save 支持
export 导出(import 导入)是根据容器拿到的镜像,再导入时会丢失镜像所有的历史记录和元数据信息(即仅保存容器当时的快照状态),所以无法进行回滚操作。
save 保存(load 加载)的镜像,没有丢失镜像的历史,可以回滚到之前的层(layer)。
docker export 的应用场景:主要用来制作基础镜像,比如我们从一个 ubuntu 镜像启动一个容器,然后安装一些软件和进行一些设置后,使用 docker export 保存为一个基础镜像。然后,把这个镜像分发给其他人使用,比如作为基础的开发环境。
docker save 的应用场景:如果我们的应用是使用 docker-compose.yml 编排的多个镜像组合,但我们要部署的客户服务器并不能连外网。这时就可以使用 docker save 将用到的镜像打个包,然后拷贝到客户服务器上使用 docker load 载入。