繫結掛載

繫結掛載自 Docker 早期就已存在。與磁碟區相比,繫結掛載的功能有限。當您使用繫結掛載時,主機上的檔案或目錄會掛載到容器中。檔案或目錄由其在主機上的絕對路徑參考。相反地,當您使用磁碟區時,會在主機上 Docker 的儲存目錄中建立新的目錄,Docker 會管理該目錄的內容。

檔案或目錄不需要事先存在於 Docker 主機上。如果它尚不存在,則會依需求建立。繫結掛載的效能非常好,但它們依賴主機檔案系統具有可用的特定目錄結構。如果您正在開發新的 Docker 應用程式,請考慮改用具名磁碟區。您無法使用 Docker CLI 命令直接管理繫結掛載。

Bind mounts on the Docker host

提示

正在使用大型儲存庫或單體儲存庫,或是使用不再與您的程式碼庫一起調整的虛擬檔案系統?查看同步檔案共用。它透過使用同步檔案系統快取來增強繫結掛載效能,提供快速且彈性的主機到 VM 檔案共用。

選擇 -v 或 --mount 旗標

一般來說,--mount 更明確且詳細。最大的差異在於 -v 語法將所有選項組合在一個欄位中,而 --mount 語法則將它們分開。以下是每個旗標的語法比較。

提示

新使用者應該使用 --mount 語法。經驗豐富的使用者可能更熟悉 -v--volume 語法,但建議使用 --mount,因為研究顯示它更容易使用。

  • -v--volume:由三個欄位組成,以冒號 (:) 分隔。欄位必須按照正確的順序排列,而且每個欄位的含義並不明顯。

    • 在繫結掛載的情況下,第一個欄位是**主機**上檔案或目錄的路徑。
    • 第二個欄位是檔案或目錄在容器中掛載的路徑。
    • 第三個欄位是可選的,它是一個以逗號分隔的選項列表,例如 rozZ。這些選項將在下方討論。
  • --mount:由多個鍵值對組成,以逗號分隔,每個鍵值對都包含一個 <key>=<value> 元組。 --mount 語法比 -v--volume 更冗長,但鍵的順序並不重要,而且旗標的值更容易理解。

    • 掛載的 type(類型),可以是 bindvolumetmpfs。本主題討論的是繫結掛載,因此類型始終為 bind
    • 掛載的 source(來源)。對於繫結掛載,這是 Docker Daemon 主機上檔案或目錄的路徑。可以指定為 sourcesrc
    • destination(目標)的值是檔案或目錄在容器中掛載的路徑。可以指定為 destinationdsttarget
    • 如果存在 readonly(唯讀)選項,則會導致繫結掛載以唯讀方式掛載到容器中
    • 如果存在 bind-propagation(繫結傳播)選項,則會更改繫結傳播。可以是 rprivateprivatersharedsharedrslaveslave 之一。
    • --mount 旗標不支援用於修改 selinux 標籤的 zZ 選項。

以下範例同時顯示了 --mount-v 語法(如果可能),並優先顯示 --mount

-v--mount 行為之間的差異

由於 -v--volume 旗標長期以來一直是 Docker 的一部分,因此無法更改其行為。這表示 -v--mount 之間存在一種不同的行為。

如果您使用 -v--volume 來繫結掛載 Docker 主機上尚不存在的檔案或目錄,-v 會為您建立端點。它始終會被建立為目錄。

如果您使用 --mount 來繫結掛載 Docker 主機上尚不存在的檔案或目錄,Docker 不會自動為您建立它,而是會產生錯誤。

使用繫結掛載啟動容器

假設您有一個目錄 source,並且在建置原始碼時,構件會儲存到另一個目錄 source/target/ 中。您希望構件在 /app/ 中可供容器使用,並且您希望容器在每次在開發主機上建置原始碼時都能存取新的建置。使用以下命令將 target/ 目錄繫結掛載到容器中的 /app/。從 source 目錄中執行該命令。 $(pwd) 子命令會展開為 Linux 或 macOS 主機上的目前工作目錄。如果您使用的是 Windows,另請參閱Windows 上的路徑轉換

以下 --mount-v 範例產生相同的結果。除非您在執行第一個範例後移除 devtest 容器,否則您無法同時執行它們。


$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest
$ 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 磁碟區的行為不同。

這個範例被設計得很極端,它會將容器的 /usr/ 目錄的內容替換為主機上的 /tmp/ 目錄。在大多數情況下,這會導致容器無法正常運作。

--mount-v 範例具有相同的最終結果。


$ 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".
$ 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 範例具有相同的結果。


$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app,readonly \
  nginx:latest
$ 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

遞迴掛載

當您繫結掛載本身包含掛載的路徑時,預設情況下,這些子掛載也會包含在繫結掛載中。可以使用 --mountbind-recursive 選項來設定此行為。此選項僅適用於 --mount 旗標,不適用於 -v--volume

如果繫結掛載是唯讀的,Docker 引擎會盡最大努力使子掛載也成為唯讀的。這稱為遞迴唯讀掛載。遞迴唯讀掛載需要 Linux 核心版本 5.12 或更高版本。如果您執行的是較舊的核心版本,則預設情況下,子掛載會自動以讀寫方式掛載。嘗試在 5.12 之前的核心版本上使用 bind-recursive=readonly 選項將子掛載設定為唯讀,將會導致錯誤。

bind-recursive 選項支援的值為

說明
enabled(啟用)(預設)如果核心版本為 v5.12 或更高版本,則唯讀掛載會遞迴地設為唯讀。否則,子掛載為讀寫。
disabled(停用)子掛載會被忽略(不包含在繫結掛載中)。
writable(可寫入)子掛載為讀寫。
readonly(唯讀)子掛載為唯讀。需要核心版本 v5.12 或更高版本。

設定繫結傳播

繫結傳播預設為繫結掛載和磁碟區的 rprivate。它僅可設定繫結掛載,並且僅限於 Linux 主機。繫結傳播是一個進階主題,許多使用者永遠不需要設定它。

繫結傳播是指在給定繫結掛載中建立的掛載是否可以傳播到該掛載的副本。考慮一個掛載點 /mnt,它也掛載在 /tmp 上。傳播設定控制 /tmp/a 上的掛載是否也可用於 /mnt/a。每個傳播設定都有一個遞迴對應項。在遞迴的情況下,考慮 /tmp/a 也掛載為 /foo。傳播設定控制 /mnt/a 和/或 /tmp/a 是否存在。

警告

掛載傳播不適用於 Docker Desktop。

傳播設定說明
shared(共享)原始掛載的子掛載會公開給副本掛載,副本掛載的子掛載也會傳播到原始掛載。
slave(從屬)類似於共享掛載,但僅限單向。如果原始掛載公開子掛載,副本掛載可以看到它。但是,如果副本掛載公開子掛載,原始掛載則看不到它。
private(私有)掛載是私有的。其中的子掛載不會公開給副本掛載,副本掛載的子掛載也不會公開給原始掛載。
rshared(遞迴共享)與 shared 相同,但傳播也會延伸到任何原始或副本掛載點中巢狀的掛載點,以及從這些掛載點延伸。
rslave(遞迴從屬)與 slave 相同,但傳播也會延伸到任何原始或副本掛載點中巢狀的掛載點,以及從這些掛載點延伸。
rprivate(遞迴私有)預設值。與 private 相同,表示原始或副本掛載點中任何位置的掛載點都不會朝任一方向傳播。

您必須先讓主機檔案系統支援繫結傳播,才能在掛載點上設定繫結傳播。

有關繫結傳播的更多資訊,請參閱共享子樹的 Linux 核心文件

以下範例將 target/ 目錄兩次掛載到容器中,第二次掛載同時設定了 ro 選項和 rslave 繫結傳播選項。

--mount-v 範例具有相同的結果。


$ 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
$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app \
  -v "$(pwd)"/target:/app2:ro,rslave \
  nginx:latest

現在,如果您建立 /app/foo/,則 /app2/foo/ 也會存在。

設定 selinux 標籤

如果您使用 selinux,您可以新增 zZ 選項來修改要掛載到容器中的主機檔案或目錄的 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

搭配 Compose 使用繫結掛載

具有繫結掛載的單個 Docker Compose 服務如下所示。

services:
  frontend:
    image: node:lts
    volumes:
      - type: bind
        source: ./static
        target: /opt/app/static
volumes:
  myapp:

有關在 Compose 中使用 bind 類型磁碟區的更多資訊,請參閱 Compose 磁碟區參考Compose 磁碟區設定參考

後續步驟