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

https://deepzz.com/post/the-docker-volumes-basic.html
Desc:docker volume,容器卷,Permission denied,解決容器卷映射的問題,容器卷那些事

[docker volume 容器卷的那些事(二)](https://deepzz.com/post/
the-docker-volumes-permissions
.html)。接觸 docker 的朋友都知道,docker 鏡像是以 layer 概念存在的,一層一層的疊加,最終成為我們需要的鏡像。但該鏡像的每一層都是 ReadOnly 只讀的。只有在我們運行容器的時候才會創(chuàng)建讀寫層。文件系統(tǒng)的隔離使得:

  • 容器不再運行時,數(shù)據(jù)將不會持續(xù)存在,數(shù)據(jù)很難從容器中取出。
  • 無法在不同主機之間很好的進行數(shù)據(jù)遷移。
  • 數(shù)據(jù)寫入容器的讀寫層需要內(nèi)核提供聯(lián)合文件系統(tǒng),這會額外的降低性能。

docker 為我們提供了三種不同的方式將數(shù)據(jù)掛載到容器中:volume、bind mount、tmpfs。

types-of-mounts

volume 方式

volume 方式是 docker 中數(shù)據(jù)持久化的最佳方式。

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

相關(guān)用例

volume 方式應(yīng)該是持久化數(shù)據(jù)的首選方式, 其推薦用例:

  • 在多個容器之間共享數(shù)據(jù),volume 在容器停止或刪除的時候依然存在,如果需要刪除需要顯示(dockr rm -v...),多個容器可以加載相同的卷。
  • 當(dāng)主機不能保證有一個指定的目錄或文件結(jié)構(gòu)時。
  • 當(dāng)需要備份、還原或主機間的數(shù)據(jù)遷移時。停止容器,備份卷的目錄(如/var/lib/docker/volumes/<volume-name>

使用方式

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

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

-v/-mount 標(biāo)志
最初,-v-volume 用于獨立的容器,--mount 用于 swarm server。但 docker 17.06 之后,也可以使用 --mount。兩者的區(qū)別在于,-v 將所有選項組合在一個字段中,--mount 則將它們分開。

新用戶應(yīng)使用 --mount 語法,老用戶推薦使用 --mount。

  • -v/--volume,由(:)分隔的三個字段組成,<卷名>:<容器路徑>:<選項列表>。選項列表,如:ro只讀。
  • --mount,由多個鍵值對組成,由,分隔,每個由一個<key=<value>>元組組成。
    • type,值可以為 bind,volume,tmpfs
    • source,對于命名卷,是卷名。對于匿名卷,這個字段被省略??赡鼙恢付?sourcesrc
    • destination,文件或目錄將被掛載到容器中的路徑??梢灾付?destination,dsttarget。
    • volume-opt 可以多次指定。

創(chuàng)建管理 volume

# 創(chuàng)建一個卷
$ 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 產(chǎn)生的效果相同,但下面命令不能同時執(zhí)行,會沖突:

# --mount
# 使用  --mount source=myvol2,target/app,readonly 創(chuàng)建只讀的
$ docker run -d \
  -it \
  --name devtest \
  --mount source=myvol2,target=/app \
  nginx:latest
  
# -v 
# 使用 -v myvol2:/app:ro 創(chuàng)建只讀的
$ docker run -d \
  -it \
  --name devtest \
  -v myvol2:/app \
  nginx:latest

你可以執(zhí)行 docker inspect devtest 驗證卷是否創(chuàng)建并且掛載正確:

"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

當(dāng)啟動 service 的時候,如果 Driverlocal 的時候,則任何容器都不能共享此數(shù)據(jù)。另外 service 只能使用 --mount 標(biāo)志。

使用 volume driver

當(dāng)使用 docker volume create 創(chuàng)建卷或啟動尚未創(chuàng)建卷的容器的時候,可以指定卷驅(qū)動程序。

下面這個例子,首先創(chuàng)建獨立卷時使用 volume driver,然后在啟動創(chuàng)建新卷的容器時使用 volume driver。

初始設(shè)置
這個例子假定你有 2 個節(jié)點,第一個是 docker 主機,可以使用 SSH 連接到第二個節(jié)點。

在 docker 主機上安裝 vieux/sshfx 插件:

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

使用 volume driver 創(chuàng)建卷
下面指定了一個 SSH 密碼,但如果 2 臺主機共享密鑰已配置,則可以省略密碼。每個 volume driver 可以有多個配置選項,使用 -o 標(biāo)志指定。

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

創(chuàng)建容器時使用 volume driver
這里需要注意的是,如果需要在命令中使用選項,則必須使用 --mount,而不是 -v。

$ docker run -d \
  -it \
  --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

bind mount 方式

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

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

相關(guān)用例

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

  • 從主機共享配置文件到容器。默認情況,docker 會綁定類似 /etc/resolv.conf 的文件用于 DNS 的解析。
  • 主機與容器共享源代碼或構(gòu)建工具。如,你可以將 Maven target/ 掛載到容器中,并且每次主機上構(gòu)建 Maven 項目時,容器都可以訪問重建的構(gòu)件。
  • 主機的文件或目錄結(jié)構(gòu)與容器所需的一致時。

如果將空文件或目錄掛載到容器,容器中的該目錄又有文件,那么,這些文件將會被復(fù)制到主機上的目錄中。如果將非空的文件或目錄掛載到容器,容器中的該目錄也有文件,那么,容器中的文件將會被隱藏。

使用方式

-v/-mount 標(biāo)志
最初,-v-volume 用于獨立的容器,--mount 用于 swarm server。但 docker 17.06 之后,也可以使用 --mount。兩者的區(qū)別在于,-v 將所有選項組合在一個字段中,--mount 則將它們分開。

新用戶應(yīng)使用 --mount 語法,老用戶推薦使用 --mount

  • -v--volume:由(:)分隔的字段組成。這些字段是有順序的。
    • 第一個字段,主機上的文件或目錄。
    • 第二個字段,容器中的文件或目錄。
    • 第三個字段,可選,且用逗號分隔,如:roconsistent,delegatedcached,zZ。
  • --mount:由多個鍵值對組成,由逗號分隔,每一個由 <key>=<value> 元祖組成。鍵值對沒有順序。
    • type,可以是 bind,volume,tmpfs
    • source,主機上的文件或目錄的路徑??赡苡?src,source 指定。
    • destination,容器中的文件或目錄的路徑。可能用 destination,dsttarget 指定。
    • readonly,如果存在,將更改 Propagation,可以是一個 rprivate。
    • consistency,如果存在,可以是 consistent,delegatedcached,只在 Mac 版有效。
    • --mount 標(biāo)志不支持 zZ 修改 selinux。

-v 和 --mount 的差異
使用 -v--volume 綁定主機不存在的文件或目錄,將會自動創(chuàng)建。始終創(chuàng)建的是一個目錄。

使用 --mount 綁定主機上不存在的文件或目錄,則不會自動創(chuàng)建,會產(chǎn)生一個錯誤。

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

# 只讀方式:--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 可以查看相關(guān)信息,查看 Mounts 部分:

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

這些信息表明了這是一個 bind 掛載,源路徑和目的路徑,并且是可讀寫的,且 Propagation 設(shè)置為 rprivate

停止容器:

$ docker container stop devtest

$ docker container rm devtest

配置 Propagation
Propagation 的在 bind mount 和 volume 中默認為 rprivate。它只能在 bind mount 配置,并且只能在 Linux 主機上配置。這是一個高級選項,許多用戶不需要配置它。

Propagation 是指在給定的掛載卷或命名卷中創(chuàng)建的掛載是否可以傳播到該掛載的副本??紤]一個掛載點 /mnt,它被掛載在 /tmp。傳播設(shè)置控制是否掛載 /tmp/a 也可用 /mnt/a.每個 Propagation 設(shè)置都有一個遞歸對應(yīng)點。在遞歸的情況下,考慮 /tml/a 被掛載為 /foo。傳播設(shè)置控制是否 /mnt/a/tmp/a 將存在。

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

在可以在安裝點上設(shè)置綁定傳播之前,主機文件系統(tǒng)需要已經(jīng)支持綁定傳播。有關(guān)綁定傳播的更多信息,請參閱 共享子樹Linux內(nèi)核文檔。

以下示例將 target/ 目錄裝載到容器中兩次,第二個裝入設(shè)置 ro 選項和 rslave 綁定傳播選項。

--mount-v 實例有同樣的結(jié)果。

$ 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

現(xiàn)在如果你創(chuàng)建 /app/foo/,/app2/foo/ 也將存在。

配置selinux標(biāo)簽

如果使用的 selinux 話,可以添加 z 或者 Z 選項來修改正在裝入容器的主機文件或目錄selinux 標(biāo)簽。這會影響主機本身的文件或目錄,并可能導(dǎo)致Docker范圍之外的后果。

  • z 選項指示綁定安裝內(nèi)容在多個容器之間共享。
  • Z 選項指示綁定安裝內(nèi)容是私有的和非共享的。

使用極端謹(jǐn)慎使用這些選項。綁定一個系統(tǒng)目錄,例如 /home或者 /usr 用這個 Z 選項,將會使你的主機無法工作,你可能需要手工重新標(biāo)記主機文件。

重要:在使用綁定安裝服務(wù)時,selinux標(biāo)簽(:Z:z)以及 :ro 被忽略。有關(guān)詳細信息,請參閱 moby/moby#32579

這個例子設(shè)置 z 選項來指定多個容器可以共享綁定掛載的內(nèi)容:

使用 --mount 標(biāo)志來修改selinux標(biāo)簽是不可能的。

$ 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 主機上發(fā)生寫入或通過容器中的掛載時,都會將更改刷新到磁盤,以便共享中的所有參與者都具有完全一致的視圖。在某些情況下,完全一致可能會嚴(yán)重影響性能。Docker 17.05 和更高版本引入了選項來調(diào)整一個一個,每個容器的一致性設(shè)置。以下選項可用:

  • consistent 或者 default:完全一致的默認設(shè)置,如上所述。
  • delegated:容器運行時的掛載視圖是權(quán)威的。在容器中進行的更新可能在主機上可見之前可能會有延遲。
  • cached:macOS主機的掛載視圖是權(quán)威的。在主機上進行的更新在容器中可見之前可能會有延遲。

這些選項在除 macOS 以外的所有主機操作系統(tǒng)上完全忽略。

在--mount和-v實例有同樣的結(jié)果。

$ 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,僅存儲在主機系統(tǒng)的內(nèi)存中,不會寫入主機的文件系統(tǒng)。

相關(guān)用例

tmpfs,使用它的情況一般是,對安全比較重視以及不需要持久化數(shù)據(jù)。

使用方式

--tmpfs--mount 的關(guān)系與前面兩種方式的關(guān)系不用多說。那它們之間的差異是:

  • --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 標(biāo)志,因為該 --tmpfs 標(biāo)志不支持它們。

選項 描述
tmpfs-size tmpfs 的大小,以字節(jié)為單位。無限制默認。
tmpfs-mode tmpfs 的八進制文件模式。例如,700 或者 0770。默認為 1777 或世界可寫。

以下示例將設(shè)置 tmpfs-mode1770,以便在容器內(nèi)不可世界讀取。

docker run -d \
  -it \
  --name tmptest \
  --mount type=tmpfs,destination=/app,tmpfs-mode=1770 \
  nginx:latest
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容