容器镜像在Docker host的存储位置:
最上面的层(layer)为读写层,也就是容器。下面的其他的层都是只读层的镜像层。并且除了最下面的一层外,其他的层都有一个指针指向自己下面的一层镜像(联合文件系统)。
联合文件系统对用户来说只是一个文件系统。但是这些层并不是不能看到的,如果需要查看的话,可以进入运行Docker的机器上进行查看,从这些层中可以看到Docker内部实现的一些细节。
Docker容器镜像和容器的数据都存放在服务器的/var/lib/docker/的这个路径下。不同的linux发行版本存储方式上有差别。CentOS发行版本上的存储方式为Overlay或Overlay2。
docker info
Dislay system-wide information
[root@vm1 ~]# docker info
Client: Docker Engine - Community
Version: 24.0.2
Context: default
Debug Mode: false
Plugins:
buildx: Docker Buildx (Docker Inc.)
Version: v0.10.5
Path: /usr/libexec/docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v2.18.1
Path: /usr/libexec/docker/cli-plugins/docker-compose
Server:
Containers: 15
Running: 2
Paused: 0
Stopped: 13
Images: 6
Server Version: 24.0.2
Storage Driver: overlay2
Backing Filesystem: xfs
Supports d_type: true
Using metacopy: false
Native Overlay Diff: true
userxattr: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 1
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: io.containerd.runc.v2 runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 3dce8eb055cbb6872793272b4f20ed16117344f8
runc version: v1.1.7-0-g860f061
init version: de40ad0
Security Options:
seccomp
Profile: builtin
Kernel Version: 3.10.0-1160.90.1.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 1.777GiB
Name: vm1
ID: a00730c7-0264-4797-b3fc-62b65f0650a8
Docker Root Dir: /var/lib/docker
Debug Mode: false
Experimental: false
Insecure Registries:
192.168.17.9:5000
127.0.0.0/8
Live Restore Enabled: false
WARNING: API is accessible on http://0.0.0.0:2375 without encryption.
Access to the remote API is equivalent to root access on the host. Refer
to the 'Docker daemon attack surface' section in the documentation for
more information: https://docs.docker.com/go/attack-surface/
[root@vm1 ~]#
其中,我们看到有容器的数量信息、镜像的信息、还有Storage Driver为overlay2的相关信息。
cgroup Driver:cgroupfs的信息。Backing Filesystem:xfs:容器本身文件系统的类型。
centos系统docker默认使用的存储驱动是devicemapper,而这种存储驱动有两种模式loop-lvm和direct-lvm,不巧默认又使用了比较低效的loop_lvm。
OverlayFS是一个类似AUFS的现代联合文件系统,更快实现简单。
OverlayFS是内核提供的文件系统,overlay和overlay2是docker的存储驱动。
[root@vm1 ~]# cd /var/lib/docker
[root@vm1 docker]# ll
total 20
drwx--x--x 5 root root 149 Jun 28 21:34 buildkit
drwx--x--- 17 root root 4096 Jun 29 20:42 containers
-rw------- 1 root root 36 Jun 25 20:47 engine-id
drwx------ 3 root root 22 Jun 25 20:47 image
drwxr-x--- 3 root root 19 Jun 25 20:47 network
drwx--x--- 59 root root 8192 Jun 29 20:42 overlay2
drwx------ 4 root root 32 Jun 25 20:47 plugins
drwx------ 2 root root 6 Jun 29 16:45 runtimes
drwx------ 2 root root 6 Jun 25 20:47 swarm
drwx------ 2 root root 6 Jun 29 20:39 tmp
drwx-----x 2 root root 50 Jun 29 16:45 volumes
[root@vm1 docker]#
其中有一个overlay2,里面就是保存的容器和镜像的数据。
docker pull下载一个镜像,然后再检查overlay2,我们可以看到目录中多了一个长数字名称的文件夹名称,里面有diff和l文件夹。
diff文件夹中是一个文件系统。
l文件夹中里面是一个软链接。短名称可以方便挂载。
启动容器后,检查mount |grep overlay2
Overlay及Overlay2的工作原理:
OverlayFS是将单个Linux主机上的两个目录合并成一个目录。这些目录被称为层,统一过程被称为联合挂载。
OverlayFS底层目录称为lowerdir,高层目录称为upperdir,合并统一视图称为merged。
当需要修改一个文件的时候,使用CoW(写时复制)将文件从只读的Lower复制到可写的Upper进行修改,结果也保存在Upper层。在Docker中,底下的只读层就是image,可写层就是Container。
overlay2是overlay的改进版, 只支持4.0以上内核添加了Multiple lower layers in overlayfs的特性,所以overlay2可以直接造成multiple lower layer不用像overlay一样通过硬链接的方式(最大128层)。所以消耗更少的inode。
本质区别是镜像层之间共享数据的方法不同。
overlay共享数据方式是通过硬链接,只挂载一层,其他层通过最高层通过硬链接形式共享(增加了磁盘的inode的负担)。
而overlay2是通过每层的lower文件。
Linux的磁盘满与不满,跟磁盘空间有关,还跟inode号。
[root@vm1 ~]# df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
devtmpfs 230177 382 229795 1% /dev
tmpfs 232852 1 232851 1% /dev/shm
tmpfs 232852 771 232081 1% /run
tmpfs 232852 16 232836 1% /sys/fs/cgroup
/dev/sda5 8130560 88973 8041587 2% /
/dev/sda2 256000 341 255659 1% /boot
overlay 8130560 88973 8041587 2% /var/lib/docker/overlay2/8b35c660b5b6c108f46d4ed0eac2caf5c9e247ad5faf74afb537197914a42daf/merged
tmpfs 232852 1 232851 1% /run/user/0
overlay 8130560 88973 8041587 2% /var/lib/docker/overlay2/2a5b4c6c3c5ac9995f16cc08e64bcb50913a1264130d93fcb0bc7f3f84684c6b/merged
[root@vm1 ~]#
如果upperdir和lowerdir文件名相同,则使用upperdir中的文件。
image layer是只读层。
Container layer是读写层。在容器层进行更改保存。
Container mount是联合挂载,merged(合并)。
/var/lib/docker/overlay2路径下的信息在不同的阶段会有变化,了解这几个阶段中新增的数据以及容器和镜像的存储结构的变化,非常有利于我们对Docker容器以及Docker镜像的理解。
Docker运行前、Docker运行后,下载镜像后,运行容器后四个阶段中Docker存储的变化。
Docker运行前:
没有启动Docker daemon之前不会在/var/lib目录中增加docker目录。
Docker启动后:
就能看到docker目录。其中overlay2目录是空的,里面只有backingFsBlockDev和l两个目录存在。
下载容器镜像以后:
然后我们可以看到有镜像存在,是一串长字符串的目录,里面有diff和link两个文件夹,diff目录中是容器中的文件系统目录结构。
centos镜像只有一层。
l目录中包含了很多软链接,使用的是短名称指向其他层,短名称用于避免mount参数时候达到页面大小的限制。
启动容器后,就看到在overlay2文件夹中多了两个文件夹,其中有个末尾是init文件名的文件夹。
在文件夹中有diff、link、lower、merged、work文件夹。
diff:记录每一层自己内容的数据;
link:记录该层链接目录(实际是l目录下到层的链接),比如在容器中创建目录或在diff新增该目录。
lower:底层镜像:是挂载的那一层底层centos的镜像。
merged:底层镜像和容器层合并在一起。这就是容器当中所使用的目录。创建容器时将lower-id指向的镜像层目录以及upper目录联合挂载到merged目录;
work:用来完成如copy-on-write的操作。
容器为什么不能数据持久化,因为我们会删容器。
容器启动后,可以在docker host中查看mount情况:
[root@vm1 ~]# mount |grep overlay
overlay on /var/lib/docker/overlay2/8b35c660b5b6c108f46d4ed0eac2caf5c9e247ad5faf74afb537197914a42daf/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/M4VPDQ5FHKDLN6OIJ2LWFEH3K2:/var/lib/docker/overlay2/l/4S7OYV5CW5SH6U33B65NVKTNCT:/var/lib/docker/overlay2/l/TXPV5HR7JK3L6TC6XD665LACSQ:/var/lib/docker/overlay2/l/QUKGQQ4UJYER63JNQINCZWYKS6:/var/lib/docker/overlay2/l/OCHI2IOPGUGHPIYETI7BGSVLJM:/var/lib/docker/overlay2/l/S3QUEFLNJWSEC6JAZTFURK3UFB,upperdir=/var/lib/docker/overlay2/8b35c660b5b6c108f46d4ed0eac2caf5c9e247ad5faf74afb537197914a42daf/diff,workdir=/var/lib/docker/overlay2/8b35c660b5b6c108f46d4ed0eac2caf5c9e247ad5faf74afb537197914a42daf/work)
overlay on /var/lib/docker/overlay2/2a5b4c6c3c5ac9995f16cc08e64bcb50913a1264130d93fcb0bc7f3f84684c6b/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/FEC3OVNUCJ7VKX6FHZKUAD6N22:/var/lib/docker/overlay2/l/L4ORWWKZ2CT6WMPJNXU2GRATZR:/var/lib/docker/overlay2/l/34X2UKWUTBBUZNXWDM62JQ54WG:/var/lib/docker/overlay2/l/PWFI7ZJXZQL5PFFKNCTYJKIGZ2:/var/lib/docker/overlay2/l/KDQCSU7LTWHKS5Q4CSZVJ5VZ5A:/var/lib/docker/overlay2/l/W7UBFRAJFAVE2KDIQNLG2LDGBB:/var/lib/docker/overlay2/l/CKDZAUJCARXIALNBKTE34WXAAZ:/var/lib/docker/overlay2/l/LPJXJJYHWNWR2TGU6DI2GALJZB:/var/lib/docker/overlay2/l/FDQX22BNWOZGK64W4RAYHSXNJ5:/var/lib/docker/overlay2/l/75VQYCQU7QSMR4IBFTUFMZCUQL,upperdir=/var/lib/docker/overlay2/2a5b4c6c3c5ac9995f16cc08e64bcb50913a1264130d93fcb0bc7f3f84684c6b/diff,workdir=/var/lib/docker/overlay2/2a5b4c6c3c5ac9995f16cc08e64bcb50913a1264130d93fcb0bc7f3f84684c6b/work)
[root@vm1 ~]#