Docker 基礎知識 - 使用卷(volume)管理應用程式資料

技術譯民發表於2020-07-15

卷(volumes)是 Docker 容器生產和使用持久化資料的首選機制。繫結掛載(bind mounts)依賴於主機的目錄結構,卷(volumes)完全由 Docker 管理。卷與繫結掛載相比有幾個優勢:

  • 卷比繫結掛載更容易備份或遷移。
  • 您可以使用 Docker CLI 命令或 Docker API 來管理卷。
  • 卷可以在 Linux 和 Windows 容器上工作。
  • 卷可以更安全地在多個容器之間共享。
  • 卷驅動程式允許您在遠端主機或雲提供商上儲存卷、加密卷的內容或新增其他功能。
  • 新卷的內容可以由容器預先填充。(New volumes can have their content pre-populated by a container.)

此外,與將資料持久化到容器的可寫層相比,卷通常是更好的選擇,因為卷不會增加使用它的容器的大小,而且卷的內容存在於給定容器的生命週期之外。

docker-types-of-mounts-volume

如果容器生成非永續性狀態資料,請考慮使用 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),可以是 bindvolume 或者 tmpfs。本主題討論卷(volume),因此型別(type)始終為卷(volume)。
    • 掛載的源(source),對於命名卷,這是卷的名稱。對於匿名卷,此欄位被省略。可以用 source 或者 src 來指定。
    • 目標(destination),將容器中檔案或目錄掛載的路徑作為其值。可以用 destinationdst 或者 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

在機器之間共享資料

在構建故障容錯的應用程式時,您可能需要配置同一服務的多個副本,以訪問相同的檔案。

volumes-shared-storage

在開發應用程式時,有幾種方法可以實現這一點。一種方法是向您的應用程式新增邏輯,在雲物件儲存系統(如 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

作者 : Docker 官網
譯者 : 技術譯民
出品 : 技術譯站
連結 : 英文原文

相關文章