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目錄下

# 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下

# 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目錄中。

"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屬性,這樣也防止了容器操作不屬於其權限范圍的文件

# 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的大小和訪問權限:

tmpfs-size:指定tmpfs的大小

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

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

首先安裝docker插件

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

在node1節點上創建位於node2節點的卷,登陸的ssh密碼為root,對端ip為192.168.80.161

# 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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM