目录
commit 命令
# 将容器打包成镜像的命令,:TAG可有可无
docker commit -m="commit信息" -a="作者名" 容器ID 你的镜像名:TAG
创建一个容器
# 以Mariadb为例,我们启动一个mariadb镜像,然后进入这个镜像做一些修改
docker run -it mariadb bash
上面的命令是创建一个 mariadb
镜像的容器并进入这个容器,我们在要在这个容器里新建一个文件夹,然后把我们修改过的这个容器打包成一个新的镜像
新建一个test文件夹
现在我们已经准备好了要打包的容器啦
打包镜像
因为需要容器ID,我们先查看一下容器ID
# 如果你的容器正在运行中,用这个命令
docker ps
# 如果你的容器当前没有运行,那就用这个命令来查看容器信息
docker ps -a
第一行就是我们要打包的容器,执行 commit
命令:
docker commit -m="test commit" -a="kirizi" 30f034aea26a my_image01:1.0
执行成功返回了镜像ID,再用 docker images
命令看看我们的所有镜像:
可以看到第一行就是我们刚刚创建的my_image01:1.0
,并且注意看最后一列 SIZE
列,我们的my_image01
和mariadb
的镜像大小几乎是一样的。
# 查看镜像信息
docker inspect 镜像ID
我们使用docker inspect
来看看我们的镜像和原始镜像的区别在哪儿
这是我们的镜像的信息,一些值得注意的信息
# 这个image id是我们用来创建这个容器的mariadb的镜像id,Volumes是
"Image": "a748acbaccae",
# docker的文件分层机制,可以看到我们的镜像文件分了9层
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:6515074984c6f8bb1b8a9962c8fb5f310fc85e70b04c88442a3939c026dbfad3",
"sha256:ef35264eddcdd186fd3a3a994135ee9848f945d75c2ed81fb3db2adf6c3d2fa7",
"sha256:daec9799caa31439c45c0eda941688a825b2c16abc63cace00d6f6d15c540ce3",
"sha256:fd44d9fa9cd41d4a154323aa232b22e650b45ad92085a249f9febcd97b2af209",
"sha256:e77dd0f2e017c73e064e20c166f30e2dd11ed9534a773c848c9bb73a7d6c4004",
"sha256:cdbb3a99fe5526710d8c51ae42e6eb1a106fa53fb4494c3a9ea7326a88460002",
"sha256:9a3285a0ae185ce0cc550989d474f5ec59f687fb5f9f3af48dd9fecc942c0900",
"sha256:9d646cb6b224d9bf1400b776ea4c2684371815902a48abe77e6fdd5058040fbe",
"sha256:6e28cb318b18cec6896c847a8291f1dc0c8653ee2560a6ee19ff3ad1995737ff"
]
},
再看看原始的 mariadb
image的信息
其他地方都跟my_image01
差不多,就不贴上来了,主要看看这个 RootFS
字段,可以发现我们自己的镜像有9层,而这个只有8层,并且这8层和my_image01
的前8层是一样的,这就是 docker
的镜像分层存储机制,也就是联合文件系统。
联合文件系统
联合文件系统(union file system),直接用百度去搜这个关键词搜出来的都是个 docker 相关的东西,但其实联合文件系统不是 docker 创造出来的,它是一种 linux 文件系统。
联合文件系统工作在其他文件系统的上一层,它聚合了多个文件系统里的文件到同一个根目录下,比起文件系统,它更像是一个挂载机制。
在上图我们可以看到由 XFS
、ext3
这两个文件系统控制的不同的路径都被挂载到了 /mnt
目录下,比较流行的联合文件系统有 UnionFS
, AUFS
, OverlayFS
等,docker
默认使用的文件系统是overlay2
,可以使用docker info
命令查看当前的文件系统:
以 OverlayFS
为例,我们做几个实验看看联合文件系统是怎么工作的:
联合文件系统实践
前置准备
# 我这里是起一个ubuntu docker容器来进行这个实验,大家如果觉得麻烦也可以直接用自己的宿主机进行
# 用docker进行实验的话要记得--privileged=True加这个命令,不然mount命令会因为没有权限而执行失败
docker run --name ubuntu01 -it --privileged=True ubuntu:18.04 bash
# 创建一个文件夹用来存放我们实验用的各种文件
mkdir test_unionfs
cd test_unionfs
# 创建几个基础文件夹
mkdir layer1 layer2 layer3 union_layer
# 创建虚拟磁盘分区
dd if=/dev/zero of=fs1 bs=1024 count=1024
dd if=/dev/zero of=fs2 bs=1024 count=1024
dd if=/dev/zero of=fs3 bs=1024 count=1024
# 创建几个不同的文件系统
mkfs -t ext2 fs1
mkfs -t ext3 fs2
mkfs -t ext4 fs3
# 挂载分区到基础文件夹
mount fs1 layer1
mount fs2 layer2
# 创建几个文件
touch layer1/file_of_layer1 layer1/file
echo "file from layer1" > layer1/file
touch layer2/file_of_layer2 layer2/file
echo "file from layer2" > layer2/file
# 取消挂载
umount layer1
umount layer2
不使用联合文件系统的挂载
# 挂载fs1到union_layer
mount fs1 union_layer
ls union_layer
cat union_layer/file
# 挂载fs2到union_layer
mount fs2 union_layer
ls union_layer
cat union_layer/file
可以看到当我们把 fs2
挂载到 union_layer
的时候原本挂载的 fs1
里的东西就看不见了,只能看见新挂载的fs2
里的文件(fs1
还是挂载在union_layer
上的,因为我们没有取消挂载,可以使用df
命令查看当前所有挂载的目录,可以看到union_layer
是挂载着两个文件系统的),也就是一个挂载点不能同时挂载多个卷,但是联合文件系统却可以做到这一点
使用联合文件系统进行挂载
# 在实验一里我们挂载了文件系统到 union_layer,先取消挂载,因为我们挂载了两个文件系统,所以要执行两次
umount union_layer
umount union_layer
# 挂载文件系统 -o ro 表示使用只读模式,也就是我们无法修改该文件系统中的原始文件,layer1、layer2 模拟docker image 里的的只读层
mount -o ro fs1 layer1
mount -o ro fs2 layer2
# layer3模拟 docker 容器的最顶层,也就是可读写的容器层,所以它需要读写权限
mount fs3 layer3
# 创建工作目录
mkdir layer3/upper layer3/workdir
# 使用 overlay 作为联合文件系统
mount -t overlay -o \
lowerdir=layer1:layer2,\
upperdir=layer3/upper,\
workdir=layer3/workdir \
none union_layer
ls union_layer
现在 union_layer
里可以同时看到 layer1
和layer2
里的文件了,如果我们在union_layer
里修改file file_of_layer1 file_of_layer2
这些文件会怎么样呢,看看实验三:
写时复制机制
# 在layer3是无法看见layer1和layer2里的文件的,这里的layer3就是模拟的我们docker里的container layer
cat layer3/file
>"No such file or directory"
# 看看layer1里的file文件里有啥
cat layer1/file
"file from layer1"
# 看看layer2里的file文件里有啥
cat layer2/file
>"file from layer2"
cat union_layer/file
>"file from layer1"
# 当前layer3/upper里是没有东西的,如果我们在挂载了layer1和layer2的union_layer层里进行了文件的修改
# 那么修改之后的文件会存储在layer3/upper,这也就是linux里的写时复制(cow)技术
cat layer3/upper/file
> No such file or directory
echo "file from union_layer" > union_layer/file
# 可以看到现在layer3/upper/file里已经有了我们刚刚写到union_layer/file里的内容了
cat layer3/upper/file
> "file from union_layer"
# image 层 layer1 里的内容不变
cat layer1/file
> "file from layer1"
# 移除挂载层里的文件
rm union_layer/file
# image层里的文件不会受到影响,它是只读的
ls layer1
> file file_of_layer1 lost+found
ll layer3/upper/file
> c--------- 1 root root 0, 0 Jan 7 15:17 layer3/upper/file
再回到 docker
中,我们来看看在 docker
里是到底怎么通过联合文件系统实现镜像分层的:
结合上面的实验再看这张图是不是就很清晰啦,我们实验中的 layer1
、layer2
模拟的是 docker 里的 image layer
,layer3
模拟的是container layer
,挂载的那个 union_layer
模拟的是container mount
。
container mount
层让我们可以同时看到container layer
和 image layer
里的文件内容,而我们的修改都会存储在container layer
,不会影响到image layer
。
image layer
是可以复用的只读层,就像我们文章一开始所打包的那个镜像,我们使用一个镜像创建了容器,并且在这个容器里新建了一个文件,把我们新建了文件的这个容器打包成新的镜像,这个新的镜像只比原来的镜像多了一层,这一层就是图上的container layer
。