綁定掛載(bind mounts)在 Docker 的早期就已經出現了。與卷相比,綁定掛載的功能有限。當您使用綁定掛載時,主機上的文件或目錄將掛載到容器中。文件或目錄由其在主機上的完整或相對路徑引用。相反地,當您使用卷時,在主機上 Docker 的存儲目錄中創建一個新目錄,Docker 管理該目錄的內容。
該文件或目錄不需要已經存在於 Docker 主機上。如果還不存在,則按需創建。綁定掛載的性能非常好,但它們依賴於主機的文件系統,該文件系統具有特定的可用目錄結構。如果您正在開發新的 Docker 應用程序,請考慮改用命名卷。不能使用 Docker CLI 命令直接管理綁定掛載。

選擇 -v 或者 --mount 標記
最初,-v 或 --volume 標記用於獨立容器,--mount 標記用於集群服務。但是,從 Docker 17.06 開始,您也可以將 --mount 用於獨立容器。通常,--mount 標記表達更加明確和冗長。最大的區別是 -v 語法將所有選項組合在一個字段中,而 --mount 語法將選項分離。下面是每個標記的語法比較。
提示:新用戶推薦使用
--mount語法,有經驗的用戶可能更熟悉-vor--volume語法,但是更鼓勵使用--mount語法,因為研究表明它更易於使用。
-v或--volume: 由三個字段組成,以冒號(:)分隔。字段必須按照正確的順序排列,且每個字段的含義不夠直觀明顯。- 對於綁定掛載(bind mounts), 第一個字段是主機上文件或目錄的路徑。
- 第二個字段是容器中文件或目錄掛載的路徑。
- 第三個字段是可選的,是一個逗號分隔的選項列表,比如
ro、consistent、delegated、cached、z和Z。這些選項會在本文下面討論。
--mount:由多個鍵-值對組成,以逗號分隔,每個鍵-值對由一個<key>=<value>元組組成。--mount語法比-v或--volume更冗長,但是鍵的順序並不重要,標記的值也更容易理解。- 掛載的類型(
type),可以是bind、volume或者tmpfs。本主題討論綁定掛載(bind mounts),因此類型(type)始終為綁定掛載(bind)。 - 掛載的源(
source),對於綁定掛載,這是 Docker 守護進程主機上的文件或目錄的路徑。可以用source或者src來指定。 - 目標(
destination),將容器中文件或目錄掛載的路徑作為其值。可以用destination、dst或者target來指定。 readonly選項(如果存在),則會將綁定掛載以只讀形式掛載到容器中。bind-propagation選項(如果存在),則更改綁定傳播。 可能的值是rprivate、private、rshared、shared、rslave或slave之一。consistency選項(如果存在), 可能的值是consistent、delegated或cached之一。 這個設置只適用於 Docker Desktop for Mac,在其他平台上被忽略。--mount標記不支持用於修改 selinux 標簽的z或Z選項。
- 掛載的類型(
下面的示例盡可能同時展示 --mount 和 -v 兩種語法,並且先展示 --mount。
-v 和 --mount 行為之間的差異
由於 -v 和 -volume 標記長期以來一直是 Docker 的一部分,它們的行為無法改變。這意味着 -v 和 -mount 之間有一個不同的行為。
如果您使用 -v 或 -volume 來綁定掛載 Docker 主機上還不存在的文件或目錄,則 -v 將為您創建它。它總是作為目錄創建的。
如果使用 --mount 綁定掛載 Docker 主機上還不存在的文件或目錄,Docker 不會自動為您創建它,而是產生一個錯誤。
啟動帶有綁定掛載的容器
考慮這樣一個情況:您有一個目錄 source,當您構建源代碼時,工件被保存到另一個目錄 source/target/ 中。您希望工件在容器的 /app/ 目錄可用,並希望每次在開發主機上構建源代碼時,容器能訪問新的構建。使用以下命令將 target/ 目錄綁定掛載到容器的 /app/。在 source 目錄中運行命令。在 Linux 或 macOS 主機上,$(pwd) 子命令擴展到當前工作目錄。
下面的 --mount 和 -v 示例會產生相同的結果。除非在運行第一個示例之后刪除了 devtest 容器,否則不能同時運行它們。
--mount:
$ docker run -d \
-it \
--name devtest \
--mount type=bind,source="$(pwd)"/target,target=/app \
nginx:latest
-v:
$ docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app \
nginx:latest
使用 docker inspect devtest 驗證綁定掛載是否被正確創建。查看 Mounts 部分:
"Mounts": [
{
"Type": "bind",
"Source": "/tmp/source/target",
"Destination": "/app",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
這表明掛載是一個 bind 掛載,它顯示了正確的源和目標,也顯示了掛載是可讀寫的,並且傳播設置為 rprivate。
停止容器:
$ docker container stop devtest
$ docker container rm devtest
掛載到容器上的非空目錄
如果您將其綁定掛載到容器上的一個非空目錄中,則該目錄的現有內容會被綁定掛載覆蓋。這可能是有益的,例如當您想測試應用程序的新版本而不構建新鏡像時。然而,它也可能是令人驚訝的,這種行為不同於 docker volumes。
這個例子被設計成極端的,僅僅使用主機上的 /tmp/ 目錄替換容器的 /usr/ 目錄的內容。在大多數情況下,這將導致容器無法正常工作。
--mount 和 -v 示例有相同的結果。
--mount:
$ docker run -d \
-it \
--name broken-container \
--mount type=bind,source=/tmp,target=/usr \
nginx:latest
docker: Error response from daemon: oci runtime error: container_linux.go:262:
starting container process caused "exec: \"nginx\": executable file not found in $PATH".
-v:
$ docker run -d \
-it \
--name broken-container \
-v /tmp:/usr \
nginx:latest
docker: Error response from daemon: oci runtime error: container_linux.go:262:
starting container process caused "exec: \"nginx\": executable file not found in $PATH".
容器被創建,但沒有啟動。刪除它:
$ docker container rm broken-container
使用只讀綁定掛載
對於一些開發應用程序,容器需要寫入綁定掛載,因此更改將傳播回 Docker 主機。在其他時候,容器只需要讀訪問。
這個示例修改了上面的示例,但是通過在容器內的掛載點之后的選項列表(默認為空)中添加 ro,將目錄掛載為只讀綁定掛載。當有多個選項時,使用逗號分隔它們。
--mount 和 -v 示例有相同的結果。
--mount:
$ docker run -d \
-it \
--name devtest \
--mount type=bind,source="$(pwd)"/target,target=/app,readonly \
nginx:latest
-v:
$ docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app:ro \
nginx:latest
使用 docker inspect devtest 驗證綁定掛載是否被正確創建。查看 Mounts 部分:
"Mounts": [
{
"Type": "bind",
"Source": "/tmp/source/target",
"Destination": "/app",
"Mode": "ro",
"RW": false,
"Propagation": "rprivate"
}
],
停止容器:
$ docker container stop devtest
$ docker container rm devtest
配置綁定傳播
對於綁定掛載和卷,綁定傳播默認都是 rprivate 。只能為綁定掛載配置,而且只能在 Linux 主機上配置。綁定傳播是一個高級主題,許多用戶從不需要配置它。
綁定傳播是指在給定綁定掛載或命名卷中創建的掛載是否可以傳播到該掛載的副本。考慮一個掛載點 /mnt,它也掛載在 /tmp 上。傳播設置控制 /tmp/a 上的掛載是否也可以在 /mnt/a 上使用。每個傳播設置都有一個遞歸對應點。在遞歸的情況下,考慮一下 /tmp/a 也被掛載為 /foo。傳播設置控制 /mnt/a 和/或 /tmp/a 是否存在。
| 傳播設置 | 描述 |
|---|---|
| shared | 原始掛載的子掛載公開給副本掛載,副本掛載的子掛載也傳播給原始掛載。 |
| slave | 類似於共享(shared)掛載,但僅在一個方向上。如果原始掛載公開子掛載,副本掛載可以看到它。但是,如果副本掛載公開子掛載,則原始掛載無法看到它。 |
| private | 該掛載是私有的。原始掛載的子掛載不公開給副本掛載,副本掛載的子掛載也不公開給原始掛載。 |
| rshared | 與 shared 相同,但傳播也擴展到嵌套在任何原始或副本掛載點中的掛載點。 |
| rslave | 與 slave 相同,但傳播也擴展到嵌套在任何原始或副本掛載點中的掛載點。 |
| rprivate | 默認值。與 private 相同,這意味着原始或副本掛載點中的任何位置的掛載點都不會在任何方向傳播。 |
當你在掛載點上設置綁定傳播之前,主機文件系統需要已經支持綁定傳播。
有關綁定傳播的更多信息,請參見 Linux 內核共享子樹文檔。
下面的示例兩次將 target/ 目錄掛載到容器中,第二次掛載設置了 ro 選項和 rslave 綁定傳播選項。
--mount 和 -v 示例有相同的結果。
--mount:
$ docker run -d \
-it \
--name devtest \
--mount type=bind,source="$(pwd)"/target,target=/app \
--mount type=bind,source="$(pwd)"/target,target=/app2,readonly,bind-propagation=rslave \
nginx:latest
-v:
$ docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app \
-v "$(pwd)"/target:/app2:ro,rslave \
nginx:latest
現在,如果您創建 /app/foo/, /app2/foo/ 也存在。
配置 selinux 標簽
如果使用 selinux ,則可以添加 z 或 Z 選項,以修改掛載到容器中的主機文件或目錄的 selinux 標簽。這會影響主機上的文件或目錄,並且會產生超出 Docker 范圍之外的后果。
z選項表示綁定掛載內容在多個容器之間共享。Z選項表示綁定掛載內容是私有的、非共享的。
使用這些選項時要格外小心。使用 Z 選項綁定掛載系統目錄(如 /home 或 /usr )會導致您的主機無法操作,您可能需要重新手動標記主機文件。
重要提示:當對服務使用綁定掛載時,selinux 標簽(
:Z和:Z) 以及:ro將被忽略。詳情請參閱 moby/moby #32579。
這個示例設置了 z 選項來指定多個容器可以共享綁定掛載的內容:
無法使用 --mount 標記修改 selinux 標簽。
$ docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app:z \
nginx:latest
為 macOS 配置掛載一致性
Docker Desktop for Mac 使用 osxfs 將從 macOS 共享的目錄和文件傳播到 Linux VM。這種傳播使運行在 Docker Desktop for Mac 上的 Docker 容器可以使用這些目錄和文件。
默認情況下,這些共享是完全一致的,這意味着每次在 macOS 主機上或通過容器中的掛載發生寫操作時,更改都會刷新到磁盤上,以便共享中的所有參與者都擁有完全一致的視圖。在某些情況下,完全一致性會嚴重影響性能。Docker 17.05及更高版本引入了一些選項,在每個掛載、每個容器的基礎上調整一致性設置。以下選項可供選擇:
consistent或default: 完全一致性的默認設置,如上所述。delegated: 容器運行時的掛載視圖是權威的。在容器中所做的更新,在主機上可見之前,可能會有延遲。cached: macOS 主機的掛載視圖是權威的。在主機上所做的更新,在容器中可見之前,可能會有延遲。
這些選項在除 macOS 之外的所有主機操作系統上都被完全忽略。
--mount 和 -v 示例有相同的結果。
--mount:
$ docker run -d \
-it \
--name devtest \
--mount type=bind,source="$(pwd)"/target,destination=/app,consistency=cached \
nginx:latest
-v:
$ docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app:cached \
nginx:latest
