認識 Docker Volume







容器技術使用了 rootfs 機制和 Mount Namespace,構建出了一個同宿主機完全隔離開的文件系統環境。
那麼這時候還需要考慮兩個問題:

1、宿主機上的文件和目錄,怎麼才能讓容器裏的進程訪問到?
2、而容器裏進程新建的文件,怎麼才能讓宿主機獲取到?

這正是 Docker Volume 要解決的問題:Volume 機制,允許你將宿主機上指定的目錄或者文件,掛載到容器裏面進行讀取和修改操作。




一、宿主機目錄掛載到容器


那麼,Docker 是如何做到把一個宿主機上的目錄或者文件,掛載到容器裏面去呢?

當容器進程被創建之後,
儘管開啓了 Mount Namespace,但是在執行 chroot(或者 pivot_root)之前,容器進程一直可以看到宿主機上的整個文件系統。

而宿主機的文件系統包括了容器鏡像,容器鏡像的各個層,保存在 /var/lib/docker/aufs/diff 目錄下,
在容器進程啓動後,它們會被聯合掛載到 /var/lib/docker/aufs/mnt/ 目錄中,這樣容器所需的 rootfs 就準備好了。

所以,
我們只需要在 rootfs 準備好之後,在執行 chroot 之前,
把 Volume 指定的宿主機目錄(比如 /home 目錄)掛載到指定的容器目錄(比如 /test 目錄)在宿主機上對應的目錄(即 /var/lib/docker/aufs/mnt/[可讀寫層 ID]/test)上,
這個 Volume 的掛載工作就完成了。

另外,由於此時 Mount Namespace 已經開啓;所以,這個掛載事件只在這個容器裏可見。
你在宿主機上,是看不見容器內部的這個掛載點的; 這就保證了容器的隔離性不會被 Volume 打破。




二、 bind mount 原理


上面用到的掛載技術,就是 Linux 的綁定掛載(bind mount)機制。
它的主要作用就是,允許你將一個目錄或者文件,而不是整個設備,掛載到一個指定的目錄上。
並且,這時你在該掛載點上進行的任何操作,只是發生在被掛載的目錄或者文件上,而原掛載點的內容則會被隱藏起來且不受影響。

bind mount 綁定掛載實際上是一個 inode 替換的過程:
在 Linux 操作系統中,inode 可以理解爲存放文件內容的“對象”,而 dentry,也叫目錄項,就是訪問這個 inode 所使用的“指針”。

在這裏插入圖片描述

如上圖所示,

mount --bind  /home /test

會將 /home 掛載到 /test 上,相當於將 /test 的 dentry,重定向到了 /home 的 inode。

當我們修改 /test 目錄時,實際修改的是 /home 目錄的 inode。
所以一旦執行 umount 命令,/test 目錄原先的內容就會恢復:因爲修改真正發生在的,是 /home 目錄裏。

所以,在一個正確的時機,進行一次綁定掛載,Docker 就可以成功地將一個宿主機上的目錄或文件掛載到容器中,讓容器裏的進程訪問到。

這樣,進程在容器裏對這個 /test 目錄進行的所有操作,都實際發生在宿主機的對應目錄 /home 裏,而不會影響容器鏡像的內容。




三、Volume 信息 是否會被 docker commit 提交?


那麼,這個 /test 目錄裏的內容,既然掛載在容器 rootfs 的可讀寫層,它會不會被 docker commit 提交掉呢?
也不會。
容器的鏡像操作,比如 docker commit,都是發生在宿主機空間的。
而由於 Mount Namespace 的隔離作用,宿主機並不知道這個綁定掛載的存在。
在宿主機看來,容器中可讀寫層的 /test 目錄(/var/lib/docker/aufs/mnt/[可讀寫層 ID]/test),始終是空的。

所以,容器 Volume 裏的信息並不會被 docker commit 提交掉;但這個掛載點目錄 /test 本身會出現在新的鏡像當中。




volume 本質上只是宿主機上的一個獨立目錄,而並不屬於 rootfs 的一部分!




參考文檔:張磊老師的 深入剖析Kubernetes

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章