docker的overlay文件系統

原文連接:https://arkingc.github.io/2017/05/05/2017-05-05-docker-filesystem-overlay/

docker原文:https://docs.docker.com/storage/storagedriver/overlayfs-driver/

 

OverlayFS是和AUFS相似的聯合文件系統(union filesystem),它有如下特點:

  • 設計簡潔;
  • 內核3.18開始已經併入內核主線
  • 可能更快

因此,它在社區迅速獲得廣泛關注並被很多人認爲是AUFS的繼承。但是它任然很年輕,
因此在生產環境中使用時要謹慎

docker的overlay存儲驅動利OverlayFS的一些特徵來構建以及管理鏡像和容器的磁盤結構

docker1.12後推出的overlay2在inode的利用方面比ovelay更有效overlay2要求內核版本大於等於4.0

overlay

如下圖所示,Overlay在主機上用到2個目錄,這2個目錄被看成是overlay的層。
upperdir爲容器層、lowerdir爲鏡像層使用聯合掛載技術將它們掛載在同一目錄(merged)下,提供統一視圖

圖片來源: Docker文檔

當容器層和鏡像層擁有相同的文件時,容器層的文件可見,隱藏了鏡像層相同的文件。
容器掛載目錄(merged)提供了統一視圖

overlay只使用2層,意味着多層鏡像不會被實現爲多個OverlayFS層。每個鏡像被實現爲自己的目錄,
這個目錄在路徑/var/lib/docker/overlay下。硬鏈接被用來索引和低層共享的數據,節省了空間

當創建一個容器時,overlay驅動連接代表鏡像層頂層的目錄(只讀)和一個代表容器層的新目錄(讀寫)

例子:鏡像和容器的磁盤結構[overlay]

 
root@chenximing-MS-7823:/home/chenximing# tree /var/lib/docker/overlay/

/var/lib/docker/overlay/

一開始overlay路徑下爲空,用docker pull命令下載一個由5層鏡像層組成的docker鏡像:

 
chenximing@chenximing-MS-7823:~$ docker pull ubuntu

Using default tag: latest
latest: Pulling from library/ubuntu
c62795f78da9: Pull complete 
d4fceeeb758e: Pull complete 
5c9125a401ae: Pull complete 
0062f774e994: Pull complete 
6b33fd031fac: Pull complete 
Digest: sha256:c2bbf50d276508d73dd865cda7b4ee9b5243f2648647d21e3a471dd3cc4209a0
Status: Downloaded newer image for ubuntu:latests

此時,在路徑/var/lib/docker/overlay/下,每個鏡像層都有一個對應的目錄,包含了該層鏡像的內容,通過tree命令發現,每個鏡像層下只包含一個root目錄 (因爲docker1.10開始,使用基於內容的尋址,因此目錄名和鏡像層的id不一致)

 
root@chenximing-MS-7823:/home/chenximing# tree -L 2 /var/lib/docker/overlay/

/var/lib/docker/overlay/
├── 3279a41fd358cdda798d99cc2da0425b4836a489083ae9db4aedc834f426915a
│   └── root
├── 33629fe3999e692ecbeb340f855a2d05ddf4e173ea915041be217e1c9cc8a48f
│   └── root
├── 6ab876fcc7ad584dd1eebae0d9304abb22561012f6adb87828f57906d799c33b
│   └── root
├── 77806a4b8257cd5508a1131d4d47c2d4b4d51703a1cc0dd8daddf0e86a68d492
│   └── root
└── ed271f2159e3c9de18ede980e4e2ecb0ef39b96927d446ba93deab0b6ff1a8e3
    └── root

每一層都包含了”該層獨有的文件”以及”和其低層共享的數據的硬連接”。如ls命令,每一層都使用一個硬鏈接來指向實際ls命令的inode號(405241):

 
root@chenximing-MS-7823:/home/chenximing# ls -i /var/lib/docker/overlay/3279a41fd358cdda798d99cc2da0425b4836a489083ae9db4aedc834f426915a/root/bin/ls
    
405241 /var/lib/docker/overlay/3279a41fd358cdda798d99cc2da0425b4836a489083ae9db4aedc834f426915a/root/bin/ls
    
root@chenximing-MS-7823:/home/chenximing# ls -i /var/lib/docker/overlay/33629fe3999e692ecbeb340f855a2d05ddf4e173ea915041be217e1c9cc8a48f/root/bin/ls
    
405241 /var/lib/docker/overlay/33629fe3999e692ecbeb340f855a2d05ddf4e173ea915041be217e1c9cc8a48f/root/bin/ls

使用剛剛拉取的ubuntu鏡像創建一個容器,用docker ps命令查看:

 
root@chenximing-MS-7823:/home/chenximing# docker ps
    
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
8963ffb422fa        ubuntu              "/bin/bash"         32 minutes ago      Up 52 seconds                           container_1

創建容器時,實際上是在已有的鏡像層上創建了一層容器層,容器層在路徑/var/lib/docker/overlay下也存在對應的目錄:

 
root@chenximing-MS-7823:/home/chenximing# ls /var/lib/docker/overlay/
    
3279a41fd358cdda798d99cc2da0425b4836a489083ae9db4aedc834f426915a
33629fe3999e692ecbeb340f855a2d05ddf4e173ea915041be217e1c9cc8a48f
6ab876fcc7ad584dd1eebae0d9304abb22561012f6adb87828f57906d799c33b
6abc47fcf668b6820a2a9a8b0d0c6150f9df61ab5a323783630fac3fd282144d       //創建容器後新增的目錄
6abc47fcf668b6820a2a9a8b0d0c6150f9df61ab5a323783630fac3fd282144d-init  //創建容器後新增的目錄
77806a4b8257cd5508a1131d4d47c2d4b4d51703a1cc0dd8daddf0e86a68d492
ed271f2159e3c9de18ede980e4e2ecb0ef39b96927d446ba93deab0b6ff1a8e3

“6abc47……”和“6abc47…..-init”爲創建容器後新增的目錄。查看這兩個目錄的內容:

 
root@chenximing-MS-7823:/home/chenximing# tree -L 3 /var/lib/docker/overlay/6abc47fcf668b6820a2a9a8b0d0c6150f9df61ab5a323783630fac3fd282144d*/
   
/var/lib/docker/overlay/6abc47fcf668b6820a2a9a8b0d0c6150f9df61ab5a323783630fac3fd282144d/
├── lower-id
├── merged
├── upper
│   ├── dev
│   │   └── console
│   ├── etc
│   │   ├── hostname
│   │   ├── hosts
│   │   ├── mtab -> /proc/mounts
│   │   └── resolv.conf
│   └── root
└── work
    └── work
    
/var/lib/docker/overlay/6abc47fcf668b6820a2a9a8b0d0c6150f9df61ab5a323783630fac3fd282144d-init/
├── lower-id
├── merged
├── upper
│   ├── dev
│   │   └── console
│   └── etc
│       ├── hostname
│       ├── hosts
│       ├── mtab -> /proc/mounts
│       └── resolv.conf
└── work
    └── work

“6abc47……”爲讀寫層,“6abc47…..-init”爲初始層。初始層中大多是初始化容器環境時,與容器相關的環境信息,如容器主機名,主機host信息以及域名服務文件等。所有對容器做出的改變都記錄在讀寫層

文件lower-id用來索引該容器使用的鏡像層,upper目錄包含了容器層的內容,每當啓動一個容器時,會將lower-id指向的鏡像層目錄以及upper目錄聯合掛載到merged目錄,因此,容器內的視角就是merged目錄下的內容。而work目錄則是用來完成如copy-on_write的操作

看看容器使用到了哪一層鏡像層:

 
root@chenximing-MS-7823:/home/chenximing# cat /var/lib/docker/overlay/6abc47fcf668b6820a2a9a8b0d0c6150f9df61ab5a323783630fac3fd282144d/lower-id
    
ed271f2159e3c9de18ede980e4e2ecb0ef39b96927d446ba93deab0b6ff1a8e3
    
root@chenximing-MS-7823:/home/chenximing# cat /var/lib/docker/overlay/6abc47fcf668b6820a2a9a8b0d0c6150f9df61ab5a323783630fac3fd282144d-init/lower-id
    
ed271f2159e3c9de18ede980e4e2ecb0ef39b96927d446ba93deab0b6ff1a8e3

在剛纔創建的容器中創建一個文件:

 
root@8963ffb422fa:/# touch file
    
root@8963ffb422fa:/# echo "hello world" > file
    
root@8963ffb422fa:/# ls
bin   dev  file  lib    media  opt   root  sbin  sys  usr
boot  etc  home  lib64  mnt    proc  run   srv   tmp  var

此時再觀察“6abc47……”目錄(讀寫層):

 
root@chenximing-MS-7823:/home/chenximing# tree -L 3 /var/lib/docker/overlay/6abc47fcf668b6820a2a9a8b0d0c6150f9df61ab5a323783630fac3fd282144d/  
    
/var/lib/docker/overlay/6abc47fcf668b6820a2a9a8b0d0c6150f9df61ab5a323783630fac3fd282144d/
├── lower-id
├── merged
├── upper
│   ├── dev
│   │   └── console
│   ├── etc
│   │   ├── hostname
│   │   ├── hosts
│   │   ├── mtab -> /proc/mounts
│   │   └── resolv.conf
│   ├── file
│   └── root
└── work
    └── work

發現upper目錄下多出了一個file文件,就是剛纔在容器中創建的文件

overlay2

和overlay爲了實現“兩個目錄反映多層鏡像“而使用硬鏈接不同,overlay2驅動天生支持多層。(最多128)

因此,overlay2在使用docker層相關的命令時,能提供更好的性能(如:docker build、docker commit)。而且overlay2消耗的inode節點更少

例子:鏡像和容器的基於磁盤的結構[overlay2]

docker pull ubuntu拉取完一個5層的Ubuntu鏡像後,/var/lib/docker/overlay2下可以看到6個目錄:

 
root@chenximing-MS-7823:/home/chenximing# tree -L 2 /var/lib/docker/overlay2
    
/var/lib/docker/overlay2
├── 1d18eac18e483e5af46d52673e254cb89996041d91356c6dd1f0ac1518ba130a
│   ├── diff
│   └── link   //文件
├── 28702e0cca9ff9a3245ce7e61326d098788e855ea599bf160465f537756a56a6
│   ├── diff
│   ├── link   //文件
│   ├── lower   //文件
│   ├── merged
│   └── work
├── 299397034eb96f22d544bf544c9ef993fa32571f466bd8a881aec0fc5a94a8df
│   ├── diff
│   ├── link   //文件
│   ├── lower   //文件
│   ├── merged
│   └── work
├── 8c0de7df4581df4787b08a28356cc8d7ba7b420c70dc36cc0615afdafb6ee15a
│   ├── diff
│   ├── link   //文件
│   ├── lower   //文件
│   ├── merged
│   └── work
├── f21a47541026214e519fccdf8b838ac2c4e11a1a2dd6a5febc061381a1972ad7
│   ├── diff
│   ├── link   //文件
│   ├── lower   //文件
│   ├── merged
│   └── work
└── l
    ├── 5DJFQNGXSA5CVOC6NA6HPUCXXB -> ../299397034eb96f22d544bf544c9ef993fa32571f466bd8a881aec0fc5a94a8df/diff
    ├── EGQNS3O24ONBL5BJSGNTX4NP6E -> ../1d18eac18e483e5af46d52673e254cb89996041d91356c6dd1f0ac1518ba130a/diff
    ├── JWB63ZJDZOTK5N22OMR5BKUVMG -> ../8c0de7df4581df4787b08a28356cc8d7ba7b420c70dc36cc0615afdafb6ee15a/diff
    ├── UZHRLLTPQMODQQYBLN6NOT6N2K -> ../28702e0cca9ff9a3245ce7e61326d098788e855ea599bf160465f537756a56a6/diff
    └── X76VPZDDKIW3GLWBFUHKDFBEPE -> ../f21a47541026214e519fccdf8b838ac2c4e11a1a2dd6a5febc061381a1972ad7/diff
    
24 directories, 9 files

”l“目錄包含一些符號鏈接作爲縮短的層標識符. 這些縮短的標識符用來避免掛載時超出頁面大小的限制

 
root@chenximing-MS-7823:/home/chenximing# ls -l /var/lib/docker/overlay2/l/
    
總用量 20
lrwxrwxrwx 1 root root 72  4月 20 17:31 5DJFQNGXSA5CVOC6NA6HPUCXXB -> ../299397034eb96f22d544bf544c9ef993fa32571f466bd8a881aec0fc5a94a8df/diff
lrwxrwxrwx 1 root root 72  4月 20 17:31 EGQNS3O24ONBL5BJSGNTX4NP6E -> ../1d18eac18e483e5af46d52673e254cb89996041d91356c6dd1f0ac1518ba130a/diff
lrwxrwxrwx 1 root root 72  4月 20 17:31 JWB63ZJDZOTK5N22OMR5BKUVMG -> ../8c0de7df4581df4787b08a28356cc8d7ba7b420c70dc36cc0615afdafb6ee15a/diff
lrwxrwxrwx 1 root root 72  4月 20 17:31 UZHRLLTPQMODQQYBLN6NOT6N2K -> ../28702e0cca9ff9a3245ce7e61326d098788e855ea599bf160465f537756a56a6/diff
lrwxrwxrwx 1 root root 72  4月 20 17:31 X76VPZDDKIW3GLWBFUHKDFBEPE -> ../f21a47541026214e519fccdf8b838ac2c4e11a1a2dd6a5febc061381a1972ad7/diff

最底層包含”link”文件(不包含lower文件,因爲是最底層),在上面的結果中“1d8e….”爲最底層。這個文件記錄着作爲標識符的更短的符號鏈接的名字、最底層還有一個”diff”目錄(包含實際內容)

 
root@chenximing-MS-7823:/home/chenximing# cat /var/lib/docker/overlay2/1d18eac18e483e5af46d52673e254cb89996041d91356c6dd1f0ac1518ba130a/link 
    
EGQNS3O24ONBL5BJSGNTX4NP6E
    
root@chenximing-MS-7823:/home/chenximing# ls /var/lib/docker/overlay2/1d18eac18e483e5af46d52673e254cb89996041d91356c6dd1f0ac1518ba130a/diff/
    
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr

從第二層開始,每層鏡像層包含”lower“文件,根據這個文件可以索引構建出整個鏡像的層次結構。”diff“文件(層的內容)。還包含”merged“和”work”目錄,用途和overlay一樣

 
root@chenximing-MS-7823:/home/chenximing# cat /var/lib/docker/overlay2/28702e0cca9ff9a3245ce7e61326d098788e855ea599bf160465f537756a56a6/link 
    
UZHRLLTPQMODQQYBLN6NOT6N2K
    
root@chenximing-MS-7823:/home/chenximing# ls /var/lib/docker/overlay2/28702e0cca9ff9a3245ce7e61326d098788e855ea599bf160465f537756a56a6/diff/
    
etc  sbin  usr  var
    
root@chenximing-MS-7823:/home/chenximing# cat /var/lib/docker/overlay2/28702e0cca9ff9a3245ce7e61326d098788e855ea599bf160465f537756a56a6/lower
    
l/EGQNS3O24ONBL5BJSGNTX4NP6E

再來看看容器層,容器層的文件構成和鏡像層類似(這點和overlay不同),使用剛剛拉取的ubuntu鏡像創建一個容器,/var/lib/docker/overlay2目錄下新增2個目錄:

 
├── 2558ed8dd7051c1e78ea980590a228100ec3d299c01b35e9b7fa503723593d19
│   ├── diff
│   ├── link
│   ├── lower
│   ├── merged
│   └── work
├── 2558ed8dd7051c1e78ea980590a228100ec3d299c01b35e9b7fa503723593d19-init
│   ├── diff
│   ├── link
│   ├── lower
│   ├── merged
│   └── work
└── l
    ├── 7H7MUMX4IOAVLIJ2YPLR5MZOO5 -> ../2558ed8dd7051c1e78ea980590a228100ec3d299c01b35e9b7fa503723593d19-init/diff
    ├── EBSBKJBLA2WYBHTMHKSMCEEWNP -> ../2558ed8dd7051c1e78ea980590a228100ec3d299c01b35e9b7fa503723593d19/diff

    
root@chenximing-MS-7823:/home/chenximing# cat /var/lib/docker/overlay2/2558ed8dd7051c1e78ea980590a228100ec3d299c01b35e9b7fa503723593d19/lower 
    
l/7H7MUMX4IOAVLIJ2YPLR5MZOO5:l/5DJFQNGXSA5CVOC6NA6HPUCXXB:l/JWB63ZJDZOTK5N22OMR5BKUVMG:l/X76VPZDDKIW3GLWBFUHKDFBEPE:l/UZHRLLTPQMODQQYBLN6NOT6N2K:l/EGQNS3O24ONBL5BJSGNTX4NP6E

容器的讀寫

對於讀,考慮下列3種場景:

  • 讀的文件不在容器層:如果讀的文件不在容器層,則從鏡像層進行讀
  • 讀的文件只存在在容器層:直接從容器層讀
  • 讀的文件在容器層和鏡像層:讀容器層中的文件,因爲容器層隱藏了鏡像層同名的文件

對於寫,考慮下列場景:

  • 寫的文件不在容器層,在鏡像層:由於文件不在容器層,因此overlay/overlay2存儲驅動使用copy_up操作從鏡像層拷貝文件到容器層,然後將寫入的內容寫入到文件新的拷貝中
  • 刪除文件和目錄:刪除鏡像層的文件,會在容器層創建一個whiteout文件來隱藏它;刪除鏡像層的目錄,會創建opaque目錄,它和whiteout文件有相同的效果
  • 重命名目錄:對一個目錄調用rename(2)僅僅在資源和目的地路徑都在頂層時才被允許,否則返回EXDEV

配置Docker使用overlay/overlay2存儲驅動

  • 配置overlay要求內核版本大於等於3.18,且加載了overlay內核模塊;
  • 配置overlay2要求內核版本大於等於4.0;
  • docker daemon處於stopped狀態;

OverlayFS能在大多數Linux文件系統上運行,但是ext4是目前在生產環境中被推薦的

1) stop Docker daemon

 
$ sudo service docker stop

2) 確認內核版本以及overlay內核模塊已經加載

 
#查看內核版本
$ uname -r  

#查看內核是否已加載overlay模塊 
$ lsmod | grep overlay  
overlay

如果未加載overlay模塊,假設你的內核已經編譯了overlay模塊,則可以通過下列命令進行加載,或者在啓動docker服務時,會自動加載overlay模塊:

 
$ modprobe overlay

3) 啓動docker daemon(使用overlay/overlay2存儲驅動)

方法一(推薦)
在/etc/docker/路徑下創建daemon.json文件,並添加下列內容:

 
{
  "storage-driver": "overlay2"
}

然後啓動docker服務:

 
$ sudo service docker start

方法二:

 
$ dockerd --storage-driver=overlay &  

[1] 29403  
root@ip-10-0-0-174:/home/ubuntu# INFO[0000] Listening for HTTP on unix (/var/run/docker.sock)  
INFO[0000] Option DefaultDriver: bridge  
INFO[0000] Option DefaultNetwork: bridge

4) 覈實daemon已經在使用overlay/overlay2

 
$ docker info
    
Containers: 0  
Images: 0  
Storage Driver: overlay  
Backing Filesystem: extfs
...

OverlayFS and Docker 性能

一般來說,overlay/overlay2很快,幾乎肯定比aufs和devicemapper快。在某些特定場景下,還可能比btrfs快。
此外,還有幾點overlay/overlay2驅動性能相關的注意事項:

  • 頁緩存:OverlayFS支持頁緩存共享,意味着多個容器訪問相同的文件能夠共享一個單一的page cache entry
    使得overlay/overlay2驅動能高效使用內存,是PaaS以及其它高密度場景一個好的選擇
  • copy_up:對鏡像層大文件進行寫操作時,copy-on-write會給寫操作帶來大量延遲
  • inode 限制:使用overlay會引起過度的inode消耗,消耗會隨着主機上的鏡像和容器的增加而增加。擁有大量鏡像的主機在大量容器啓動和停止時可能會耗盡inodes。不幸的是你只能在文件系統創建時指定inode數,因此你可能需要考慮將/var/lib/docker放在另一個獨立的設備上,或者在創建文件系統時手動修改inode值。而overlay2則沒有這樣的問題
  • RPM和Yum:OverlayFS僅實現了POSIX標準的一部分,某些操作還會違反POSIX標準,copy_up操作就是其中一個

下面是提升OverlayFS驅動性能的最佳實踐:

  • SSD:爲了獲得最佳性能,一個通常的想法是使用諸如SSD這類更快的存儲設備
  • 使用數據卷: 數據卷提供了最好的以及最可預見的性能。因爲繞過了存儲驅動,因此不會存在瘦供給和copy-on-write帶來的潛在性能開銷。因此,寫操作較頻繁的數據應該放在數據捲上
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章