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 下面是因為我們規劃了磁盤,調整了默認的存儲目錄。

可以看到 lowerdirupperdir,都位于/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  diff  link  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 <docker-maint@nginx.com>"},"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     0  5.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.txtupper 目錄下就會馬上出現一個 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.txt
in 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└── work5 directories, 8 files
root@VM-8-12-ubuntu:/data/myworkdir/fs# cat upper/in_lower.txt
in lower! after edit!

如果我們刪除 in_lower.txtlower 目錄里的"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└── work5 directories, 7 files
root@VM-8-12-ubuntu:/data/myworkdir/fs# ll upper/
total 16
drwxr-xr-x 2 root root 4096 Apr 15 20:49 ./
drwxr-xr-x 6 root root 4096 Apr 15 20:39 ../
-rw-r--r-- 1 root root   20 Apr 15 20:40 in_both.txt
c--------- 1 root root 0, 0 Apr 15 20:49 in_lower.txt
-rw-r--r-- 1 root root    9 Apr 15 20:39 in_upper.txt

注意到 upper 下 in_lower.txt 的文件類型沒有 ,而是 c 不是-或者 d

可以看到這種文件系統對于底層來說不影響,共享比較容易,但是如果編輯,刪除頻繁的話,性能還是比較差的。要不停的拷貝或者標記

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/76298.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76298.shtml
英文地址,請注明出處:http://en.pswp.cn/web/76298.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

雙層Key緩存

雙層 Key 緩存是一種針對 緩存擊穿 和 雪崩問題 的優化方案&#xff0c;其核心思想是通過 主備雙緩存 的機制&#xff0c;確保在熱點數據過期時仍能提供可用服務&#xff0c;同時降低對數據庫的瞬時壓力。以下是其核心原理、實現細節及適用場景的深度解析&#xff1a; 一、核心…

力扣每日打卡 2176. 統計數組中相等且可以被整除的數對(簡單)

力扣 2176. 統計數組中相等且可以被整除的數對 簡單 前言一、題目內容二、解題方法1. 暴力解法2.官方題解官方也是暴力解法 前言 這是刷算法題的第十三天&#xff0c;用到的語言是JS 題目&#xff1a;力扣 2176. 統計數組中相等且可以被整除的數對(簡單) 一、題目內容 給你一…

云服務器和物理服務器

服務器&#xff0c;作為互聯網世界中數據存儲與處理的關鍵樞紐&#xff0c;其重要性不言而喻。在眾多服務器類型中&#xff0c;云服務器和物理服務器占據了主導地位&#xff0c;它們各自有著獨特的特點和應用場景。咱們就來深入探討一下這兩者的區別。

Kubernetes Pod 調度策略:從基礎到進階

文章目錄 環境Kubernetes 部署Kubernetes Pod 調度策略Kubernetes Pod 調度策略對照表調度流程經歷階段案例展示生成yaml文件默認調度節點選擇器為節點添加標簽編寫 Deployment 配置文件應用資源并查看調度結果 Node Affinity&#xff08;節點親和性&#xff09;為節點添加標簽…

SQLite、MySQL、SQL Server、Oracle 和 PostgreSQL 五種數據庫的區別

以下是 SQLite、MySQL、SQL Server、Oracle 和 PostgreSQL 五種主流關系型數據庫管理系統(RDBMS)的區別,從多個維度進行對比: 1. 架構與部署 SQLite(Structured Query Language Lite?): 嵌入式數據庫,無服務器架構。數據庫存儲在一個單一的磁盤文件中。部署簡單,適合輕量…

電路安全智控系統與主機安全防護系統主要功能是什么

電路安全智控系統被稱為電路安全用電控制系統。電路安全智控系統具備一系列強大且實用的功能。電路安全智控系統能夠對總電壓、總電流、總功率、總電能&#xff0c;以及各分路的電壓、電流、功率、電能和功率因素等進行全方位的監控。在大型工廠的電力分配中&#xff0c;通過對…

使用Lean 4和C#進行數學定理證明與邏輯推理

步驟1&#xff1a;安裝與配置環境 安裝Lean 4 訪問Lean官網或GitHub倉庫&#xff0c;按照指南安裝Lean 4及配套工具鏈&#xff08;如VS Code擴展&#xff09;。 設置C#開發環境 安裝.NET SDK及IDE&#xff08;如Visual Studio或Rider&#xff09;&#xff0c;確保C#開發環境正…

八股文---MySQl(3)

目錄 12.事務的特性是什么&#xff1f;可以詳細說一下嗎&#xff1f; 回答 13并發事務帶來哪些問題&#xff1f;怎么解決這些問題呢&#xff1f;MySQL的默認隔離級別是&#xff1f; 臟讀&#xff1a;一個事務讀到另外一個事務還沒有提交的數據。 不可重復讀&#xff1a;一個…

實驗五 內存管理實驗

實驗五 內存管理實驗 一、實驗目的 1、了解操作系統動態分區存儲管理過程和方法。 2、掌握動態分區存儲管理的主要數據結構--空閑表區。 3、加深理解動態分區存儲管理中內存的分配和回收。 4、掌握空閑區表中空閑區3種不同放置策略的基本思想和實現過程。 5、通過模擬程…

【MySQL】MySQL表的增刪改查(CRUD) —— 上篇

目錄 MySQL表的增刪改查&#xff08;CRUD&#xff09; 1. 新增&#xff08;Create&#xff09;/插入數據 1.1 單行數據 全列插入 insert into 表名 values(值, 值......); 1.2 單行數據 指定列插入 1.3 多行數據 指定列插入 1.4 關于時間日期&#xff08;datetime&am…

【MATLAB代碼例程】AOA與TOA結合的高精度平面地位,適用于四個基站的情況,附完整的代碼

本代碼實現了一種基于到達角(AOA) 和到達時間(TOA) 的混合定位算法,適用于二維平面內移動或靜止目標的定位。通過4個基站的協同測量,結合最小二乘法和幾何解算,能夠有效估計目標位置,并支持噪聲模擬、誤差分析和可視化輸出。適用于室內定位、無人機導航、工業監測等場景…

ModbusTCP 轉 Profinet 主站網關

一、 功能概述 1.1 設備簡介 本產品是 ModbusTCP 和 Profinet(M) 網關&#xff08;以下簡稱網關&#xff09;&#xff0c;使用數據映射 方式工作。 本產品在 ModbusTCP 側作為 ModbusTCP 從站&#xff0c;接 PLC 、上位機、 wincc 屏 等&#xff1b;在 Profin…

《AI大模型應知應會100篇》第25篇:Few-shot與Zero-shot使用方法對比

第25篇&#xff1a;Few-shot與Zero-shot使用方法對比 摘要 在大語言模型的應用中&#xff0c;**Few-shot&#xff08;少樣本&#xff09;和Zero-shot&#xff08;零樣本&#xff09;**是兩種核心的提示策略。它們各自適用于不同的場景&#xff0c;能夠幫助用戶在不進行額外訓練…

深入理解C++中string的深淺拷貝

目錄 一、引言 二、淺拷貝與深拷貝的基本概念 2.1 淺拷貝 2.2 深拷貝 在C++ 中, string 類的深淺拷貝有著重要的區別。 淺拷貝 深拷貝 string 類中的其他構造函數及操作 resize 構造 = 構造(賦值構造) + 構造(拼接構造) cin 和 cin.get 的區別 三、C++中string類的…

在Qt中驗證LDAP賬戶(Windows平臺)

一、前言 原本以為在Qt&#xff08;Windows平臺&#xff09;中驗證 LDAP 賬戶很簡單&#xff1a;集成Open LDAP的開發庫即可。結果臨了才發現&#xff0c;Open LDAP壓根兒不支持Windows平臺。沿著重用的原則&#xff0c;考慮遷移Open LDAP的源代碼&#xff0c;卻發現工作量不小…

《軟件設計師》復習筆記(11.4)——處理流程設計、系統設計、人機界面設計

目錄 一、業務流程建模 二、流程設計工具 三、業務流程重組&#xff08;BPR&#xff09; 四、業務流程管理&#xff08;BPM&#xff09; 真題示例&#xff1a; 五、系統設計 1. 主要目的 2. 設計方法 3. 主要內容 4. 設計原則 真題示例&#xff1a; 六、人機界面設…

UniRig ,清華聯合 VAST 開源的通用自動骨骼綁定框架

UniRig是清華大學計算機系與VAST聯合開發的前沿自動骨骼綁定框架&#xff0c;專為處理復雜且多樣化的3D模型而設計。基于強大的自回歸模型和骨骼點交叉注意力機制&#xff0c;UniRig能夠生成高質量的骨骼結構和精確的蒙皮權重&#xff0c;大幅提升動畫制作的效率和質量。 UniR…

LeetCode 443 壓縮字符串

字符數組壓縮算法詳解&#xff1a;實現與分析 一、引言 在處理字符數組時&#xff0c;我們常常遇到需要對連續重復字符進行壓縮的場景。這不僅可以節省存儲空間&#xff0c;還能提升數據傳輸效率。本文將深入解析一個經典的字符數組壓縮算法&#xff0c;通過詳細的實現步驟和…

alertManager部署安裝、告警規則配置詳解及告警消息推送

? java接受告警請求RestController RequestMapping("/alert") Slf4j public class TestApi {private static final DateTimeFormatter FORMATTER DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");RequestMappingpublic void sendTemplate(HttpServl…

數據庫勒索病毒威脅升級:企業數據安全防線如何用安當RDM組件重構

摘要&#xff1a;2025年Q1全球數據庫勒索攻擊量同比激增101.8%&#xff0c;Cl0p、Akira等團伙通過邊緣設備漏洞滲透企業核心系統&#xff0c;制造業、金融業等關鍵領域面臨數據加密與業務停擺雙重危機。本文深度解析勒索病毒對數據庫的五大毀滅性影響&#xff0c;結合安當RDM防…