Docker--Docker镜像原理
docker 是操作系统层的虚拟化,所以 docker 镜像的本质是在模拟操作系统。
联合文件系统(UnionFS)
联合文件系统(UnionFS) 是Docker镜像实现分层存储的核心技术,它通过将多个只读层(Image Layers)和一个可写层(Container Layer)叠加,形成一个虚拟的、统一的文件系统。
核心概念
分层存储:
- Docker镜像由多个只读层组成,每一层对应一个构建步骤(如RUN、COPY指令)。
- 这些层通过UnionFS叠加在一起,形成一个逻辑上的完整文件系统。
只读层与可写层:
- 只读层:镜像的每一层都是只读的,确保了镜像的一致性和不可变性。
- 可写层:当容器启动时,Docker会在镜像的只读层之上添加一个可写的容器层,用于记录容器的文件修改。
共享与复用:
- 多个容器可以共享同一个镜像的只读层,节省存储空间。
- 不同容器之间的可写层相互隔离,互不影响。
工作原理
文件系统的叠加:
- UnionFS将多个只读层和一个可写层按照顺序叠加,形成一个统一的视图。
- 当访问文件时,UnionFS会从顶层开始查找,直到找到目标文件为止。
写时复制(Copy-on-Write, CoW):
-== 如果容器需要修改只读层中的文件,UnionFS会先在可写层中创建一个该文件的副本,然后对副本进行修改==。
- 这种机制避免了直接修改只读层,确保了镜像的不可变性。
透明性:
- 对于用户来说,UnionFS提供的文件系统视图是透明的,用户无需关心文件的实际存储位置。
实现方式
常见实现:
- AUFS:早期Docker使用的UnionFS实现,但已逐渐被其他实现取代。
- OverlayFS:现代Linux内核中广泛使用的UnionFS实现,性能更优。
- Btrfs、ZFS:其他支持UnionFS的文件系统,但使用较少。
OverlayFS的分层结构:
- LowerDir:镜像的只读层,由多个目录组成。
- UpperDir:容器的可写层。
- WorkDir:用于存储OverlayFS的临时数据。
- MergedDir:最终呈现给容器的统一文件系统视图。
在Docker中的应用场景
镜像构建:
- Docker通过UnionFS将多个构建步骤的层叠加,形成一个完整的镜像。
- 每个层都可以被其他镜像共享,减少了存储空间的占用。
容器运行:
- 容器启动时,Docker会在镜像的只读层之上添加一个可写层,用于记录容器的文件修改。
- 这种设计使得容器可以快速启动,并且多个容器可以共享同一个镜像。
镜像分发:
由于镜像的层是共享的,Docker在分发镜像时只需要传输新增的层,大大减少了网络带宽的占用。
UnionFS的优势
存储效率:
- 通过共享层,减少了存储空间的占用。
- 写时复制机制避免了重复的数据复制。
性能优化:
- OverlayFS等现代UnionFS实现具有较高的性能,能够满足容器化应用的需求。
灵活性:
- 支持动态添加和删除层,方便镜像的管理和更新。
Docker分层存储机制
Docker分层存储机制是Docker镜像构建与运行的核心技术,通过将镜像和容器的数据存储在多个独立的层中,实现了高效、灵活的镜像管理和容器运行。
基本概念
镜像层(Image Layer)
- Docker镜像由多个只读层组成,每一层对应一个构建步骤(如RUN、COPY、ADD指令)。
- 这些层通过
联合文件系统(UnionFS)技术叠加,形成一个逻辑上的完整文件系统。 - 每个层只存储与前一层相比的增量变化,避免重复数据存储。
容器层(Container Layer)
- 当容器启动时,Docker会在镜像的只读层之上添加一个可写的容器层。
- 容器运行时的所有修改(如文件创建、修改、删除)都记录在容器层中,不会影响镜像层。
工作原理
镜像构建
- 构建镜像时,每执行一条Dockerfile指令,都会生成一个新的镜像层。
- 例如,执行RUN apt-get update && apt-get install -y nginx会在基础镜像层之上新增一层,记录安装的Nginx软件包。
容器运行
- 容器启动时,Docker会将镜像的只读层与容器层联合挂载,形成一个可读写的文件系统视图。
- 容器层是临时的,当容器停止或删除时,容器层的修改会丢失(除非通过卷或绑定挂载持久化数据)。
写时复制(Copy-on-Write, CoW)
- 当容器需要修改只读层中的文件时,Docker会将该文件复制到容器层,然后在容器层中进行修改。
- 这种机制保证了镜像层的不可变性,同时提高了容器启动速度。
分层存储的优势
镜像复用与共享
- 多个容器可以共享同一个镜像的只读层,减少存储空间占用。
- 例如,多个基于同一基础镜像构建的应用镜像可以共享基础镜像层。
高效构建与部署
- 镜像构建时,Docker会利用缓存机制。如果某一层的内容没有变化,Docker会直接使用缓存的层,而不需要重新构建。
- 镜像的分层存储使得镜像的传输和存储更加高效。
版本控制与回滚
- 每一层的变化都可以被追踪,开发者可以轻松地回滚到之前的版本,或者在不同版本之间切换。
快速启动 - 由于容器启动时只需添加一个轻量级的可写层,而不是重新创建整个文件系统,因此容器启动速度非常快。
实现细节
层标识符(Layer ID)
- 每个镜像层都有一个唯一的标识符,用于在不同的镜像之间共享。
存储驱动(Storage Driver)
- Docker支持多种存储驱动,如AUFS、OverlayFS、Device Mapper等,这些驱动都实现了分层存储的机制。
例如,OverlayFS是现代Linux系统中广泛使用的存储驱动,性能更优。
层的内容
- 每一层包含了文件系统的变化,例如添加、删除或修改的文件。这些变化以增量方式存储,只有发生变化的部分会被存储。
镜像分层存储实战
先拉取镜像
docker pull nginx:1.21.1 通过 docker image history 查看如下
root@VM-8-12-ubuntu:/data/ahri# docker history nginx:1.21.1 IMAGE CREATED CREATED BY SIZE COMMENT 822b7ec2aaf2 3 years ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B <missing>3 years ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B <missing>3 years ago /bin/sh -c #(nop) EXPOSE 80 0B <missing>3 years ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B <missing>3 years ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a… 4.61kB <missing>3 years ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7… 1.04kB <missing>3 years ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0… 1.96kB <missing>3 years ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0… 1.2kB <missing>3 years ago /bin/sh -c set -x && addgroup --system -… 63.9MB <missing>3 years ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~buster 0B <missing>3 years ago /bin/sh -c #(nop) ENV NJS_VERSION=0.6.1 0B <missing>3 years ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.1 0B <missing>3 years ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B <missing>3 years ago /bin/sh -c #(nop) CMD ["bash"] 0B <missing>3 years ago /bin/sh -c #(nop) ADD file:4ff85d9f6aa246746… 69.3MB 可以看到 dockerfile 和做出来的镜像是对应的,而且不是每一层都占用空间的。
我们再通过inspect命令查看该镜像的存储位置。
root@VM-8-12-ubuntu:/data/ahri# docker image inspect nginx:1.21.1[{"GraphDriver":{"Data":{"LowerDir":"/data/var/lib/docker/overlay2/ec2f5f43a9a6f4e7063fb6ef633103b1bca417f34488a4a48736758f9eb6019f/diff:/data/var/lib/docker/overlay2/e368569b4eb5117dabbb84864913883ecf8f50130097619ce128a7bcdf141092/diff:/data/var/lib/docker/overlay2/4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1/diff:/data/var/lib/docker/overlay2/0b85066e21c3238a8d0214b82399b41b6fedfbebe48ba8a88ab45d0947089685/diff:/data/var/lib/docker/overlay2/8bdfae00fada474094f86a6fc004d5a2e53d660509fcb3ec987d204b2df5eb20/diff", "MergedDir":"/data/var/lib/docker/overlay2/b35408cd1036fd7aa5d4fb5220f06fe9a56606fb2ad1316a8beca766c886e692/merged", "UpperDir":"/data/var/lib/docker/overlay2/b35408cd1036fd7aa5d4fb5220f06fe9a56606fb2ad1316a8beca766c886e692/diff", "WorkDir":"/data/var/lib/docker/overlay2/b35408cd1036fd7aa5d4fb5220f06fe9a56606fb2ad1316a8beca766c886e692/work"}, "Name":"overlay2"}, "RootFS":{"Type":"layers", "Layers":["sha256:d000633a56813933cb0ac5ee3246cf7a4c0205db6290018a169d7cb096581046", "sha256:63b5f2c0d071d1ac41fe869b0f2321c3adec53d8d51b4a03017d865c38dd41f8", "sha256:875b5b50454b905c1046c99ab65e403bf27400bf9c96c157332cda2538698dc2", "sha256:ed94af62a494fbea70c27afcedea4c303817196b50e8dd98b5be88cd514aab01", "sha256:8e58314e4a4fbd97b70bed2b4c5f4b2911ff7f3e3ee310be89fab1120768d533", "sha256:d47e4d19ddecb22dc95d641e9c29192a2d13e8506bc60d1c8f6452685ed63634"]}, "Metadata":{"LastTagTime":"0001-01-01T00:00:00Z"}}]可以看到 GraphDriver 也就是我们的存储驱动,是 overlay2 的存储驱动;
nginx 的 overlay2 的四个目录也都显示出来了,我们知道 docker 的默认目录是/var/lib/docker,之所以在/data/var/lib/docker 下面是因为我们规划了磁盘,调整了默认的存储目录。
可以看到 lowerdir,upperdir,都位于/data/var/lib/docker/overlay2 下面,因为我们调整过默认存储位置所以对比默认的/var/lib/docker 多了/data;我们进入到
cd /data/var/lib/docker/overlay 该目录下,查找我们的nginx,看下文件的怎么存储的
root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# tree -P nginx -f |grep "/sbin/nginx" │ │ │ │ └── ./0b85066e21c3238a8d0214b82399b41b6fedfbebe48ba8a88ab45d0947089685/diff/usr/sbin/nginx │ │ │ │ └── ./db3ef3edafc0f2fe808eb68b6f53aec88b9c2e6fd525bb7e7cf9bbfec464992b/diff/usr/sbin/nginx │ │ │ │ └── ./e43ea820aaac10fe0afac4afac18f8fa929ae618868ae467f59f3be0348ce09c/diff/usr/sbin/nginx │ │ │ └── ./eb2gtnp096i7l1cddihp05s0m/diff/usr/local/nginx/sbin/nginx │ │ │ │ └── ./ee39d178f8d876a5f2a725528e6de013ffca56e8214eb8e01287ec62d5b90d04/diff/usr/sbin/nginx │ │ │ └── ./fejtx6ef605fty3ldwkg4m1q6/diff/usr/local/nginx/sbin/nginx │ │ │ └── ./lkq1gz8jjbyoal36hm586np6k/diff/usr/local/nginx/sbin/nginx -P nginx选项表示只显示匹配模式nginx的文件或目录名。
从当前目录开始,递归地列出所有匹配 nginx 的文件或目录(通过tree -P nginx -f)。
然后,通过grep进一步过滤,只显示路径中包含/sbin/nginx的行。
搜索后可以看到我们找到了多个nginx 文件,因为我们本地有多个 nginx 镜像所以搜到了多个 nginx 文件,通过lowerdir 的值我们可以确定有一个是和我们 nginx:1.12.1 的匹配上的
同样的方式我们通过 Dockerfile 发现,nginx 还存储了个 docker-entrypoint.sh,我们搜索这个文件,我们发现这个文件也被放到了 diff 目录下面,和我们的 lowerdir 中
一个 layer 是对应的。
root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# tree -P "docker-entrypoint.sh" -f | grep "docker-entrypoint.sh" │ │ └── ./2b130b497b862c6acff53f31ebdbc5ecf772345c2b9ce4326f620bcef741507d/diff/usr/local/bin/docker-entrypoint.sh │ │ └── ./4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1/diff/docker-entrypoint.sh │ │ └── ./5b1bddba0c572552b60b6b6652bc91a87ac06b9211ba5624dff834375755cf9e/diff/docker-entrypoint.sh │ │ └── ./64bc799bb08a0ad3430763f0e542b335290e94672e745aa94a852e4304ef4e3f/diff/usr/local/bin/docker-entrypoint.sh │ │ └── ./78ba0a0382912a6afddfae1e6284cf44613f06053d091450ae5b81843cf8fd29/diff/usr/local/bin/docker-entrypoint.sh │ │ └── ./895480d1f77b5a0db270020acb3884a3e6c7060e8b0aa549f2cf6010bfb00218/diff/docker-entrypoint.sh │ │ └── ./a0040d7c6e1a802554a81abc3647e12a173e718387b65c8818fff523da1bb543/diff/usr/local/bin/docker-entrypoint.sh │ │ └── ./a015c0762e6d24830c7631bec8e3c7959860090f0ea78b8a1dd2a3b5e2d5fe7f/diff/docker-entrypoint.sh │ │ └── ./abd0373582a3c3c4cd39e9148cfb2d40eb7d61f74fb677a03eadfb9f8e231b41/diff/usr/local/bin/docker-entrypoint.sh │ │ └── ./b8969b128b086fc04ce55d73892dedc65421ff3da8753ceab61fe958220eeb3c/diff/usr/local/bin/docker-entrypoint.sh │ │ └── ./faa473d46c0b34676cec3c287b3ce9350b40657fa5f5df14840e086021fa2b8e/diff/usr/local/bin/docker-entrypoint.sh 接下来我们进入 diff 的上一级目录查看
root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# cd 4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1 root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2/4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1# ls committed difflink lower work 可以看到 link 文件,里面是每一个 diff 目录的短名称,或者说软链接
root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2/4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1# cat ./link J6U3CHDJ5RRYP6ONH2VJLP34P2root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2/4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1# 通过遍历 l 目录我们会发现,整个 docker 的镜像的 diff 目录都被做了对应的软链接,或者说起了个短名称。

每一个 diff 是一个层级的内容,层级的关系是存放到了 lower 文件中,里面存放着父级的层级。
root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# cd 4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1 root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2/4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1# cat lower l/HUIS7U7MEMOI47VSVORK45VAB4:l/BDTHLGWDJQYNWQQP4AH2NAT3BEroot@VM-8-12-ubuntu:/data/var/lib/docker/overlay2/4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1# 最后我们查看下 mergeddir,发现 merged dir 是不存在的,当我们启动为容器的时候才是有有效的。
root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# ll b35408cd1036fd7aa5d4fb5220f06fe9a56606fb2ad1316a8beca766c886e692/merged ls: cannot access 'b35408cd1036fd7aa5d4fb5220f06fe9a56606fb2ad1316a8beca766c886e692/merged': No such file or directory root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# docker run -d --name mylayer nginx:1.21.1 f1a5126c83a4a010a115b3afb340618302e68834ba95b7e6b3afe4c60e3432ea root@VM-8-12-ubuntu:/data/var/lib/docker/overlay2# docker inspect mylayer[{"Id":"f1a5126c83a4a010a115b3afb340618302e68834ba95b7e6b3afe4c60e3432ea", "Created":"2025-04-15T12:33:55.683157888Z", "Path":"/docker-entrypoint.sh", "Args":["nginx", "-g", "daemon off;"], "State":{"Status":"running", "Running": true, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid":3747944, "ExitCode":0, "Error":"", "StartedAt":"2025-04-15T12:33:56.829866983Z", "FinishedAt":"0001-01-01T00:00:00Z"}, "Image":"sha256:822b7ec2aaf2122b8f80f9c7f45ca62ea3379bf33af4e042b67aafbf6eac1941", "ResolvConfPath":"/data/var/lib/docker/containers/f1a5126c83a4a010a115b3afb340618302e68834ba95b7e6b3afe4c60e3432ea/resolv.conf", "HostnamePath":"/data/var/lib/docker/containers/f1a5126c83a4a010a115b3afb340618302e68834ba95b7e6b3afe4c60e3432ea/hostname", "HostsPath":"/data/var/lib/docker/containers/f1a5126c83a4a010a115b3afb340618302e68834ba95b7e6b3afe4c60e3432ea/hosts", "LogPath":"/data/var/lib/docker/containers/f1a5126c83a4a010a115b3afb340618302e68834ba95b7e6b3afe4c60e3432ea/f1a5126c83a4a010a115b3afb340618302e68834ba95b7e6b3afe4c60e3432ea-json.log", "Name":"/mylayer", "RestartCount":0, "Driver":"overlay2", "Platform":"linux", "MountLabel":"", "ProcessLabel":"", "AppArmorProfile":"docker-default", "ExecIDs": null, "HostConfig":{"Binds": null, "ContainerIDFile":"", "LogConfig":{"Type":"json-file", "Config":{}}, "NetworkMode":"bridge", "PortBindings":{}, "RestartPolicy":{"Name":"no", "MaximumRetryCount":0}, "AutoRemove": false, "VolumeDriver":"", "VolumesFrom": null, "ConsoleSize":[22, 134], "CapAdd": null, "CapDrop": null, "CgroupnsMode":"host", "Dns":[], "DnsOptions":[], "DnsSearch":[], "ExtraHosts": null, "GroupAdd": null, "IpcMode":"private", "Cgroup":"", "Links": null, "OomScoreAdj":0, "PidMode":"", "Privileged": false, "PublishAllPorts": false, "ReadonlyRootfs": false, "SecurityOpt": null, "UTSMode":"", "UsernsMode":"", "ShmSize":67108864, "Runtime":"runc", "Isolation":"", "CpuShares":0, "Memory":0, "NanoCpus":0, "CgroupParent":"", "BlkioWeight":0, "BlkioWeightDevice":[], "BlkioDeviceReadBps":[], "BlkioDeviceWriteBps":[], "BlkioDeviceReadIOps":[], "BlkioDeviceWriteIOps":[], "CpuPeriod":0, "CpuQuota":0, "CpuRealtimePeriod":0, "CpuRealtimeRuntime":0, "CpusetCpus":"", "CpusetMems":"", "Devices":[], "DeviceCgroupRules": null, "DeviceRequests": null, "MemoryReservation":0, "MemorySwap":0, "MemorySwappiness": null, "OomKillDisable": false, "PidsLimit": null, "Ulimits":[], "CpuCount":0, "CpuPercent":0, "IOMaximumIOps":0, "IOMaximumBandwidth":0, "MaskedPaths":["/proc/asound", "/proc/acpi", "/proc/kcore", "/proc/keys", "/proc/latency_stats", "/proc/timer_list", "/proc/timer_stats", "/proc/sched_debug", "/proc/scsi", "/sys/firmware", "/sys/devices/virtual/powercap"], "ReadonlyPaths":["/proc/bus", "/proc/fs", "/proc/irq", "/proc/sys", "/proc/sysrq-trigger"]}, "GraphDriver":{"Data":{"LowerDir":"/data/var/lib/docker/overlay2/3dfbd9c0692bc89a1d337ab0638741bd740ffa9b3aadf73f67d159eeb3fec6ae-init/diff:/data/var/lib/docker/overlay2/b35408cd1036fd7aa5d4fb5220f06fe9a56606fb2ad1316a8beca766c886e692/diff:/data/var/lib/docker/overlay2/ec2f5f43a9a6f4e7063fb6ef633103b1bca417f34488a4a48736758f9eb6019f/diff:/data/var/lib/docker/overlay2/e368569b4eb5117dabbb84864913883ecf8f50130097619ce128a7bcdf141092/diff:/data/var/lib/docker/overlay2/4d5612682090b9800340353f0550207d69d15ecf7135353cf09a8c8db252afb1/diff:/data/var/lib/docker/overlay2/0b85066e21c3238a8d0214b82399b41b6fedfbebe48ba8a88ab45d0947089685/diff:/data/var/lib/docker/overlay2/8bdfae00fada474094f86a6fc004d5a2e53d660509fcb3ec987d204b2df5eb20/diff", "MergedDir":"/data/var/lib/docker/overlay2/3dfbd9c0692bc89a1d337ab0638741bd740ffa9b3aadf73f67d159eeb3fec6ae/merged", "UpperDir":"/data/var/lib/docker/overlay2/3dfbd9c0692bc89a1d337ab0638741bd740ffa9b3aadf73f67d159eeb3fec6ae/diff", "WorkDir":"/data/var/lib/docker/overlay2/3dfbd9c0692bc89a1d337ab0638741bd740ffa9b3aadf73f67d159eeb3fec6ae/work"}, "Name":"overlay2"}, "Mounts":[], "Config":{"Hostname":"f1a5126c83a4", "Domainname":"", "User":"", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts":{"80/tcp":{}}, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "NGINX_VERSION=1.21.1", "NJS_VERSION=0.6.1", "PKG_RELEASE=1~buster"], "Cmd":["nginx", "-g", "daemon off;"], "Image":"nginx:1.21.1", "Volumes": null, "WorkingDir":"", "Entrypoint":["/docker-entrypoint.sh"], "OnBuild": null, "Labels":{"maintainer":"NGINX Docker Maintainers <[email protected]>"}, "StopSignal":"SIGQUIT"}, "NetworkSettings":{"Bridge":"", "SandboxID":"f2228ea45a64eb766b13fa2e23f85d61598da6ea4bd986e06f10717f69f19a08", "SandboxKey":"/var/run/docker/netns/f2228ea45a64", "Ports":{"80/tcp": null }, "HairpinMode": false, "LinkLocalIPv6Address":"", "LinkLocalIPv6PrefixLen":0, "SecondaryIPAddresses": null, "SecondaryIPv6Addresses": null, "EndpointID":"09f0d767349c97d93bcf7c4b4eeecea2df7f68e92de6f92f6e3580618667b82a", "Gateway":"172.17.0.1", "GlobalIPv6Address":"", "GlobalIPv6PrefixLen":0, "IPAddress":"172.17.0.2", "IPPrefixLen":16, "IPv6Gateway":"", "MacAddress":"02:42:ac:11:00:02", "Networks":{"bridge":{"IPAMConfig": null, "Links": null, "Aliases": null, "MacAddress":"02:42:ac:11:00:02", "DriverOpts": null, "NetworkID":"4fa1564bc0380eab5968a99151790435aff1c91960ca086c48a9ad2a267382f3", "EndpointID":"09f0d767349c97d93bcf7c4b4eeecea2df7f68e92de6f92f6e3580618667b82a", "Gateway":"172.17.0.1", "IPAddress":"172.17.0.2", "IPPrefixLen":16, "IPv6Gateway":"", "GlobalIPv6Address":"", "GlobalIPv6PrefixLen":0, "DNSNames": null }}}}]我们通过镜像实际存储位置可以看到镜像在存储的时候,通过分层来实现,并通过link 和 lower 完成层与层之间链接关系配置,diff 存放了我们的内容,并且没有什么加密。
overlay 文件系统工作实战
我们首先创建一个目录用来挂载我们的文件系统
mkdir -p /data/myworkdir/fs 创建文件系统的工作目录
root@VM-8-12-ubuntu:/data# cd /data/myworkdir/fs root@VM-8-12-ubuntu:/data/myworkdir/fs# ls root@VM-8-12-ubuntu:/data/myworkdir/fs# mkdir upper lower merged work准备一些文件
root@VM-8-12-ubuntu:/data/myworkdir/fs# echo "in lower" > lower/in_lower.txt root@VM-8-12-ubuntu:/data/myworkdir/fs# echo "in upper" > upper/in_upper.txt root@VM-8-12-ubuntu:/data/myworkdir/fs# echo "In both. from lower" > lower/in_both.txt root@VM-8-12-ubuntu:/data/myworkdir/fs# echo "In both. from upper" > upper/in_both.txt挂载 overlay 目录
mount -t overlay overlay -o lowerdir=./lower,upperdir=./upper,workdir=./work ./merged 通过 df -h 可以看到我们完成了挂载
root@VM-8-12-ubuntu:/data/myworkdir/fs# df -h Filesystem Size Used Avail Use% Mounted on udev 937M 0 937M 0% /dev tmpfs 198M 836K 197M 1% /run /dev/vda2 50G 29G 19G 61% / tmpfs 986M 24K 986M 1% /dev/shm tmpfs 5.0M 05.0M 0% /run/lock tmpfs 986M 0 986M 0% /sys/fs/cgroup tmpfs 198M 0 198M 0% /run/user/1000 overlay 50G 29G 19G 61% /data/var/lib/docker/overlay2/3dfbd9c0692bc89a1d337ab0638741bd740ffa9b3aadf73f67d159eeb3fec6ae/merged overlay 50G 29G 19G 61% /data/myworkdir/fs/merged 此时看下目录结构,然后发现 merged 目录自动生成了 3 个文件,可以看到both,lower,upper 都在。merged 目录其实就是用户看到的目录,用户的实际文件操作在这里进行。
root@VM-8-12-ubuntu:/data/myworkdir/fs# tree -a. ├── lower │ ├── in_both.txt │ └── in_lower.txt ├── merged │ ├── in_both.txt │ ├── in_lower.txt │ └── in_upper.txt ├── upper │ ├── in_both.txt │ └── in_upper.txt └── work └── work merged 目录下编辑一下in_lower.txt,upper 目录下就会马上出现一个 in_lower.txt,而且内容就是编辑后的。而 lower 目录下的 in_lower.txt 内容不变
root@VM-8-12-ubuntu:/data/myworkdir/fs/merged# vi in_lower.txt root@VM-8-12-ubuntu:/data/myworkdir/fs/merged# cat in_lower.txtin lower! after edit! root@VM-8-12-ubuntu:/data/myworkdir/fs/merged# cd .. root@VM-8-12-ubuntu:/data/myworkdir/fs# tree -a. ├── lower │ ├── in_both.txt │ └── in_lower.txt ├── merged │ ├── in_both.txt │ ├── in_lower.txt │ └── in_upper.txt ├── upper │ ├── in_both.txt │ ├── in_lower.txt │ └── in_upper.txt └── work └── work 5 directories, 8 files root@VM-8-12-ubuntu:/data/myworkdir/fs# cat upper/in_lower.txtin lower! after edit!如果我们删除 in_lower.txt,lower 目录里的"in_lower.txt"文件不会有变化,只是在upper/ 目录中增加了一个特殊文件来告诉OverlayFS,"in_lower.txt'这个文件不能出现在 merged/ 里了,类似 AuFS 的 whiteout
root@VM-8-12-ubuntu:/data/myworkdir/fs# rm -f merged/in_lower.txt root@VM-8-12-ubuntu:/data/myworkdir/fs# tree -a. ├── lower │ ├── in_both.txt │ └── in_lower.txt ├── merged │ ├── in_both.txt │ └── in_upper.txt ├── upper │ ├── in_both.txt │ ├── in_lower.txt │ └── in_upper.txt └── work └── work 5 directories, 7 files root@VM-8-12-ubuntu:/data/myworkdir/fs# ll upper/ total 16 drwxr-xr-x 2 root root 4096 Apr 1520:49 ./ drwxr-xr-x 6 root root 4096 Apr 1520:39 ../ -rw-r--r-- 1 root root 20 Apr 1520:40 in_both.txt c--------- 1 root root 0, 0 Apr 1520:49 in_lower.txt -rw-r--r-- 1 root root 9 Apr 1520:39 in_upper.txt 注意到 upper 下 in_lower.txt 的文件类型没有 ,而是 c 不是-或者 d
可以看到这种文件系统对于底层来说不影响,共享比较容易,但是如果编辑,删除频繁的话,性能还是比较差的。要不停的拷贝或者标记。