1. 程式人生 > >深入理解 Docker Volume

深入理解 Docker Volume

本文主要介紹了Docker Volume的原理以及使用方式,是Docker入門教程的延伸。作者通過從資料的共享、資料容器、備份、許可權以及刪除Volume五方面深入介紹了Volume的工作原理,從實戰中幫助讀者瞭解Volume。

從Docker IRC頻道以及 stackoverflow 的問題來看,很多人還不是很明白Docker Volume的工作原理。在這篇文章中,我會盡最大的努力來解釋Volume是如何工作的,並展示一些最佳實踐。這篇文章主要是針對那些對Volume不瞭解的Docker使用者,當然有經驗的使用者也可以通過本文了解一些Volume的細節。

如果想要了解Docker Volume,首先我們需要知道Docker的檔案系統是如何工作的。Docker映象是由多個檔案系統(只讀層)疊加而成。當我們啟動一個容器的時候,Docker會載入只讀映象層並在其上(譯者注:映象棧頂部)新增一個讀寫層。如果執行中的容器修改了現有的一個已經存在的檔案,那該檔案將會從讀寫層下面的只讀層複製到讀寫層,該檔案的只讀版本仍然存在,只是已經被讀寫層中該檔案的副本所隱藏。當刪除Docker容器,並重新啟動通過該映象啟動時,之前的更改將會丟失。在Docker中,只讀層及在頂部的讀寫層的組合被稱為 Union File System (聯合檔案系統)。

為了能夠儲存(持久化)資料以及共享容器間的資料,Docker提出了Volume的概念。簡單來說,Volume就是目錄或者檔案,它可以繞過預設的聯合檔案系統,而以正常的檔案或者目錄的形式存在於宿主機上。

我們可以通過兩種方式來初始化Volume,這兩種方式有些細小而又重要的差別。我們可以在執行時使用 -v 來宣告Volume:

$ docker run -it --name container-test -h CONTAINER -v /data debian /bin/bash

[email protected]
:/# ls /data

[email protected]:/#

上面的命令會將 /data 掛載到容器中,並繞過聯合檔案系統,我們可以在主機上直接操作該目錄。任何在該映象 /data 路徑的檔案會將被複制到Volume。我們可以使用 docker inspect 命令找到Volume在主機上的儲存位置:

$ docker inspect -f {{.Volumes}} container-test

你會看到類似的輸出:

map[/data:/var/lib/docker/vfs/dir/cde167197ccc3e138a14f1a4f...b32cec92e79059437a9]

這說明Docker把在 /var/lib/docker 下的某個目錄掛載到了容器內的 /data 目錄下。讓我們從主機上新增檔案到此資料夾下:

$ sudo touch /var/lib/docker/vfs/dir/cde167197ccc3e13814f...b32ce9059437a9/test-file

進入我們的容器內可以看到:

$
[email protected]
:/# ls /data

test-file

只要將主機的目錄掛載到容器的目錄上,那改變就會立即生效。我們可以在Dockerfile中通過使用 VOLUME 指令來達到相同的目的:

FROM debian:wheezy

VOLUME /data

但還有另一件只有 -v 引數能做到而Dockerfile是做不到的事是在容器上掛載指定的主機目錄。例如:

$ docker run -v /home/adrian/data:/data debian ls /data

該命令將掛載主機的 /home/adrian/data 目錄到容器內的 /data 目錄上。任何在 /home/adrian/data 目錄的檔案都將會出現在容器內。這對於在主機和容器之間共享檔案是非常有幫助的,例如掛載需要編譯的原始碼。為了保證可移植性(並不是所有的系統的主機目錄都是可以用的),掛載主機目錄不需要從Dockerfile指定。當使用 -v 引數時,映象目錄下的任何檔案都不會被複制到Volume中。(譯者注:Volume會複製到映象目錄,映象不會複製到卷)
資料共享

如果要授權一個容器訪問另一個容器的Volume,我們可以使用 -volumes-from 引數來執行 docker run 。

$ docker run -it -h NEWCONTAINER --volumes-from container-test debian /bin/bash

[email protected]
:/# ls /data

test-file

[email protected]:/#

值得注意的是不管container-test是否執行,它都會起作用。只要有容器連線Volume,它就不會被刪除。
資料容器

常見的使用場景是使用純資料容器來持久化資料庫、配置檔案或者資料檔案等。 官方的文件 上有詳細的解釋。例如:

$ docker run --name dbdata postgres echo "Data-only container for postgres"

該命令將會建立一個包含已經在Dockerfile裡定義過Volume的postgres映象,執行 echo 命令然後退出。當我們執行 docker ps 命令時, echo 可以幫助我們識別某映象的用途。我們可以用 -volumes-from 命令來其它容器的Volume:

$ docker run -d --volumes-from dbdata --name db1 postgres

使用資料容器的兩個注意點:

    不要執行資料容器,這純粹是在浪費資源。
    不要為了資料容器而使用“最小的映象”,如 busybox 或 scratch ,只使用資料庫映象本身就可以了。你已經擁有該映象,所以並不需要佔用額外的空間。

備份

如果你在用資料容器,那做備份相當容易:

$ docker run --rm --volumes-from dbdata -v $(pwd):/backup debian tar cvf /backup/backup.tar /var/lib/postgresql/data

該示例應該會將Volume裡所有的東西壓縮為一個tar包(官方的postgres Dockerfile在/var/lib/postgresql/data目錄下定義了一個Volume)
許可權與許可

通常你需要設定Volume的許可權或者為Volume初始化一些預設資料或者配置檔案。要注意的關鍵點是,在Dockerfile的 VOLUME 指令後的任何東西都不能改變該Volume,比如:

FROM debian:wheezy

RUN useradd foo

VOLUME /data

RUN touch /data/x

RUN chown -R foo:foo /data

該Docker file如預期那樣執行,我們本來希望 touch 命令在映象的檔案系統上執行,但是實際上它是在一個臨時容器的Volume上執行。如下所示:

FROM debian:wheezy

RUN useradd foo

RUN mkdir /data && touch /data/x

RUN chown -R foo:foo /data

VOLUME /data

Docker可以將映象中Volume下的檔案掛載到Volume下,並設定正確的許可權。如果你指定Volume的主機目錄將不會出現這種情況。

如果你沒有通過 RUN 指令設定許可權,那麼你就需要在容器啟動時使用 CMD 或 ENTRYPOINT 指令來執行(譯者注:CMD指令用於指定一個容器啟動時要執行的命令,與RUN類似,只是RUN是映象在構建時要執行的命令)。
刪除Volumes

這個功能可能會更加重要,如果你已經使用 docker rm 來刪除你的容器,那可能有很多的孤立的Volume仍在佔用著空間。

Volume只有在下列情況下才能被刪除:

    該容器可以用 docker rm -v 來刪除且沒有其它容器連線到該Volume(以及主機目錄是也沒被指定為Volume)。注意, -v 是必不可少的。
    docker run 中使用 rm 引數

除非你已經很小心的,總是像這樣來執行容器,否則你將會在 /var/lib/docker/vfs/dir 目錄下得到一些殭屍檔案和目錄,並且還不容易說出它們到底代表什麼。