Docker 支持持久化和非持久化兩種方式的存儲。node
-
非持久化化存儲自動建立,從屬於容器,生命週期與容器相同,即刪除容器也會刪除所有非持久化數據。 -
若是想把容器中的數據保留下來,也就是持久化,那麼須要將數據存儲到捲上。卷與容器是解耦的,從而能夠獨立地建立並管理卷,而且卷也不與任意容器聲明週期綁定,即用戶刪除一個關聯了卷的容器,可是卷並不會被刪除。
非持久化存儲
每一個容器都會被自動分配本地存儲。默認狀況下,容器所有文件和目錄都是用該存儲的。非持久存儲屬於容器的一部分,而且與容器的生命週期同樣---容器建立時會建立非持久化存儲,同時該存儲也會隨着容器的刪除而刪除。web
在 Linux 系統中,該存儲目錄在 /var/lib/docker/<storage-driver>
下,是容器的一部分。這個 storage-driver
是指要使用的存儲驅動。假如要想在生產環境中使用 Linux 運行 Docker,須要確認當前的存儲驅動是否符合當前 Linux 版本:docker
-
RedHat Enterprise Linux:Docker 17.06 或者更高的版本中使用 Overlay2 驅動。 -
Ubuntu:使用 Overlay2 或者 AUFS 驅動。若是正在使用 Linux 4.x 或者更高版本的內核,建議使用 Overlay2。
總的來講,Overlay2 驅動正在逐漸流行,可能在將來會成爲大多數平臺上的推薦存儲驅動。微信
持久化
容器中持久化數據的方式推薦使用卷,也就是先建立卷,接着將卷掛載到容器上。這個時候,卷會掛載到容器文件系統的某個目錄中,任何寫到該目錄下的內容都會寫到卷中。即便容器被刪除了,卷及其上面的數據也仍然存在。編輯器
以下圖所示,Docker 卷就被掛載到了容器的 /code 目錄,那麼任何寫入 /code 目錄中的數據其實都是寫入到 Docker 卷中,而且這個 Docker 卷在容器刪除以後依然存在。而其餘目錄使用的都是臨時的本地存儲。性能
![](http://static.javashuo.com/static/loading.gif)
★卷本質就是 Docker 主機上的一個目錄。將 Docker 主機中的一個目錄掛載到了容器文件系統中的一個目錄後,此時操做容器文件系統中的目錄,其實就是操做相應的 Dokcer 主機上的目錄。也就是至關於容器再也不僅僅只能訪問容器的文件系統了,還能夠訪問所在 Docker 主機所在的文件系統了。spa
」
見識一下
建立和查看卷
docker volumn create myvol # 建立名爲 myvol 的卷
默認狀況下,Docker 建立新卷時採用內置的 local 驅動,採用這個驅動也就說明建立的卷只能被容器所在的 Docker 主機所使用(上述所使用的就是 local 驅動)。.net
除了 local 驅動以外,你還可使用 -d
參數指定不一樣的驅動。第三方驅動也能夠經過插件方式接入,這些驅動提供了高級存儲特性,併爲 Docker 集成了外部存儲系統。卷插件涵蓋了塊存儲、文件存儲、對象存儲等。插件
-
塊存儲:相對性能更高,適用於對小塊數據的隨機訪問負載。好比 Amazon EBS 或者 OpenStack 塊存儲服務。 -
文件存儲:包括 NFS 和 SMB 協議的系統,在高性能場景下表現優異。好比 NetApp FAS、Azure 文件存儲。 -
對象存儲:適用於較大且長期存儲的、不多變動的二進制數據存儲。一般對象存儲是根據內容尋址,而且性能較低。好比 Amazon S3。
docker volumn ls
docker volumn inspect [VOLUMN_NAME]
inspect 命令會輸出相應卷的詳細信息,Driver 和 Scope 都是 local,那麼表示這個卷使用默認 local 驅動建立,只能用於當前 Docker 主機上的容器。Mountpoint 表示卷位於 Docker 主機上的位置,使用 local 驅動建立的卷在 Docker 主機上均有專屬目錄。在 Linux 中則位於 /var/lib/docker/volumes
目錄下。3d
[
{
"CreatedAt": "2020-09-28T16:07:25+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/myvol/_data",
"Name": "myvol",
"Options": {},
"Scope": "local"
}
]
★Dockerfile 中可使用
」VOLUMN <container-mount-point>
指令的方式部署卷。須要注意的是 Dockerfile 中沒法指定主機目錄,由於主機目錄一般狀況是相對主機的一個目錄(就是跟主機有關的目錄),那麼這個目錄在不一樣主機間會不一樣,可能會致使構建失敗。若是經過 Dockerfile 指定,那麼每次部署時都須要指定主機目錄。
卷使用
docker container run -it --name voltainer --mount source=bizvol,target=/vol alpine
上述的命令建立了一個新的獨立容器,並將容器內的 /vol 目錄掛載到了名爲 bizvol 的卷。假如容器的文件系統中沒有 /vol 這個目錄,那麼會建立;假如已有這個目錄,那麼則會使用這個目錄(該目錄的內容到時候會變成卷裏面的內容)。同理,系統中沒有叫 bizvol 的卷,那麼該命令也會建立一個這樣的卷;若是已經存在這個捲了,那麼則使用這個卷。
![](http://static.javashuo.com/static/loading.gif)
假設,咱們把這個容器給刪除了,那麼 bizvol 這個卷仍是在的。並且,你在容器運行過程當中往 /vol 這個目錄中寫入的數據也在這個卷中。以下所示,在容器運行過程當中先往 /vol/file 中寫入一段數據,而後退出並刪除容器。以後,查看卷所在的目錄,發現建立的文件和寫入的數據仍是在的。
![](http://static.javashuo.com/static/loading.gif)
深刻深刻
上面對卷的闡述更可能是更可能是從持久化的角度出發,而卷的另外一大做用就是「打通」容器文件系統和主機文件系統,使得容器裏在指定目錄下建立的文件能夠被宿主機訪問到,也可使得宿主機上指定目錄下的文件能夠被容器裏的進程訪問到。那麼,這個是如何作到的呢?
這裏主要用到了 Linux 的綁定掛載(bind mount)機制。它的主要做用就是將一個目錄或者文件掛載到一個指定的目錄上。而且,以後你在掛載點上進行的任何操做,都只發生在被掛載的目錄或者文件上,而原掛載點的內容則會被隱藏起來且不受影響。綁定掛載其實是一個 inode 替換的過程。好比,執行 mount --bind /home /test
會將 /home 以 bind 的方式掛載到 /test 上。而這一操做其實就至關於將 /test 重定向到了 /home 的 inode 上。所以,當咱們修改 /test 目錄的時候,實際上修改的是 /home 目錄的 inode。
![](http://static.javashuo.com/static/loading.gif)
所以,咱們只須要在「容器進程「建立出來而且容器的 rootfs 準備好以後,可是在 chroot 以前,把 volume 指定的宿主機目錄掛載到指定的容器目錄在宿主機上對應的目錄便可(由於這時候容器進程能夠一直看到宿主機上的整個文件系統,同時因爲執行這個掛載操做的時候,容器已經建立出來了,那麼此時 mount namespace 至關於已經開啓了,因此掛載事件只在容器裏可見)。
★這邊的容器進程是 Docker 建立的一個容器初始化進程(dockerinit),而不是應用進程(ENTRYPOINT+CMD)。dockerinit 負責完成根目錄的準備、掛載設備和目錄、配置 hostname 等一系列須要在容器內進行的初始化操做。最後經過 execv() 系統調用,讓進程取代本身,成爲容器裏 PID=1 的進程。
」
因爲 volume 掛載到指定的容器目錄在宿主機上對應的目錄位於可讀寫層,那麼在 docker commit 的時候會被提交嘛?不會。這個主要是由於 docker commit 發生在宿主機空間,而這個 mount 發生在容器裏面,而且這個 mount 因爲 mount namespace 的隔離,不會影響到宿主機,也就是說宿主機上並無這個掛載。所以,在提交的時候只會提交一個空的目錄,由於 /test 是實實在在被新建在可讀寫層了的(這個新建可不受 mount namespace 的影響,由於 mount namespace 隻影響 mount 相關的)。
下面咱們來實驗一下,首先啓動一個容器而且讓這個容器使用一個 volume,掛載在容器裏的 /test 目錄上。以後在容器的 /test 目錄中建立一個新的文件爲 test.txt。
![](http://static.javashuo.com/static/loading.gif)
以後跑到卷所在的位置查看是否有相應的 test.txt 文件建立,結果顯示有 test.txt 文件建立。以後,咱們再去可讀寫層對應的目錄查看是否有 test.txt 文件,結果顯示是有 test 目錄,可是沒有 test.txt 文件。所以,docker commit 的時候只會提交一個 test 空目錄。
![](http://static.javashuo.com/static/loading.gif)
經常使用命令彙總
# 建立名爲 myvol 的卷。默認狀況下,新卷建立使用 local 啓動,可是也可使用 -d 指定不一樣的驅動
docker volumn create myvol
# 列出本地 Docker 主機上的所有卷
docker volumn ls
# 查看卷的詳細信息,能夠經過這條命令查看卷在 Docker 主機文件系統中的具體位置
docker volumn inspect [VOLUMN_NAME]
# 刪除未裝入到某個容器或者服務的全部卷,不能刪除正在被容器或者服務使用的卷
docker volumn prune
# 刪除指定卷,不能刪除正在被容器或者服務使用的卷
docker volumn rm [VOLUMN_NAME]
# 建立了一個新的容器,並將容器內的 /vol 目錄掛載到了名爲 bizvol 的卷。假如容器的文件系統中沒有 /vol 這個目錄,那麼會建立;假如已有這個目錄,那麼則會使用這個目錄(該目錄的內容到時候會變成卷裏面的內容)。同理,系統中沒有叫 bizvol 的卷,那麼該命令也會建立一個這樣的卷;若是已經存在這個捲了,那麼則使用這個卷。
docker container run -it --name voltainer --mount source=bizvol,target=/vol alpine
# 沒有顯示聲明宿主機目錄,那麼會在宿主機上建立一個臨時目錄 /var/lib/docker/volumn/[volume_name]/_data,而後把它掛載到容器 /test 目錄上。
docker run -v /test ...
# 把宿主機的 /home 目錄掛載到容器的 /test 目錄上
docker run -v /home:/test ...
本文分享自微信公衆號 - 多選參數(zhouxintalk)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。