docker容器技術基礎之聯合檔案系統OverlayFS

頂級飲水機管理員發表於2021-07-23

我們在上篇介紹了容器技術中資源隔離與限制docker容器技術基礎之linux cgroup、namespace

這篇小作文我們要嘗試學習容器的另外一個重要技術之聯合檔案系統之OverlayFS,在介紹OverlayFS之前我們會學習一下映象、容器、層的相關知識,然後是OverlayFS及相關例項,最後介紹docker中overlay2驅動即overlayfs在容器中的實現。


一、映象、容器和層

docker中映象是層級結構的,即圖中的image layers,每一層只是與它之前的層的一組差異。這些層堆疊在彼此的頂部。當我們建立一個新容器時,會在映象層加一個新的可寫層。這一層通常被稱為“容器層”。對正在執行的容器所做的所有更改,例如寫入新檔案、修改現有檔案和刪除檔案,都將寫入這個薄的可寫容器層。

container-layers

那麼一個映象建立多個容器時是怎樣的景象呢?如下圖所示,新增新資料或修改現有資料的所有寫入容器都儲存在此可寫層中。當容器被刪除時,可寫層也被刪除。底層映象保持不變。因為每個容器都有自己的可寫容器層,所有的變化都儲存在這個容器層中,所以多個容器可以共享對同一個底層映象的訪問,同時又擁有自己的資料狀態。

sharing-layers

到這裡你可能要問了:映象為什麼要分層啊?亂七八糟的!

其實不然,通過映象的層級結構主要的一個優點是你可以把你的基礎映象進行共享,什麼意思呢?比如你現在需要一個Nginx映象、一個Tomcat映象它們都可以通過一個base映象如centos或者ubuntu製作而成,它看起來是這樣的

image-20210723103255000

如此一來通過映象分層可以大大減少磁碟空間佔用,同時降低映象復構建雜度,何樂而不為。


二、聯合檔案系統OverlayFS

通過上面我們大概瞭解了映象、容器和層的關係,那麼又有一個問題了:映象層和可寫容器層的檔案或者內容是如何來管理的?明明是分層的又是怎麼合併的?

接下來我們將介紹UnionFS(聯合檔案系統),它的厲害之處在於可以將多個目錄掛載到一個根目錄。OverlayFS 是linux現代聯合檔案系統的一個代表,合併於Linux核心的3.18版本。從 docker 18.06後docker為OverlayFS提供了兩個儲存驅動,原始的overlay及overlay2(改善 inode 利用率),overlay2是目前docker推薦和首選儲存驅動,通過它來管理映象層和可寫容器層內容。

我們可以在docker info中檢視docker儲存驅動版本

[root@i-k9pwet2d ~]# docker info
...
 Server Version: 20.10.6
 Storage Driver: overlay2
 Backing Filesystem: extfs
...

OverlayFS這種堆疊的檔案系統,依賴於其他檔案系統之上,比如我們在info 中看到的extfs或者xfs等,它的結構如下圖:

upper-lower

我們的基礎層稱為“lowerdir”即原始檔案所在的位置。

客戶端所做的任何修改都將反映在“upperdir”層上:

  • 如果更改檔案,新版本將寫入其中(file1)。
  • 如果刪除檔案,將在該層上建立一個刪除標記(file2)。
  • 建立一個新檔案(file4)。

最後,“merged”是所有層合併後的最終檢視。

假如你有一些資料,需要多個程式來訪問和修改它。每個程式都要建立一個獨立的資料檢視,你要儲存多份原始資料,資料量大的話顯然這會非常低效的。使用OverlayFS將會是very good!

接下來我們來我們搞個實驗看看

我建立如下目錄結構,workdir在OverlayFS中需要為空,用作內部臨時儲存。lowerdir包含3個檔案file1、file2、file3

[root@i-k9pwet2d overlayfs_test]# tree .
.
├── client_1
│   ├── upperdir
│   └── workdir
├── client_2
│   ├── upperdir
│   └── workdir
├── lowerdir
│   ├── file1.txt
│   └── file2.txt
│	└── file3.txt
└── merged
    ├── client_1
    └── client_2

10 directories, 3 files

掛載overlay

mount -t overlay overlay \
-o lowerdir=/overlaytest/lowerdir \
-o upperdir=/overlaytest/client_1/upperdir \
-o workdir=/overlaytest/client_1/workdir \
/overlaytest/merged/client_1

mount -t overlay overlay \
-o lowerdir=/overlaytest/lowerdir \
-o upperdir=/overlaytest/client_2/upperdir \
-o workdir=/overlaytest/client_2/workdir \
/overlaytest/merged/client_2

掛載後檢視我們的檢視,可以看到三個檔案已經被合併到merged區了

[root@i-k9pwet2d overlaytest]# tree .
.
├── client_1
│   ├── upperdir
│   └── workdir
│       └── work
├── client_2
│   ├── upperdir
│   └── workdir
│       └── work
├── lowerdir
│   ├── file1.txt
│   ├── file2.txt
│   └── file3.txt
└── merged
    ├── client_1
    │   ├── file1.txt
    │   ├── file2.txt
    │   └── file3.txt
    └── client_2
        ├── file1.txt
        ├── file2.txt
        └── file3.txt

下一步我們修改merged/client_1下修改我們的都資料

[root@i-k9pwet2d client_1]# echo "data no.1">>file1.txt 
[root@i-k9pwet2d client_1]# rm file2.txt 
[root@i-k9pwet2d client_1]# echo "data4" > file4.txt
[root@i-k9pwet2d client_1]# ls
file1.txt  file3.txt  file4.txt

再看我們的檢視,可以看到修改只作用於client_1/upperdir,對我們lowerdir下原始資料以及client_2資料並不影響。

[root@i-k9pwet2d overlaytest]# tree .
.
├── client_1
│   ├── upperdir
│   │   ├── file1.txt
│   │   ├── file2.txt
│   │   └── file4.txt
│   └── workdir
│       └── work
├── client_2
│   ├── upperdir
│   └── workdir
│       └── work
├── lowerdir
│   ├── file1.txt
│   ├── file2.txt
│   └── file3.txt
└── merged
    ├── client_1
    │   ├── file1.txt
    │   ├── file3.txt
    │   └── file4.txt
    └── client_2
        ├── file1.txt
        ├── file2.txt
        └── file3.txt

12 directories, 12 files

OverlayFS在容器中的實現

下圖顯示了 在Docker 中映象和 容器是如何通過OverlayFS分層與互相構造的對映。影像層是lowerdir,容器層是upperdir。統一檢視合併到merged目錄,該目錄實際上是容器安裝點。

overlay_constructs

我們檢視一個真正執行容器centos的inspect

docker inspect ca9a9e0a35c7

可以看到OverlayFS對應的目錄地址,lowerDir即我們的原始資料包含image的rootfs(根檔案)以及init相關檔案,關於docker init層可以自行檢索一下哈,這裡不做介紹了。

"GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/444808f5d6566eebf4ea73ea593b2c2076d4347ff57bd98cbc179dbac9265968-init/diff:/var/lib/docker/overlay2/0d6b94986ba1af1cc75e7c237f78d7e02d40f5ae5ec3f67ddb699ae6d07a2ca8/diff",
                "MergedDir": "/var/lib/docker/overlay2/444808f5d6566eebf4ea73ea593b2c2076d4347ff57bd98cbc179dbac9265968/merged",
                "UpperDir": "/var/lib/docker/overlay2/444808f5d6566eebf4ea73ea593b2c2076d4347ff57bd98cbc179dbac9265968/diff",
                "WorkDir": "/var/lib/docker/overlay2/444808f5d6566eebf4ea73ea593b2c2076d4347ff57bd98cbc179dbac9265968/work"
            },
            "Name": "overlay2"
        },

LowerDir下檢視原始rootfs

[root@i-k9pwet2d client_1]# ls /var/lib/docker/overlay2/0d6b94986ba1af1cc75e7c237f78d7e02d40f5ae5ec3f67ddb699ae6d07a2ca8/diff
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

我們進入容器新增和刪除檔案看看

[root@i-k9pwet2d ~]# docker exec -it ca9a /bin/bash

[root@ca9a9e0a35c7 /]# echo "newfile" >file 

[root@ca9a9e0a35c7 /]# rm /tmp/ks-script-esd4my7v

顯然到到LowerDir下檢視原始rootfs並不受影響而是把變更寫入到了upperdir即容器層

cd  /var/lib/docker/overlay2/444808f5d6566eebf4ea73ea593b2c2076d4347ff57bd98cbc179dbac9265968/diff

[root@i-k9pwet2d diff]# tree .
.
├── file
└── tmp
    └── ks-script-esd4my7v

1 directory, 2 files

以上的操作也就是docker中所謂的CoW(寫時複製)策略。在docker中overlay2驅動對聯合檔案系統操作的更多場景可以參閱官方文件


這樣我們實現容器的三大基礎技術Namespace、Cgroup、UnionFS聯合檔案系統已經介紹完啦,希望這三篇小作文對想了解容器實現原理的讀者有些許幫助。


參考:


小作文有不足的地方歡迎指出。

感謝收藏、點贊。關注頂級飲水機管理員,除了燒熱水,有時還做點別的。

您的支援是我燒熱水最大的動力...

相關文章