Docker中的數據管理

默認狀況下,在容器內建立的全部文件都存儲在可寫容器層上。 這意味着:html

  • 當該容器再也不存在時,數據將不會持久保存,而且若是另外一個進程須要該數據,則可能很難從容器中取出數據。
  • 容器的可寫層與運行容器的主機緊密耦合。 您沒法輕鬆地將數據移動到其餘地方。
  • 寫入容器的可寫層須要存儲驅動程序來管理文件系統。 存儲驅動程序使用Linux內核提供聯合文件系統。 與使用直接寫入主機文件系統的數據卷相比,這種額外的抽象下降了性能。

Docker爲容器提供了兩個選項來將文件存儲在主機中,以便即便容器中止後文件也能夠持久存儲:卷和綁定掛載。若是您在Linux上運行Docker,也可使用tmpfs掛載。若是您在Windows上運行Docker,則還可使用命名管道。node

選擇正確的方式掛載

不管您選擇使用哪一種類型的安裝,容器中的數據看起來都是相同的。 它在容器的文件系統中顯示爲目錄或單個文件。nginx

可視化卷、綁定掛載和tmpfs掛載之間的差別的一個簡單方法是考慮數據在Docker主機上的位置。docker

  • 卷存儲在主機文件系統中,做爲其一部分,由Docker管理(在Linux上,位於/var/lib/docker/volumes/)。 非Docker進程不該修改文件系統的這一部分。卷是在Docker中持久保存數據的最佳方法。
  • 綁定掛載能夠存儲在主機系統上的任何位置。 它們甚至多是重要的系統文件或目錄。 Docker主機或Docker容器上的非Docker進程能夠隨時對其進行修改。
  • tmpfs掛載僅存儲在主機系統的內存中,而且永遠不會寫入主機系統的文件系統中。

掛載類型的更多細節

  • 卷:由Docker建立和管理。 您可使用docker volume create命令顯式建立卷,或者Docker能夠在容器或服務建立期間建立卷。建立卷時,它存儲在Docker主機上的目錄中。 將卷裝入容器時,此目錄就是裝入容器的目錄。 這相似於綁定掛載的工做方式,除了卷由Docker管理而且與主機的核心功能隔離。給定的卷能夠同時裝入多個容器中。當沒有運行的容器使用卷時,該卷仍然對Docker可用,不會自動刪除。您可使用docker volume prune來刪除未使用的卷。掛載卷時,它多是命名的或匿名的。 匿名卷首次安裝到容器中時,不會爲其指定顯式名稱,所以Docker爲它們提供一個隨機名稱,該名稱在給定Docker主機中保證是惟一的。 除了名稱以外,命名卷和匿名卷的行爲也相同。卷還支持使用卷驅動程序,這使您能夠將數據存儲在遠程主機或雲提供商上。
  • 綁定掛載:自Docker早期以來可用。 與卷相比,綁定安裝的功能有限。 使用綁定安裝時,會將主機上的文件或目錄安裝到容器中。 該文件或目錄由主機上的完整路徑引用。該文件或目錄不須要在Docker主機上已經存在。 若是尚不存在,則按需建立。 綁定掛載性能很是好,可是它們依賴於具備特定目錄結構的主機文件系統。 若是要開發新的Docker應用程序,請考慮使用命名卷。 您不能使用Docker CLI命令直接管理綁定安裝。使用綁定安裝的好與壞的一個反作用是,您能夠經過容器中運行的進程來更改主機文件系統,包括建立,修改或刪除重要的系統文件或目錄。 這是一項強大的功能,可能會帶來安全隱患,包括影響主機系統上的非Docker進程。
  • tmpfs掛載:tmpfs掛載不會持久化在磁盤上,不管是在Docker主機上仍是在容器內。 容器在其生存期內可使用它來存儲非持久狀態或敏感信息。 例如,在內部,羣服務使用tmpfs掛載將機密掛載到服務的容器中。
  • 命名管道:npipe安裝可用於Docker主機與容器之間的通訊。 常見用例是在容器內運行第三方工具,並使用命名管道鏈接到Docker Engine API。

綁定安裝和卷均可以使用-v--volume標誌安裝到容器中,可是二者的語法略有不一樣。 對於tmpfs掛載,可使用--tmpfs標誌。 可是,在Docker 17.06及更高版本中,建議將--mount標誌用於容器和服務,用於綁定安裝,卷或tmpfs安裝,由於語法更清晰。ubuntu

卷的使用案例

卷是將數據持久保存在Docker容器和服務中的首選方法。 卷的一些用例包括:安全

  • 在多個運行中的容器之間共享數據。 若是您未明確建立它,則將在第一次將其安裝到容器中時建立該卷。 當該容器中止或卸下時,該卷仍然存在。 多個容器能夠同時裝載相同的卷(讀寫或只讀)。 僅在顯式刪除卷時纔將它們刪除。
  • 不保證Docker主機具備給定的目錄或文件結構時。 卷可幫助您將Docker主機的配置與容器運行時解耦。
  • 當您要將容器的數據存儲在遠程主機或雲提供商上時,而不是在本地。
  • 當您須要將數據從一個Docker主機備份,還原或遷移到另外一個Docker主機時,卷是一個更好的選擇。 您能夠中止使用該卷的容器,而後備份該卷的目錄(例如/var/lib/docker/volumes/<volume-name>)。

綁定安裝的使用案例

一般,應儘量使用卷。 綁定安裝適用於如下類型的用例:bash

  • 將配置文件從主機共享到容器。 默認狀況下,這是Docker經過將/etc/resolv.conf從主機掛載到每一個容器中來爲容器提供DNS解析的方式。
  • 在Docker主機上的開發環境和容器之間共享源代碼或構建工件。 例如,您能夠將Maven 掛載到容器中target/目錄,而且每次在Docker主機上構建Maven項目時,該容器均可以訪問重建的工件。若是您以這種方式使用Docker進行開發,那麼您的生產Dockerfile會將生產就緒的工件直接複製到映像中,而不是依賴於綁定安裝。
  • 當確保Docker主機的文件或目錄結構與容器所需的綁定安裝一致時。

tmpfs mounts的使用案例

tmpfs掛載最適用於您不但願數據在主機或容器內持久存在的狀況。 當您的應用程序須要寫入大量非持久狀態數據時,這多是出於安全緣由或爲了保護容器的性能。服務器

使用綁定裝載或卷的技巧

若是使用綁定安裝或卷,請牢記如下幾點:app

  • 若是將空卷裝入存在文件或目錄的容器中的目錄中,則這些文件或目錄將傳播(複製)到該卷中。 一樣,若是啓動一個容器並指定一個尚不存在的卷,則會爲您建立一個空卷。 這是預填充另外一個容器所需數據的好方法。
  • 若是將綁定安裝或非空卷安裝到存在某些文件或目錄的容器中的目錄中,則這些文件或目錄會被安裝遮蓋,就像您將文件保存到Linux主機上的/ mnt中同樣,而後 將USB驅動器安裝到/ mnt中。 在卸載USB驅動器以前,/ mnt的內容將被USB驅動器的內容遮蓋。 不會刪除或更改被遮蓋的文件,可是在裝入綁定安裝或卷時將沒法訪問這些文件。

卷的使用

卷是存儲Docker容器生成和使用的數據的首選機制。綁定掛載依賴於主機的目錄結構,而卷則徹底由Docker管理。與綁定裝載相比,卷有幾個優勢:ssh

  • 卷比綁定掛載更容易備份或遷移。
  • 您可使用Docker CLI命令或Docker API來管理卷。
  • 卷能夠在Linux和Windows容器上工做。
  • 多個容器之間能夠更安全地共享卷。
  • 卷驅動程序容許您在遠程主機或雲提供商上存儲卷,以加密卷的內容或添加其餘功能。
  • 新卷的內容能夠由容器預先填充。

此外,與將數據持久保存在容器的可寫層中相比,卷一般是更好的選擇,由於卷不會增長使用卷的容器的大小,而且卷的內容存在於給定容器的生命週期以外。

若是您的容器生成非持久狀態數據,請考慮使用tmpfs掛載以免將數據永久存儲在任何地方,並經過避免寫入容器的可寫層來提升容器的性能。

卷使用rprivate綁定傳播,而且沒法爲卷配置綁定傳播。

Choose the -v or --mount flag

最初,-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。
    • 掛載的來源source。 對於命名卷,這是卷的名稱。 對於匿名卷,將省略此字段。 能夠指定爲sourcesrc
    • destination將文件或目錄在容器中的安裝路徑做爲其值。 能夠指定爲destinationdsttarget
    • 若是存在readonly選項,則會致使綁定掛載以只讀形式掛載到容器中。
    • volume-opt選項能夠屢次指定,它採用由選項名及其值組成的鍵-值對。

若是您的卷驅動程序接受逗號分隔的列表做爲選項,則必須從外部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

--mount-v行爲的不一樣點

與綁定掛載相反,全部卷選項均可用於--mount-v標誌。 將卷與服務一塊兒使用時,僅支持--mount

建立和管理卷

與綁定掛載不一樣,您能夠建立和管理任何容器範圍以外的卷。

建立卷:

$ docker volume create my-vol
複製代碼

列出卷:

$ docker volume ls

local               my-vol
複製代碼

檢查卷:

$ docker volume inspect my-vol
[
    {
        "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/中。 下面的-v--mount示例將產生相同的結果。除非在運行第一個卷以後刪除devtest容器和myvol2卷,不然沒法同時運行它們。

$ docker run -d \
  --name devtest \
  --mount source=myvol2,target=/app \
  nginx:latest
複製代碼
$ 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
複製代碼

啓動掛載卷的服務

啓動服務並定義卷時,每一個服務容器都使用其本身的本地卷。 若是使用本地卷驅動程序,則全部容器都不能共享此數據,可是某些卷驅動程序確實支持共享存儲。 適用於AWS的Docker和適用於Azure的Docker均使用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 
複製代碼

刪除服務,它將中止全部任務:

$ docker service rm devtest-service
複製代碼

刪除服務不會刪除該服務建立的任何卷。 卷刪除是一個單獨的步驟。

服務的語法不一樣點

docker 服務建立命令不支持-v--volume選項,當要掛載捲到服務的容器中時,必須使用--mount.

使用容器填充卷

若是你像上述的那樣啓動一個容器來建立卷,容器在掛載點存在文件和目錄(就像上述的/app/),文件的內容就會複製到卷中,而後,容器就能夠掛載和使用捲了,其餘使用該卷的容器也能訪問預填充內容。

爲了說明這一點,此示例啓動了一個nginx容器,並使用容器的/usr/share/nginx/html目錄的內容填充新的卷nginx-vol,Nginx在該目錄中存儲其默認HTML內容。

--mount-v 的實例有一樣的效果:

$ docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html \
  nginx:latest
複製代碼
$ 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具備一樣的效果:

$ docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \
  nginx:latest
複製代碼
$ 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。 nfs3

$ 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
複製代碼

nfs4

docker service create -d \
    --name nfs-service \
    --mount 'type=volume,source=nfsvolume,target=/app,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/,"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 Engine守護程序將其刪除。

刪除匿名卷

威力自動刪除匿名卷,使用--rm選項。例如,下面這個命令建立一個匿名卷/foo.當容器刪除後,Docker Engine將會刪除/foo卷,但不是awesome卷。

$ docker run --rm -v /foo -v awesome:/bar busybox top
複製代碼

刪除全部卷

要刪除全部未使用的卷並釋放空間:

$ docker volume prune
複製代碼
相關文章
相關標籤/搜索