一、數據管理實現方式
docker 容器中的文件系統有很多優勢,但也有很多弊端:
- 沙盒文件系統是跟隨容器生命週期所創建和移除的,數據無法直接被持久化存儲。
- 數據和容器緊密耦合,不能簡單的把數據移動到其他地方。
由於 docker 容器文件系統是基於 UnionFS,UnionFS 支持掛載不同類型的文件系統到統一的目錄結構中,所以只需要將宿主操作系統中的文件系統裏的文件或目錄掛載到容器中,便能夠讓容器內外共享這個文件。通過這種方式可以互通容器內外的文件,實現容器中文件數據持久化。
二、掛載方式
基於底層存儲實現,docker 提供了三種適用於不同場景的文件系統掛載方式:bind mounts、volume 和 tmpfs mounts。
- bind mounts 能夠直接將宿主操作系統中的目錄與文件掛載到容器內的文件系統中,通過指定容器外的路徑和容器內的路徑,就可以形成掛載映射關係,在容器內外對文件的讀寫,都是相互可見的。
- volume 也是從宿主操作系統中掛載目錄到容器內,只不過這個掛載的目錄由 docker 進行管理,只需要指定容器內的目錄,不需要關心具體掛載到了宿主操作系統中的哪裏。
- tmpfs mounts 支持掛載系統內存中的一部分到容器的文件系統裏,不過由於內存和容器的特徵,它的存儲並不是持久的,其中的內容會隨着容器的停止而消失。
無論選擇使用哪種類型的裝載,容器中的數據看起來都是相同的。它在容器的文件系統中以目錄或單個文件的形式展示。
三、bind mounts 掛載文件到容器
可以在容器創建的時候通過 -v
或 --volume
選項來指定內外掛載的對應目錄或文件。
# 強制刪除名稱爲 nginx 的容器
$ docker rm -f nginx
$ sudo docker run -d --name nginx -v /data/nginx/html:/usr/share/nginx/html nginx:1.17.6
使用 -v
或 --volume
來掛載宿主操作系統目錄的形式是 -v <host-path>:<container-path>
或 --volume <host-path>:<container-path>
,其中 host-path 和 container-path 分別代表宿主操作系統中的目錄和容器中的目錄。需要注意的是,docker 這裏強制定義目錄時必須使用絕對路徑,不能使用相對路徑。
能夠指定目錄進行掛載,也能夠指定具體的文件來掛載。
運行上面的命令後可以看到在宿主操作系統中的文件已經出現在容器中了 :
$ sudo docker exec nginx ls /usr/share/nginx/html
index.html
在 docker inspect
的結果裏,可以看到有關容器數據掛載相關的信息:
root@dreamboy:~# sudo docker inspect nginx
[
{
...
"Mounts": [
{
"Type": "bind",
"Source": "/data/nginx/html",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
....
}
]
在上面的返回中有一個 RW 字段,這表示掛載目錄或文件的讀寫性(Read and Write)。docker 還支持以只讀的方式掛載,通過只讀方式掛載的目錄和文件,只能被容器中的程序讀取,但不接受容器中程序修改它們的請求。在掛載選項 -v <host-path>:<container-path>
後再接上 :ro
就可以只讀掛載了。
# 強制刪除名稱爲 nginx 的容器
$ docker rm -f nginx
$ sudo docker run -d --name nginx -v /data/nginx/html:/usr/share/nginx/html:ro nginx:1.17.6
一些場景
在需要從宿主操作系統共享配置的時候。對於一些配置項,可以直接從容器外部掛載到容器中,這利於保證容器中的配置爲想象中的值,也方便對配置進行監控。例如,遇到容器中時區不正確的時候,可以直接將操作系統的時區配置,也就是 /etc/timezone
這個文件掛載並覆蓋容器中的時區配置。
在需要藉助 docker 進行開發的時候。雖然在 docker 中推崇直接將代碼和配置打包進鏡像,以便快速部署和快速重建。但這在開發過程中顯然非常不方便,因爲每次構建鏡像需要耗費一定的時間,這些時間積少成多,就是對開發工作效率的嚴重浪費了。如果直接把代碼掛載進入容器,那麼我們每次對代碼的修改都可以直接在容器外部進行。
四、tmpfs mount 掛載臨時文件目錄
tmpfs mount
是一種特殊的掛載方式,它主要利用內存來存儲數據。由於內存不是持久性存儲設備,所以其帶給 tmpfs mount
的特徵就是臨時性掛載。
與掛載宿主操作系統目錄或文件不同,掛載臨時文件目錄是通過 --tmpfs
這個選項來完成。由於內存的具體位置不需要指定,這個選項裏只需要傳遞掛載到容器內的目錄即可。
# 強制刪除名稱爲 nginx 的容器
$ docker rm -f nginx
$ sudo docker run -d --name nginx --tmpfs /usr/share/nginx/html nginx:1.17.6
容器已掛載的臨時文件目錄可以通過 docker inspect nginx
命令查看。
掛載臨時文件首先要注意它不是持久存儲這一特性,在此基礎上,它有幾種常見的適應場景。
- 應用中使用到,但不需要進行持久保存的敏感數據,可以藉助內存的非持久性和程序隔離性進行一定的安全保障。
- 讀寫速度要求較高,數據變化量大,但不需要持久保存的數據,可以藉助內存的高讀寫速度減少操作的時間。
五、volume 數據卷
除了與其他虛擬機工具近似的宿主操作系統目錄掛載的功能外,docker 還創造了數據卷(volume)這個概念。數據卷的本質其實依然是宿主操作系統上的一個目錄,只不過這個目錄存放在 docker 內部,接受 Docker 的管理。
在使用數據捲進行掛載時,不用需要知道數據具體存儲在了宿主操作系統的何處,只需要給定容器中的哪個目錄會被掛載即可。
任然使用 -v
或 --volume
選項來定義數據卷的掛載。
# 強制刪除名稱爲 nginx 的容器
$ docker rm -f nginx
$ sudo docker run -d --name nginx -v /usr/share/nginx/html nginx:1.17.6
可以通過 docker inspect nginx
命令查看容器中數據卷掛載信息。
docker inspect nginx
[
{
......
"Mounts": [
{
"Type": "volume",
"Name": "951b0a8f397fa278efdbcc8004ce5c664092d53776aae3bceb67ab1a7e57e240",
"Source": "/var/lib/docker/volumes/951b0a8f397fa278efdbcc8004ce5c664092d53776aae3bceb67ab1a7e57e240/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
......
}
]
這裏得到的信息與 bind mounts 有所區別除了 Type 中的類型不一樣之外,在數據卷掛載中,還要注意一下 Name 和 Source 這兩個信息。
其中 Source 是 docker 爲分配用於掛載的宿主機目錄,其位於 docker 的資源區域(這裏是默認的/var/lib/docker
)內。
爲了方便識別數據卷,可以像命名容器一樣爲數據卷命名,這裏的 Name 就是數據卷的命名。在未給出數據卷命名的時候,docker 會採用數據卷的 ID 命名數據卷。也可以通過 -v <name>:<container-path>
這種形式來命名數據卷。
# 強制刪除名稱爲 nginx 的容器
$ docker rm -f nginx
$ sudo docker run -d --name nginx -v nginx-data:/usr/share/nginx/html nginx:1.17.6
由於 -v
選項既有 bind mounts 的定義,又有 volume 的定義,所以其傳參方式需要特別留意。前面提到的 -v
在定義綁定掛載時必須使用絕對路徑,其目的主要是爲了避免與數據卷掛載中命名這種形式的衝突。
六、共用數據卷
數據卷的命名在 docker 中是唯一的,所以可以通過數據卷的名稱確定數據卷,這就可以讓多個容器掛載同一個數據卷:
# 強制刪除名稱爲 nginx 的容器
$ docker rm -f nginx
$ docker rm -f redis
$ sudo docker run -d --name nginx -v nginx-data:/usr/share/nginx/html nginx:1.17.6
$ sudo docker run -d --name redis -v nginx-data:/usr/share/redis/dump:ro redis:5.0.7
使用 -v
選項掛載數據卷時,如果數據卷不存在,docker 會爲自動創建和分配宿主操作系統的目錄,而如果同名數據卷已經存在,則會直接引用。
不依賴於容器獨立創建數據卷
通過 docker volume create 數據卷名稱
不依賴於容器獨立創建數據卷:
$ sudo docker volume create app-data
七、查看數據卷
使用 docker volume ls
列出當前已創建的數據卷 :
docker volume ls
DRIVER VOLUME NAME
local 8e7ec3c02015b49c1017a27a017c1d3294452551c4b3348e4bfa31dccb8dbdfd
local 17e62e49b3b6dde6eb21d348066079f8c1112f36f156a80bb0ef265c85512009
local app-data
local b7790efe2935b857f560ea4891f1f0f702c75794b7f50a57c41e898184433577
local b033618c531e77c400fcd407b10038e4cc03acd8291fcc914fd501d2ccf09a97
local nginx-data
八、刪除數據卷
雖然可以直接去對應目錄(默認是 /var/lib/docker/volumes/
)刪除數據卷,但是這樣做顯然不是好的選擇,應該通過 docker 對數據卷的管理命令來刪除它們。
可以直接通過 docker volume rm 數據卷名稱
來刪除指定的數據卷。
$ sudo docker volume rm appdata
$ sudo docker volume rm 8e7ec3c02015b49c1017a27a017c1d3294452551c4b3348e4bfa31dccb8dbdfd
在刪除數據卷之前,必須保證數據卷沒有被任何容器所使用(也就是之前引用過這個數據卷的容器都已經刪除),否則 docker 不會允許刪除這個數據卷。
注意這裏不能用部分標識來刪除,必須用全名來刪除,但是對於沒有直接命名的數據卷,因爲要反覆覈對數據卷 id,這樣的方式並不算特別友好。這種沒有命名的數據卷,通常可以看成它們與對應的容器產生了綁定,因爲其他容器很難使用到它。而這種綁定關係的產生,最方便的做法是在容器刪除時將它們一併刪除。
在 docker rm
刪除容器的命令中,可以增加 -v
選項來刪除容器關聯的數據卷:
$ sudo docker rm -v nginx
如果沒有隨容器刪除這些數據卷,docker 在創建新的容器時也不會啓用它們,只會純粹的佔用着硬盤空間而又不受管理。
此時可以通過 docker volume rm
來刪除它們,但是在一堆無規律的數據卷 id 中找到沒有被容器引用的數據卷是很耗時的,docker 提供 docker volume prune
命令,它可以刪除那些沒有被容器引用的數據卷:
$ sudo docker volume prune -f
Deleted Volumes:
4713e14c4c3f1daef219009af91486b3a097336f422a6f883905782d810a9672
b033618c531e77c400fcd407b10038e4cc03acd8291fcc914fd501d2ccf09a97
b7790efe2935b857f560ea4891f1f0f702c75794b7f50a57c41e898184433577
951b0a8f397fa278efdbcc8004ce5c664092d53776aae3bceb67ab1a7e57e240
295472b7205958214245fe33620d79c8ba6399ef572e091b5bc55c06fe9b195e
ec5e03def0ccaa54603896aceff6f4e5f60d14302bb9e0527ec555336d994b42
f3bf8993cadc0c95b6016636717722af86d23676174c3db881da352cd7457ae7
672e82323ac09b80a444410d037021cf6ff855954ae6c3fae97738b3fe5865c5
4805d7d7d61f5594acc6571df1324df38855cfed0b83b6f0c727e197f6aea245
8083ed34ffc0b61ef5e12adcb5e723595b77075f1b92ce128dbb9418f7fbdeab
e69acc6ebe3b5781d9f9b0f51a6da46a4dc16a3242bfb6800c442d40f9776b52
83aa6b5650a4383debdc832fb972ae4f745e43ae41f00cd98e82f5b372eb51ac
Total reclaimed space: 1.141GB
-f
選項表示不需要確認,不添加這個選項,需要輸入確認 y
才能刪除。
九、數據卷容器
在數據卷的基礎上,有一種相對新穎的用法,也就是數據卷容器。
數據卷容器,就是一個沒有具體指定的應用,甚至不需要運行的容器。
使用它的目的,是爲了定義一個或多個數據卷並持有它的引用。
創建方式 :
$ sudo docker create --name nginx-volume -v /data/nginx nginx:1.17.6
在使用數據卷容器時,不建議再定義數據卷的名稱,因爲可以通過對數據卷容器的引用來完成數據卷的引用。而不設置數據卷的名稱,也避免了在同一 docker 中數據卷重名的尷尬。
docker 的 network 是容器間的網絡橋樑,如果做類比,數據卷容器就可以算是容器間的文件系統橋樑。可以像加入網絡一樣引用數據卷容器,只需要在創建新容器時使 --volumes-from
選項即可。
# 刪除已有容器
$ sudo docker rm -f nginx
$ sudo docker run -d --name nginx --volumes-from nginx-volume nginx:1.17.6
引用數據卷容器時,不需要再定義數據卷掛載到容器中的位置,docker 會以數據卷容器中的掛載定義將數據卷掛載到引用的容器中。
雖然看上去數據卷容器與數據卷的使用方法變化不大,但最關鍵的就在於其真正隱藏了數據卷的配置和定義,只需要通過數據卷容器的名稱來使用它。這樣意味着我們能夠更輕鬆的實現容器的遷移。
十、備份和遷移數據卷
由於數據卷本身就是宿主操作系統中的一個目錄,只需要在 docker 資源目錄裏找到它就可以很輕鬆的打包、遷移、恢復了。在 docker 裏還有更優雅的解決方式。利用數據卷容器,我們還能夠更方便的對數據卷中的數據進行遷移。
數據備份、遷移、恢復的過程可以理解爲對數據進行打包,移動到其他位置,在需要的地方解壓的過程。
備份
在數據打包之前,先建立一個用來存放打包文件的目錄。要備份數據,先建立一個臨時的容器,將用於備份的目錄和要備份的數據卷都掛載到這個容器上。
$ sudo docker run --rm --volumes-from nginx-volume -v /data/nginx:/data/nginx nginx:1.17.6 tar cvf /data/nginx/nginx.tar /opt/
/opt/
tar: Removing leading `/' from member names
其中 --rm
選項可以讓容器在停止後自動刪除,而不需要再使用容器刪除命令來刪除它,這對於使用一些臨時容器很有幫助。
在鏡像定義之後接上命令 tar cvf /data/backup/backup.tar /opt/storage
,可以直接替換掉鏡像所定義的主程序啓動命令,而去執行這一條命令。
在備份後,就可以在 /data/nginx
下找到數據卷的備份文件 nginx.tar
。
恢復
如果要恢復數據卷中的數據,也可以藉助臨時容器完成。
$ docker run --rm --volumes-from nginx-volume -v /data/nginx:/data/nginx nginx:1.17.6 tar xvf /data/nginx/nginx.tar -C /opt
恢復的過程與備份的過程類似,只不過把打包的命令轉換爲解包的命令而已。
十一、mount 掛載
docker 除了使用 -v
來掛載之外,還可以使用 --mount
來掛載。
$ sudo docker run -d --name nginx --mount 'type=volume,src=nginx-volume,dst=/data/nginx,volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>' nginx:1.17.6
在 --mount
中,可以通過逗號分隔多個參數。其中,通過 type 可以定義掛載類型其值可以是:bind
,volume
或 tmpfs
。
另外,--mount
選項能夠幫助實現集羣掛載的定義,例如在這個例子中,我們掛載的來源是一個 NFS 目錄。