docker storage driver

  • docker默認有2種方式用於持久化數據,volumes和bind mounts,也可使用tmpfs,其中使用volume是持久化數據的最好方式,volume由docker控制管理,使用docker volume create建立一個volume時,其目錄會生成到/var/lib/docker/volumes目錄下。volumes和bind mounts用於將數據持久化到硬盤中,tmpfs的數據只存在於內存中,主要用於存儲容器運行過程當中的臨時數據。容器運行中若是會產生大量無需持久化的數據,建議將數據存放在tempfs中,這樣會提升性能
  • volumes和bind mounts相似,但volume不依賴host機器的系統文件,所以使用上兼容性更大,權限隔離性更高。volume由docker管理,限制了文件的訪問範圍(用戶和組),加強安全性。若是容器須要與host使用同一個文件目錄(爲了使用host的配置文件,如/etc/resolv.conf,或不一樣docker之間共享編譯件等),能夠考慮使用bind mount。當mount到一個容器中的非空目錄時,volume會保留該目錄的內容,而bind mount會覆蓋該目錄。
  • volumes和bind mounts默認使用rprivate類型的bind propagation,將命名空間中全部的mount point變爲private propagation類型。

使用docker目錄建立一個volume,並將該volume掛載到容器的/my_Cvol目錄下node

# docker volume create my_vo # docker run -itd --rm --mount source=my_vol,target=/my_Cvol busybox:latest /bin/sh

查看該volume,其源目錄實際在/var/lib/docker/volumes/my_vol/_data下git

# docker volume inspect my_vol [ { "CreatedAt": "2018-12-24T22:42:18+08:00", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/my_vol/_data", "Name": "my_vol", "Options": null, "Scope": "local" } ]

查看使用docker inspect容器相關信息,能夠看到volume的掛載信息,掛載到容器中的目錄是可讀寫的。這樣在容器的/my_Cvol目錄下的操做也會同步到host的/var/lib/docker/volumes/my_vol/_data目錄中。github

"Mounts": [ { "Type": "volume", "Name": "my_vol", "Source": "/var/lib/docker/volumes/my_vol/_data", "Destination": "/my_Cvol", "Driver": "local", "Mode": "z", "RW": true, "Propagation": "" }

查看host上/var/lib/docker/volumes/my_vol/_data的MAC屬性能夠看到它們變爲了容器的MAC屬性,這樣也防止了容器操做不屬於其權限範圍的文件docker

# ls -Z -rw-------. root root system_u:object_r:container_var_lib_t:s0 metadata.db drwxr-xr-x. root root system_u:object_r:container_var_lib_t:s0 my_vol

使用tmpfs主要用於存儲臨時數據,因爲tmpfs使用的是共享內存方式,因此其效率比較高。使用tmpfs時有以下2個選項用於指定tmpfs的大小和訪問權限:centos

tmpfs-size:指定tmpfs的大小安全

tmpfs-mode:指定mount的目錄權限bash

  • docker有不少插件能夠實現不一樣的需求,如實現NFS卷共享,使用雲提供商的存儲介質等(更多查看Volume plugins)。下面使用ssh在不一樣node節點間共享卷

首先安裝docker插件app

# docker plugin install --grant-all-permissions vieux/sshfs

在node1節點上建立位於node2節點的卷,登錄的ssh密碼爲root,對端ip爲192.168.80.161ssh

# docker volume create --driver vieux/sshfs -o sshcmd=root@192.168.80.161:/home/sshvolume -o password=root sshvolume

在host上查看容器進程的掛載信息,能夠看到其實際使用了fuse.sshfs的方式掛載了來自的root@192.168.80.161:/home/sshvolume目錄性能

# cat /proc/19574/mountinfo |grep 176
502 399 0:49 / /sshvolume rw,nosuid,nodev,relatime master:176 - fuse.sshfs root@192.168.80.161:/home/sshvolume rw,user_id=0,group_id=0

node1上查看docker卷信息,能夠看到新增了drive爲vieux/sshfs:latest,名字爲sshvolume的卷

# docker volume ls DRIVER VOLUME NAME vieux/sshfs:latest   sshvolume

在node1上建立一個容器,並將上一步的卷掛載到容器,在容器內部建立2個文件夾,登錄到node2的/home/sshvolume,能夠看到該目錄下有node1的容器建立的文件夾

docker run -itd --mount source=sshvolume,target=/sshvolume busybox:latest /bin/sh

 

docker storage driver

storage driver負責不一樣layer之間的交互,它容許在容器的讀寫層建立數據,讀寫層數據不會被持久化,且讀寫效率較低。如下圖所示,容器鏡像的layer是隻讀的,當建立一個容器時,會新增一個讀寫層,稱爲」container layer「,對容器的全部修改都在該layer上進行。當容器刪除後,該讀寫層也會被刪除。不一樣的storage driver實現不一樣,但全部的storage driver都使用了以下棧式鏡像結構以及CoW(copy-on-write)策略。這是對CoW的描述

而CoW技術可讓全部的容器共享image的文件系統,全部數據都從image中讀取,只有當要對文件進行寫操做時,才從image裏把要寫的文件複製到本身的文件系統進行修改。因此不管有多少個容器共享同一個image,所作的寫操做都是對從image中複製到本身的文件系統中的複本上進行,並不會修改image的源文件,且多個容器操做同一個文件,會在每一個容器的文件系統裏生成一個複本,每一個容器修改的都是本身的複本,相互隔離,相互不影響。使用CoW能夠有效的提升磁盤的利用率。

使用docker ps -s能夠查看鏡像大小,能夠看到"SIZE"有2個值,如container id爲5b22377a773d的容器中,38B表示容器讀寫層的數據總和;virtual表示只讀的鏡像層加上讀寫層的大小,不一樣的容器可能會共用部分或所有的鏡像層,所以計算容器佔用空間大小不能簡單地對virtual進行疊加

# docker ps -s CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE 5b22377a773d echo:v1 "/bin/sh"               2 hours ago         Up 2 hours                              practical_darwin    38B (virtual 1.2MB) 803ee1eb5acf echo:v1 "sh -c /home/echo.sh"   2 hours ago         Up 2 hours                              hungry_hertz        66B (virtual 1.2MB)

當在5b22377a773d中手動建立一個非空文件以後,能夠看到size變爲了95B

# docker ps -s CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE 5b22377a773d echo:v1 "/bin/sh"               2 hours ago         Up 2 hours                              practical_darwin    95B (virtual 1.2MB) 803ee1eb5acf 6d495122f721 "sh -c /home/echo.sh"   2 hours ago         Up 2 hours                              hungry_hertz        66B (virtual 1.2MB)

當使用docker pull拉取一個容器鏡像時,會在/var/lib/docker/<storage-driver>/layers/下面保存各個layer。

容器的讀寫層只保存修改過的變更,而未修改的文件或目錄等則不會被保存在讀寫層。當修改容器中已經存在的文件時,會執行CoW操做,此時在鏡像層中逐層搜索該文件,當找到該文件時,會將文件拷貝到容器的讀寫層(容器的鏡像只讀層可共享,但讀寫層不能夠共享,CoW技術能夠最大化減少容器佔用的磁盤,提升磁盤利用率)。當CoW的讀寫效率比較低,可能會影響IO效率,須要注意如下2點:

  • 若是程序運行時須要大量修改存在於鏡像只讀層的文件,能夠考慮將這些須要大量修改的文件單獨放在獨立於容器運行的volume中,能夠提升IO
  • 若是鏡像的layer比較多或須要修改的文件的目錄比較深也會影響CoW的效率

ocker的storage driver使用插件方式提供功能。插件的選擇取決於docker的版本以及使用的系統等,官方對storage driver的選擇有以下建議,能夠看出目前主要推薦overlay2。overlay和devicemapper已經在docker 18.09版本中被廢除

Linux distribution Recommended storage drivers Alternative drivers
Docker Engine - Community on Ubuntu overlay2 or aufs (for Ubuntu 14.04 running on kernel 3.13) overlaydevicemapperzfsvfs
Docker Engine - Community on Debian overlay2 (Debian Stretch), aufs or devicemapper (older versions) overlayvfs
Docker Engine - Community on CentOS overlay2 overlaydevicemapperzfsvfs
Docker Engine - Community on Fedora overlay2 overlaydevicemapperzfsvfs

不一樣storage driver所須要的文件系統以下:

Storage driver Supported backing filesystems
overlay2overlay xfs with ftype=1, ext4
aufs xfsext4
devicemapper direct-lvm
btrfs btrfs
zfs zfs
vfs any filesystem

不一樣的storage driver各有優缺點:

  • overlay2,aufs和overlay工做在文件級別(非塊級別),該模式下內存使用會更有效,但在對容器的讀寫層進行大量文件的修改時會致使讀寫層變大;
  • devicemapper,btrfs和zfs工做在塊級別,它們在進行大量文件的修改時則比上述更好,btrfs和zfs會消耗更多內存;
  • 在對小而多的文件進行修改或文件系統層級比較深的狀況下,overlay比overlay2更有效,但會消耗更多inode資源;
  • zfs在高密集操做時更適用;
  • overlay2,aufs,overlay和devicemapper的可靠性更高

在對storage driver修改時須要注意

Important: When you change the storage driver, any existing images and containers become inaccessible. This is because their layers cannot be used by the new storage driver. If you revert your changes, you can access the old images and containers again, but any that you pulled or created using the new driver are then inaccessible.

下面講解下overlay2的文件結構和特色,首先下載一個centos鏡像,查看改鏡像有以下3個layer

# docker history centos:latest IMAGE CREATED CREATED BY SIZE COMMENT 1e1148e4cc2c 2 months ago        /bin/sh -c #(nop)  CMD ["/bin/bash"] 0B <missing>           2 months ago        /bin/sh -c #(nop)  LABEL org.label-schema.sc… 0B <missing>           2 months ago        /bin/sh -c #(nop) ADD file:6f877549795f4798a…   202MB

使用docker inspect查看該鏡像能夠看到其文件系統

"GraphDriver": { "Data": { "MergedDir": "/var/lib/docker/overlay2/0bb8525e901534d5c3884a9e47b91d27f887278d0433506126c45081a2a482b8/merged", "UpperDir": "/var/lib/docker/overlay2/0bb8525e901534d5c3884a9e47b91d27f887278d0433506126c45081a2a482b8/diff", "WorkDir": "/var/lib/docker/overlay2/0bb8525e901534d5c3884a9e47b91d27f887278d0433506126c45081a2a482b8/work" }, "Name": "overlay2" },

使用docker run -itd centos:latest /bin/sh啓動一個centos的容器,此時會自動建立overlay須要的lowerdir,upperdir,merged和workdir,使用docker inspect命令,能夠看到該容器的overlay2使用狀況

"GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d-init/diff:/var/lib/docker/overlay2/0bb8525e901534d5c3884a9e47b91d27f887278d0433506126c45081a2a482b8/diff", "MergedDir": "/var/lib/docker/overlay2/7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d/merged", "UpperDir": "/var/lib/docker/overlay2/7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d/diff", "WorkDir": "/var/lib/docker/overlay2/7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d/work" }, "Name": "overlay2" },

merged,upperdir和lowerdir的定義以下,upperdir爲容器的讀寫層,lowerdir爲容器的鏡像只讀層,merged爲兩者的合集

 在容器建立後在/var/lib/docker/overlay2下面會生成2個新的目錄,其中7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d-init用於設置容器的初始環境

drwx------. 5 root root     69 Feb 13 22:17 7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d drwx------. 4 root root     55 Feb 13 22:17 7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d-init

在7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d目錄下能夠看到以下文件和目錄:diff爲該容器的UpperDir,對於容器的讀寫層,在容器中建立的文件或目錄都會體如今該目錄中(以下圖,在容器的/home下建立一個名爲newfile的文件和一個名爲newfoler的目錄,在diff/home下面也會同步體現該變化)。

# tree -L 1
  .
  ├── diff
  ├── link
  ├── lower
  ├── merged
  └── work

# pwd
  /var/lib/docker/overlay2/7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d/diff/home
# ll
  total 0
  -rw-r--r--. 1 root root 0 Feb 13 23:52 newfile
  drwxr-xr-x. 2 root root 6 Feb 13 23:54 newfolder

# cat link
  SKDGVP5O54VJTAXE7CQNUMIVLQ

link中包含了一個指向本目錄diff文件夾的索引SKDGVP5O54VJTAXE7CQNUMIVLQ,能夠在/var/lib/docker/l目錄下找到其定義,實際上是個系統連接(l目錄存在的意義是防止掛載時符號超出頁大小限制--默認4k)。

# ll ../l |grep SKDGVP5O54VJTAXE7CQNUMIVLQ lrwxrwxrwx. 1 root root 72 Feb 13 22:17 SKDGVP5O54VJTAXE7CQNUMIVLQ -> ../7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d/diff

lower的內容以下,其實就是上述的GraphDriver.Data.LowerDir,對應容器鏡像的只讀層

# cat lower l/G6URSFRXVFKZI5ESH2BGXG7LFS:l/T6OXBIDQ2GU523P5CXINFO3VH5

merged爲lower和link的合集,work爲OverlayFS內部使用的文件夾。

 

overlayFS讀文件時使用時有以下特性:

  • 文件不在容器層:此時會從鏡像查找並讀取該文件,該操做會影響一部分性能
  • 文件存在於容器層:直接讀取便可
  • 同時存在於鏡像層和容器層:讀取容器層文件,忽略鏡像層

overlayFS寫文件或目錄時有以下特性:

  • 當修改的文件不在容器層時,會執行copy_up操做,將該文件從鏡像層(lowerdir)拷貝到容器層(upperdir),須要注意的是,若是該文件很大時會影響性能
    • 對同一個文件的copy_up只會進行一次,後續對該文件的操做都會基於該文件的副本

因爲overlayFS的CoW特性,在容器中須要注意如下2點(詳情參見Use the OverlayFS storage driver):

  • 如使用fd1=open("foo", O_RDONLY) 後調用 fd2=open("foo", O_RDWR).打開鏡像層的文件時,原意是打開並返回同一個文件的2個描述符,但實際上因爲此時觸發了copy_up機制,fd1指向的是鏡像層的文件,而fd2指向容器層的文件,一種解決辦法是在調用open以前觸發copy_up,如使用touch命令
  • overlayFS不徹底支持rename系統調用

TIPS:

  • overlayFS使用了2層結構(lower和upper),相比aufs提升了執行效率
  • 能夠經過在/var/lib/docker/overlay2下面直接查看容器的鏡像只讀層和容器讀寫層的信息,但容器異常退出後,容器讀寫層會被刪除,只能查看鏡像只讀層的文件信息 
  • overlay在部署多個容器時會出現inode資源佔用過大問題,建議使用overlay2

參考:

https://docs.docker.com/storage/storagedriver/

https://arkingc.github.io/2017/05/05/2017-05-05-docker-filesystem-overlay/

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

相關文章
相關標籤/搜索