1. 程式人生 > >docker volume 容器卷的那些事(一)

docker volume 容器卷的那些事(一)

docker volume 容器卷的那些事(二)。接觸 docker 的朋友都知道,docker 映象是以 layer 概念存在的,一層一層的疊加,最終成為我們需要的映象。但該映象的每一層都是 ReadOnly 只讀的。只有在我們執行容器的時候才會建立讀寫層。檔案系統的隔離使得:

  • 容器不再執行時,資料將不會持續存在,資料很難從容器中取出。
  • 無法在不同主機之間很好的進行資料遷移。
  • 資料寫入容器的讀寫層需要核心提供聯合檔案系統,這會額外的降低效能。

docker 為我們提供了三種不同的方式將資料掛載到容器中:volume、bind mount、tmpfs
types-of-mounts

volume 方式

volume 方式是 docker 中資料持久化的最佳方式。

  • docker 預設在主機上會有一個特定的區域(/var/lib/docker/volumes/ Linux),該區域用來存放 volume。
  • 非 docker 程序不應該去修改該區域。
  • volume 可以通過 docker volume 進行管理,如建立、刪除等操作。
  • volume 在生成的時候如果不指定名稱,便會隨機生成。
$ ls /var/lib/docker/volumes
ff664768bfe64e1a8cae4369dd4a2e1929362e29580735480290684e38c8f140
ffa4846b581c1a50a01e7a12a6342ad2aaa442701a35ae56ef2f0e5d7888b22c
  • volume 在容器停止或刪除的時候會繼續存在,如需刪除需要顯示宣告。
$ docker rm -v <container_id>
$ docker volume rm <volume_name>

相關用例

volume 方式應該是持久化資料的首選方式, 其推薦用例:

  • 在多個容器之間共享資料,volume 在容器停止或刪除的時候依然存在,如果需要刪除需要顯示(dockr rm -v…),多個容器可以載入相同的卷。
  • 當主機不能保證有一個指定的目錄或檔案結構時。
  • 當需要備份、還原或主機間的資料遷移時。停止容器,備份卷的目錄(如/var/lib/docker/volumes/<volume-name>

使用方式

volume 在 docker 中被推薦為首選方式,它與 bind mount 相比,有以下優點:

  • 與 bind mount 相比,volume 更容易備份或遷移。
  • 可以使用 Docker CLI 命令或 Docker API 來管理。
  • volume 在 Linux 和 Windows 容器上都能工作。
  • volume 可以在多個容器之間更安全的共享。
  • volume 驅動程式允許你在遠端主機或雲上提供儲存、加密或其他功能。
  • 新 volume 的內容可以由容器預填充。

-v/-mount 標誌
最初,-v-volume 用於獨立的容器,--mount 用於 swarm server。但 docker 17.06 之後,也可以使用 --mount。兩者的區別在於,-v 將所有選項組合在一個欄位中,--mount 則將它們分開。

新使用者應使用 --mount 語法,老使用者推薦使用 --mount

  • -v/--volume,由(:)分隔的三個欄位組成,<卷名>:<容器路徑>:<選項列表>。選項列表,如:ro只讀。
  • --mount,由多個鍵值對組成,由,分隔,每個由一個<key=<value>>元組組成。
    • type,值可以為 bindvolumetmpfs
    • source,對於命名卷,是卷名。對於匿名卷,這個欄位被省略。可能被指定為 sourcesrc
    • destination,檔案或目錄將被掛載到容器中的路徑。可以指定為 destinationdsttarget
    • volume-opt 可以多次指定。

建立管理 volume

# 建立一個卷
$ 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

用卷啟動容器
下例,將卷 myvol2 掛載到容器 /app/-v--mount 產生的效果相同,但下面命令不能同時執行,會衝突:

# --mount
# 使用  --mount source=myvol2,target/app,readonly 建立只讀的
$ docker run -d \
  -it \
  --name devtest \
  --mount source=myvol2,target=/app \
  nginx:latest

# -v 
# 使用 -v myvol2:/app:ro 建立只讀的
$ docker run -d \
  -it \
  --name devtest \
  -v myvol2:/app \
  nginx:latest

你可以執行 docker inspect devtest 驗證卷是否建立並且掛載正確:

"Mounts": [
    {
        "Type": "volume",
        "Name": "myvol2",
        "Source": "/var/lib/docker/volumes/myvol2/_data",
        "Destination": "/app",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
],

該卷有正確的 Source 和 Destination,可讀寫。

停止容器和清理卷:

$ docker container stop devtest

$ docker container rm devtest

$ docker volume rm myvol2

當啟動 service 的時候,如果 Driverlocal 的時候,則任何容器都不能共享此資料。另外 service 只能使用 --mount 標誌。

使用 volume driver

當使用 docker volume create 建立卷或啟動尚未建立卷的容器的時候,可以指定卷驅動程式。

下面這個例子,首先建立獨立卷時使用 volume driver,然後在啟動建立新卷的容器時使用 volume driver。

初始設定
這個例子假定你有 2 個節點,第一個是 docker 主機,可以使用 SSH 連線到第二個節點。

在 docker 主機上安裝 vieux/sshfx 外掛:

$ docker plugin install --grant-all-permissions vieux/sshfs

使用 volume driver 建立卷
下面指定了一個 SSH 密碼,但如果 2 臺主機共享金鑰已配置,則可以省略密碼。每個 volume driver 可以有多個配置選項,使用 -o 標誌指定。

$ docker volume create --driver vieux/sshfs \
  -o sshcmd=test@node2:/home/test \
  -o password=testpassword \
  sshvolume

建立容器時使用 volume driver
這裡需要注意的是,如果需要在命令中使用選項,則必須使用 --mount,而不是 -v

$ docker run -d \
  -it \
  --name sshfs-container \
  --volume-driver vieux/sshfs \
  --mount src=sshvolume,target=/app,[email protected]:/home/test,volume-opt=password=testpassword \
  nginx:latest

bind mount 方式

通過 bind mount 方式,你可以將你主機上的任何檔案或目錄(絕對路徑)掛載到容器中。

  • 掛載的檔案或目錄可以被任何程序修改,因此有時候容器中修改了該檔案或目錄將會影響其他程序。
  • 如果掛載主機的檔案或目錄不存在將會自動建立。
  • 使用該方式不能通過 docker volume 管理,推薦使用 volume 方式。

相關用例

bind mounts,一般情況在如下方式使用:

  • 從主機共享配置檔案到容器。預設情況,docker 會繫結類似 /etc/resolv.conf 的檔案用於 DNS 的解析。
  • 主機與容器共享原始碼或構建工具。如,你可以將 Maven target/ 掛載到容器中,並且每次主機上構建 Maven 專案時,容器都可以訪問重建的構件。
  • 主機的檔案或目錄結構與容器所需的一致時。

如果將空檔案或目錄掛載到容器,容器中的該目錄又有檔案,那麼,這些檔案將會被複制到主機上的目錄中。如果將非空的檔案或目錄掛載到容器,容器中的該目錄也有檔案,那麼,容器中的檔案將會被隱藏。

使用方式

-v/-mount 標誌
最初,-v-volume 用於獨立的容器,--mount 用於 swarm server。但 docker 17.06 之後,也可以使用 --mount。兩者的區別在於,-v 將所有選項組合在一個欄位中,--mount 則將它們分開。

新使用者應使用 --mount 語法,老使用者推薦使用 --mount

  • -v--volume:由(:)分隔的欄位組成。這些欄位是有順序的。
    • 第一個欄位,主機上的檔案或目錄。
    • 第二個欄位,容器中的檔案或目錄。
    • 第三個欄位,可選,且用逗號分隔,如:roconsistentdelegatedcachedzZ
  • --mount:由多個鍵值對組成,由逗號分隔,每一個由 <key>=<value> 元祖組成。鍵值對沒有順序。
    • type,可以是 bindvolumetmpfs
    • source,主機上的檔案或目錄的路徑。可能用 srcsource 指定。
    • destination,容器中的檔案或目錄的路徑。可能用 destinationdsttarget 指定。
    • readonly,如果存在,將更改 Propagation,可以是一個 rprivate
    • consistency,如果存在,可以是 consistentdelegatedcached,只在 Mac 版有效。
    • --mount 標誌不支援 zZ 修改 selinux。

-v 和 –mount 的差異
使用 -v--volume 繫結主機不存在的檔案或目錄,將會自動建立。始終建立的是一個目錄。

使用 --mount 繫結主機上不存在的檔案或目錄,則不會自動建立,會產生一個錯誤。

使用 bind mount 啟動容器
主機上的目錄 source/target,容器的目錄 /app/$(pwd) 將使用當前目錄:

# 只讀方式:--mount type=bind,source="$(pwd)"/target,target=/app,readonly
$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest

# 只讀方式:-v "$(pwd)"/target:/app:ro
$ 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 掛載,源路徑和目的路徑,並且是可讀寫的,且 Propagation 設定為 rprivate

停止容器:

$ docker container stop devtest

$ docker container rm devtest

配置 Propagation
Propagation 的在 bind mount 和 volume 中預設為 rprivate。它只能在 bind mount 配置,並且只能在 Linux 主機上配置。這是一個高階選項,許多使用者不需要配置它。

Propagation 是指在給定的掛載卷或命名卷中建立的掛載是否可以傳播到該掛載的副本。考慮一個掛載點 /mnt,它被掛載在 /tmp。傳播設定控制是否掛載 /tmp/a 也可用 /mnt/a.每個 Propagation 設定都有一個遞迴對應點。在遞迴的情況下,考慮 /tml/a 被掛載為 /foo。傳播設定控制是否 /mnt/a/tmp/a 將存在。

Propagation 設定 描述
shared 原始安裝的子安裝會暴露給副本安裝,並且副本安裝的子安裝也會傳播到原始安裝。
slave 類似於共享的安裝,但僅在一個方向上。如果原始安裝顯示一個子安裝,副本安裝可以看到它。但是,如果副本安裝公開了子安裝,則原始安裝無法看到它。
private 這座山是私人的。其中的子安裝不會暴露給副本安裝,並且副安裝的子安裝不會暴露給原始安裝。
rshared 與共享相同,但是傳播也擴充套件到巢狀在任何原始或副本安裝點內的掛載點。
rslave 與從屬裝置相同,但傳播也延伸到巢狀在任何原始或副本安裝點內的掛載點。
rprivate 預設。與私有相同,這意味著在原始或副本安裝點內的任何位置都不會有安裝點向任一方向傳播。

在可以在安裝點上設定繫結傳播之前,主機檔案系統需要已經支援繫結傳播。有關繫結傳播的更多資訊,請參閱 共享子樹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 話,可以新增 z 或者 Z 選項來修改正在裝入容器的主機檔案或目錄selinux 標籤。這會影響主機本身的檔案或目錄,並可能導致Docker範圍之外的後果。

  • z 選項指示繫結安裝內容在多個容器之間共享。
  • Z 選項指示繫結安裝內容是私有的和非共享的。

使用極端謹慎使用這些選項。繫結一個系統目錄,例如 /home或者 /usr 用這個 Z 選項,將會使你的主機無法工作,你可能需要手工重新標記主機檔案。

重要:在使用繫結安裝服務時,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 for Mac用於 osxfs 將從 macOS 共享的目錄和檔案傳播到 Linux VM。這種傳播使這些目錄和檔案可用於在 Docker for Mac 上執行的 Docker 容器。

預設情況下,這些共享是完全一致的,這意味著每次在 macOS 主機上發生寫入或通過容器中的掛載時,都會將更改重新整理到磁碟,以便共享中的所有參與者都具有完全一致的檢視。在某些情況下,完全一致可能會嚴重影響效能。Docker 17.05 和更高版本引入了選項來調整一個一個,每個容器的一致性設定。以下選項可用:

  • consistent 或者 default:完全一致的預設設定,如上所述。
  • delegated:容器執行時的掛載檢視是權威的。在容器中進行的更新可能在主機上可見之前可能會有延遲。
  • cached:macOS主機的掛載檢視是權威的。在主機上進行的更新在容器中可見之前可能會有延遲。

這些選項在除 macOS 以外的所有主機作業系統上完全忽略。

在–mount和-v例項有同樣的結果。

$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,destination=/app,consistency=cached \
  nginx:latest

$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:cached \
  nginx:latest

tmpfs 方式

tmpfs,僅儲存在主機系統的記憶體中,不會寫入主機的檔案系統。

相關用例

tmpfs,使用它的情況一般是,對安全比較重視以及不需要持久化資料。

使用方式

--tmpfs--mount 的關係與前面兩種方式的關係不用多說。那它們之間的差異是:

  • --tmpfs 不允許指定任何可配置選項。
  • --tmpfs 不能用語 swarm service,你必須使用 --mount

tmpfs 容器的限制

  • tmpfs 掛載不能在容器間共享。
  • tmpfs 職能在 Linux 容器上工作,不能在 windows 容器上工作。

容器中使用 tmpfs

$ docker run -d \
  -it \
  --name tmptest \
  --mount type=tmpfs,destination=/app \
  nginx:latest

$ docker run -d \
  -it \
  --name tmptest \
  --tmpfs /app \
  nginx:latest

tmpfs 通過執行 docker container inspect tmptest 並查詢 Mounts 部分來驗證安裝是掛載:

"Tmpfs": {
    "/app": ""
},

刪除容器:

$ docker container stop tmptest

$ Docker container rm tmptest

指定 tmpfs 選項
tmpfs 掛載允許兩個配置選項,這兩個都是不需要的。如果您需要指定這些選項,則必須使用該 --mount 標誌,因為該 --tmpfs 標誌不支援它們。

選項 描述
tmpfs-size tmpfs 的大小,以位元組為單位。無限制預設。
tmpfs-mode tmpfs 的八進位制檔案模式。例如,700 或者 0770。預設為 1777 或世界可寫。

以下示例將設定 tmpfs-mode1770,以便在容器內不可世界讀取。

docker run -d \
  -it \
  --name tmptest \
  --mount type=tmpfs,destination=/app,tmpfs-mode=1770 \
  nginx:latest