卷(volumes)是 Docker 容器生產和使用持久化數據的首選機制。綁定掛載(bind mounts)依賴於主機的目錄結構,卷(volumes)完全由 Docker 管理。卷與綁定掛載相比有幾個優勢:
- 卷比綁定掛載更容易備份或遷移。
- 您可以使用 Docker CLI 命令或 Docker API 來管理卷。
- 卷可以在 Linux 和 Windows 容器上工作。
- 卷可以更安全地在多個容器之間共享。
- 卷驅動程序允許您在遠程主機或雲提供商上存儲卷、加密卷的內容或添加其他功能。
- 新卷的內容可以由容器預先填充。(New volumes can have their content pre-populated by a container.)
此外,與將數據持久化到容器的可寫層相比,卷通常是更好的選擇,因為卷不會增加使用它的容器的大小,而且卷的內容存在於給定容器的生命周期之外。

如果容器生成非持久性狀態數據,請考慮使用 tmpfs 掛載(tmpfs mount)以避免將數據永久存儲在任何位置,並通過避免寫入容器的可寫層來提高容器的性能。
卷使用 rprivate 綁定傳播,並且綁定傳播對於卷是不可配置的。
選擇 -v 或 --mount 標記
最初,-v 或 --volume 標記用於獨立容器,--mount 標記用於集群服務。但是,從 Docker 17.06 開始,您也可以將 --mount 用於獨立容器。通常,--mount 標記表達更加明確和冗長。最大的區別是 -v 語法將所有選項組合在一個字段中,而 --mount 語法將選項分離。下面是每個標記的語法比較。
新用戶推薦使用
--mount語法,它比--volume語法更簡單。
如果需要指定卷驅動程序選項,則必須使用 --mount。
-v或--volume: 由三個字段組成,以冒號(:)分隔。字段必須按照正確的順序排列,且每個字段的含義不夠直觀明顯。- 對於命名卷,第一個字段是卷的名稱,在給定的主機上是惟一的。對於匿名卷,省略第一個字段。
- 第二個字段是容器中文件或目錄掛載的路徑。
- 第三個字段是可選的,是一個逗號分隔的選項列表,比如
ro。這些選項會在本文下面討論。
--mount:由多個鍵-值對組成,以逗號分隔,每個鍵-值對由一個<key>=<value>元組組成。--mount語法比-v或--volume更冗長,但是鍵的順序並不重要,標記的值也更容易理解。- 掛載的類型(
type),可以是bind、volume或者tmpfs。本主題討論卷(volume),因此類型(type)始終為卷(volume)。 - 掛載的源(
source),對於命名卷,這是卷的名稱。對於匿名卷,此字段被省略。可以用source或者src來指定。 - 目標(
destination),將容器中文件或目錄掛載的路徑作為其值。可以用destination、dst或者target來指定。 readonly選項(如果存在),則會將綁定掛載以只讀形式掛載到容器中。volume-opt選項,可以被指定多次,接受由選項名及其值組成的鍵-值對。
- 掛載的類型(
從外部 CSV 解析器轉義值
如果卷驅動程序接受以逗號分隔的列表作為選項,則必須從外部 CSV 解析器轉義該值。要轉義
volume-opt, 請使用雙引號(")將其括起來,並使用單引號(')將整個掛載參數括起來。例如,本地(
local)驅動程序在參數o中接受以逗號分隔的列表作為掛載選項。下面這個例子展示了轉義列表的正確寫法。$ docker service create \ --mount 'type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>,volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>,"volume-opt=o=addr=<nfs-address>,vers=4,soft,timeo=180,bg,tcp,rw"' --name myservice \ <IMAGE>
下面的示例盡可能同時展示 --mount 和 -v 兩種語法,並且先展示 --mount。
-v 和 --mount 行為之間的差異
與綁定掛載不同,卷的所有選項對於 --mount 和 -v 標記都可用。
當卷與服務一起使用時,只有 --mount 支持。
創建和管理卷
與綁定掛載不同,您可以在任何容器的作用域之外創建和管理卷。
創建一個卷:
$ docker volume create my-vol
卷列表:
$ docker volume ls
# 輸出結果:
DRIVER VOLUME NAME
local my-vol
檢查卷:
$ docker volume inspect my-vol
# 輸出結果:
[
{
"CreatedAt": "2020-07-04T07:06:47Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
刪除卷:
$ docker volume rm my-vol
啟動一個帶有卷的容器
如果您啟動一個有尚不存在的卷的容器,Docker 將為您創建這個卷。下面的示例將卷 myvol2 掛載到容器中的 /app/ 中。
下面的 --mount 和 -v 示例會產生相同的結果。除非在運行第一個示例之后刪除了 devtest 容器和 myvol2 卷,否則不能同時運行它們。
--mount:
$ docker run -d \
--name devtest \
--mount source=myvol2,target=/app \
nginx:latest
-v:
$ docker run -d \
--name devtest \
-v myvol2:/app \
nginx:latest
使用 docker inspect devtest 驗證卷的創建和掛載是否正確。查看 Mounts 部分:
"Mounts": [
{
"Type": "volume",
"Name": "myvol2",
"Source": "/var/lib/docker/volumes/myvol2/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
這表明掛載是一個卷,它顯示了正確的源和目標,並且掛載是可讀寫的。
停止容器並刪除卷。注意刪除卷是一個單獨的步驟。
$ docker container stop devtest
$ docker container rm devtest
$ docker volume rm myvol2
啟動帶有卷的服務
啟動服務並定義卷時,每個服務容器都使用自己的本地卷。 如果使用本地(local)卷驅動程序,則沒有任何容器可以共享此數據,但某些卷驅動程序確實支持共享存儲。Docker for AWS 和 Docker for Azure 都支持使用 Cloudstor 插件的持久存儲。
下面的示例使用四個副本啟動 nginx 服務,每個副本使用一個名為 myvol2 的本地卷。
$ docker service create -d \
--replicas=4 \
--name devtest-service \
--mount source=myvol2,target=/app \
nginx:latest
使用 docker service ps devtest-service 驗證服務是否正在運行:
$ docker service ps devtest-service
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
4d7oz1j85wwn devtest-service.1 nginx:latest moby Running Running 14 seconds ago
刪除服務,該服務將停止其所有任務:
$ docker service rm devtest-service
刪除服務不會刪除該服務創建的任何卷。刪除卷是一個單獨的步驟。
服務的語法差異
docker service create 命令不支持 -v 或 --volume 標記,在將卷掛載到服務的容器中時,必須使用 --mount 標記。
使用容器填充卷
如果您啟動了一個創建新卷的容器,如上所述,並且該容器在要掛載的目錄(例如上面的 /app/)中有文件或目錄,那么該目錄的內容將復制到新卷中。然后容器掛載並使用該卷,使用該卷的其他容器也可以訪問預填充的內容。
為了說明這一點,這個例子啟動了一個 nginx 容器,並用容器的 /usr/share/nginx/html 目錄中的內容填充新的卷 nginx-vol,這個目錄是 Nginx 存儲默認的 HTML 內容的地方。
下面的 --mount 和 -v 示例具有相同的最終結果。
--mount:
$ docker run -d \
--name=nginxtest \
--mount source=nginx-vol,destination=/usr/share/nginx/html \
nginx:latest
-v:
$ docker run -d \
--name=nginxtest \
-v nginx-vol:/usr/share/nginx/html \
nginx:latest
運行兩個示例中的任何一個之后,運行以下命令來清理容器和卷。注意:刪除卷是一個單獨的步驟。
$ docker container stop nginxtest
$ docker container rm nginxtest
$ docker volume rm nginx-vol
使用只讀卷
對於某些開發應用程序,容器需要寫入綁定掛載,以便更改傳播回 Docker 主機。在其他時候,容器只需要對數據進行讀訪問。記住,多個容器可以掛載相同的卷,並且可以對其中一些容器以讀寫方式掛載,而對其他容器以只讀方式掛載。
這個示例修改了上面的示例,但是通過在容器內的掛載點之后的選項列表(默認為空)中添加 ro,將目錄掛載為只讀卷。當有多個選項時,使用逗號分隔它們。
下面 --mount 和 -v 示例有相同的結果。
--mount:
$ docker run -d \
--name=nginxtest \
--mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \
nginx:latest
-v:
$ docker run -d \
--name=nginxtest \
-v nginx-vol:/usr/share/nginx/html:ro \
nginx:latest
使用 docker inspect nginxtest 驗證是否正確創建了只讀掛載。查看 Mounts 部分:
"Mounts": [
{
"Type": "volume",
"Name": "nginx-vol",
"Source": "/var/lib/docker/volumes/nginx-vol/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "",
"RW": false,
"Propagation": ""
}
],
停止並刪除容器,再刪除卷。刪除卷是一個單獨的步驟。
$ docker container stop nginxtest
$ docker container rm nginxtest
$ docker volume rm nginx-vol
在機器之間共享數據
在構建故障容錯的應用程序時,您可能需要配置同一服務的多個副本,以訪問相同的文件。

在開發應用程序時,有幾種方法可以實現這一點。一種方法是向您的應用程序添加邏輯,在雲對象存儲系統(如 Amazon S3)上存儲文件。另一個方法是使用支持將文件寫入外部存儲系統(如 NFS 或 Amazon S3)的驅動程序來創建卷。
卷驅動程序使您可以從應用程序邏輯中抽象底層存儲系統。例如,如果您的服務使用帶有 NFS 驅動程序的卷,那么您可以更新服務以使用其他的驅動程序(例如,將數據存儲在雲上),而無需更改應用程序邏輯。
使用卷驅動程序
當您使用 docker volume create 創建卷時,或者當您啟動使用尚未創建的卷的容器時,可以指定一個卷驅動程序。下面的示例使用 vieux/sshfs 卷驅動程序,首先在創建獨立卷時使用,然后在啟動創建新卷的容器時使用。
初始設置
這個示例假定您有兩個節點,第一個節點是 Docker 主機,可以使用 SSH 連接到第二個節點。
在 Docker 主機上,安裝 vieux/sshfs 插件:
$ docker plugin install --grant-all-permissions vieux/sshfs
使用卷驅動程序創建卷
本例指定了一個 SSH 密碼,但是如果兩個主機配置了共享密鑰,則可以省略該密碼。每個卷驅動程序可能有零個或多個可配置選項,每個選項都使用 -o 標記指定。
$ docker volume create --driver vieux/sshfs \
-o sshcmd=test@node2:/home/test \
-o password=testpassword \
sshvolume
啟動使用卷驅動程序創建卷的容器
本例指定了一個 SSH 密碼,但是如果兩個主機配置了共享密鑰,則可以省略該密碼。每個卷驅動程序可能有零個或多個可配置選項。如果卷驅動程序要求您傳遞選項,則必須使用 --mount 標記掛載卷,而不是使用 -v。
$ docker run -d \
--name sshfs-container \
--volume-driver vieux/sshfs \
--mount src=sshvolume,target=/app,volume-opt=sshcmd=test@node2:/home/test,volume-opt=password=testpassword \
nginx:latest
創建創建 NFS 卷的服務
此示例顯示如何在創建服務時創建 NFS 卷。本例使用 10.0.0.10 作為 NFS 服務器,使用 /var/docker-nfs 作為 NFS 服務器上的出口目錄。請注意,指定的卷驅動程序是 local。
NFSV3
$ docker service create -d \
--name nfs-service \
--mount 'type=volume,source=nfsvolume,target=/app,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/var/docker-nfs,volume-opt=o=addr=10.0.0.10' \
nginx:latest
NFSV4
docker service create -d \
--name nfs-service \
--mount 'type=volume,source=nfsvolume,target=/app,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/var/docker-nfs,"volume-opt=o=10.0.0.10,rw,nfsvers=4,async"' \
nginx:latest
備份、還原或遷移數據卷
卷對於備份、還原和遷移非常有用。使用 --volumes-from 標記創建一個掛載該卷的新容器。
備份容器
例如,創建一個名為 dbstore 的新容器:
$ docker run -v /dbdata --name dbstore ubuntu /bin/bash
然后在下一條命令中,我們:
- 啟動一個新容器並從
dbstore容器掛載卷 - 掛載一個本地主機目錄作為
/backup - 傳遞一個命令,將
/dbdata卷的內容壓縮到目錄/backup中的backup.tar文件。
$ docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
當命令完成且容器停止時,我們留下了 /dbdata 卷的一個備份。
從備份中還原容器
使用剛剛創建的備份,您可以將其還原到同一個容器,或者其他地方創建的容器。
例如,創建一個名為 dbstore2 的新容器:
$ docker run -v /dbdata --name dbstore2 ubuntu /bin/bash
然后在新容器的數據卷中解壓備份文件:
$ docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"
您可以使用上述技術,使用您喜歡的工具自動執行備份、遷移和還原測試。
刪除卷
當刪除容器后,Docker 數據卷仍然存在。有兩種類型的卷需要考慮:
- 命名卷具有來自容器外部的特定源,例如
awesome:/bar。 - 匿名卷沒有特定的源,因此當容器被刪除時,通知 Docker 引擎守護進程刪除它們。
刪除匿名卷
要自動刪除匿名卷,請使用 --rm 選項。例如,這個命令創建一個匿名的 /foo 卷。當容器被刪除時,Docker 引擎會刪除 /foo 卷,但不會刪除 awesome 卷。
$ docker run --rm -v /foo -v awesome:/bar busybox top
刪除所有卷
要刪除所有未使用的卷並釋放空間,請執行以下操作:
$ docker volume prune
