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 只讀的。只有在我們運(yùn)行容器的時(shí)候才會(huì)創(chuàng)建讀寫層。文件系統(tǒng)的隔離使得:
- 容器不再運(yùn)行時(shí),數(shù)據(jù)將不會(huì)持續(xù)存在,數(shù)據(jù)很難從容器中取出。
- 無(wú)法在不同主機(jī)之間很好的進(jìn)行數(shù)據(jù)遷移。
- 數(shù)據(jù)寫入容器的讀寫層需要內(nèi)核提供聯(lián)合文件系統(tǒng),這會(huì)額外的降低性能。
docker 為我們提供了三種不同的方式將數(shù)據(jù)掛載到容器中:volume、bind mount、tmpfs。

volume 方式
volume 方式是 docker 中數(shù)據(jù)持久化的最佳方式。
- docker 默認(rèn)在主機(jī)上會(huì)有一個(gè)特定的區(qū)域(
/var/lib/docker/volumes/Linux),該區(qū)域用來(lái)存放 volume。 - 非 docker 進(jìn)程不應(yīng)該去修改該區(qū)域。
- volume 可以通過
docker volume進(jìn)行管理,如創(chuàng)建、刪除等操作。 - volume 在生成的時(shí)候如果不指定名稱,便會(huì)隨機(jī)生成。
$ ls /var/lib/docker/volumes
ff664768bfe64e1a8cae4369dd4a2e1929362e29580735480290684e38c8f140
ffa4846b581c1a50a01e7a12a6342ad2aaa442701a35ae56ef2f0e5d7888b22c
- volume 在容器停止或刪除的時(shí)候會(huì)繼續(xù)存在,如需刪除需要顯示聲明。
$ docker rm -v <container_id>
$ docker volume rm <volume_name>
相關(guān)用例
volume 方式應(yīng)該是持久化數(shù)據(jù)的首選方式, 其推薦用例:
- 在多個(gè)容器之間共享數(shù)據(jù),volume 在容器停止或刪除的時(shí)候依然存在,如果需要?jiǎng)h除需要顯示(dockr rm -v...),多個(gè)容器可以加載相同的卷。
- 當(dāng)主機(jī)不能保證有一個(gè)指定的目錄或文件結(jié)構(gòu)時(shí)。
- 當(dāng)需要備份、還原或主機(jī)間的數(shù)據(jù)遷移時(shí)。停止容器,備份卷的目錄(如
/var/lib/docker/volumes/<volume-name>。
使用方式
volume 在 docker 中被推薦為首選方式,它與 bind mount 相比,有以下優(yōu)點(diǎn):
- 與 bind mount 相比,volume 更容易備份或遷移。
- 可以使用 Docker CLI 命令或 Docker API 來(lái)管理。
- volume 在 Linux 和 Windows 容器上都能工作。
- volume 可以在多個(gè)容器之間更安全的共享。
- volume 驅(qū)動(dòng)程序允許你在遠(yuǎn)程主機(jī)或云上提供存儲(chǔ)、加密或其他功能。
- 新 volume 的內(nèi)容可以由容器預(yù)填充。
-v/-mount 標(biāo)志
最初,-v 和 -volume 用于獨(dú)立的容器,--mount 用于 swarm server。但 docker 17.06 之后,也可以使用 --mount。兩者的區(qū)別在于,-v 將所有選項(xiàng)組合在一個(gè)字段中,--mount 則將它們分開。
新用戶應(yīng)使用
--mount語(yǔ)法,老用戶推薦使用--mount。
-
-v/--volume,由(:)分隔的三個(gè)字段組成,<卷名>:<容器路徑>:<選項(xiàng)列表>。選項(xiàng)列表,如:ro只讀。 -
--mount,由多個(gè)鍵值對(duì)組成,由,分隔,每個(gè)由一個(gè)<key=<value>>元組組成。-
type,值可以為bind,volume,tmpfs。 -
source,對(duì)于命名卷,是卷名。對(duì)于匿名卷,這個(gè)字段被省略??赡鼙恢付?source或src。 -
destination,文件或目錄將被掛載到容器中的路徑。可以指定為destination,dst或target。 -
volume-opt可以多次指定。
-
創(chuàng)建管理 volume
# 創(chuàng)建一個(gè)卷
$ 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
用卷啟動(dòng)容器
下例,將卷 myvol2 掛載到容器 /app/。-v 和 --mount 產(chǎn)生的效果相同,但下面命令不能同時(shí)執(zhí)行,會(huì)沖突:
# --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 驗(yàn)證卷是否創(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)啟動(dòng) service 的時(shí)候,如果
Driver是local的時(shí)候,則任何容器都不能共享此數(shù)據(jù)。另外service只能使用--mount標(biāo)志。
使用 volume driver
當(dāng)使用 docker volume create 創(chuàng)建卷或啟動(dòng)尚未創(chuàng)建卷的容器的時(shí)候,可以指定卷驅(qū)動(dòng)程序。
下面這個(gè)例子,首先創(chuàng)建獨(dú)立卷時(shí)使用 volume driver,然后在啟動(dòng)創(chuàng)建新卷的容器時(shí)使用 volume driver。
初始設(shè)置
這個(gè)例子假定你有 2 個(gè)節(jié)點(diǎn),第一個(gè)是 docker 主機(jī),可以使用 SSH 連接到第二個(gè)節(jié)點(diǎn)。
在 docker 主機(jī)上安裝 vieux/sshfx 插件:
$ docker plugin install --grant-all-permissions vieux/sshfs
使用 volume driver 創(chuàng)建卷
下面指定了一個(gè) SSH 密碼,但如果 2 臺(tái)主機(jī)共享密鑰已配置,則可以省略密碼。每個(gè) volume driver 可以有多個(gè)配置選項(xiàng),使用 -o 標(biāo)志指定。
$ docker volume create --driver vieux/sshfs \
-o sshcmd=test@node2:/home/test \
-o password=testpassword \
sshvolume
創(chuàng)建容器時(shí)使用 volume driver
這里需要注意的是,如果需要在命令中使用選項(xiàng),則必須使用 --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 方式,你可以將你主機(jī)上的任何文件或目錄(絕對(duì)路徑)掛載到容器中。
- 掛載的文件或目錄可以被任何進(jìn)程修改,因此有時(shí)候容器中修改了該文件或目錄將會(huì)影響其他進(jìn)程。
- 如果掛載主機(jī)的文件或目錄不存在將會(huì)自動(dòng)創(chuàng)建。
- 使用該方式不能通過
docker volume管理,推薦使用 volume 方式。
相關(guān)用例
bind mounts,一般情況在如下方式使用:
- 從主機(jī)共享配置文件到容器。默認(rèn)情況,docker 會(huì)綁定類似
/etc/resolv.conf的文件用于 DNS 的解析。 - 主機(jī)與容器共享源代碼或構(gòu)建工具。如,你可以將 Maven
target/掛載到容器中,并且每次主機(jī)上構(gòu)建 Maven 項(xiàng)目時(shí),容器都可以訪問重建的構(gòu)件。 - 主機(jī)的文件或目錄結(jié)構(gòu)與容器所需的一致時(shí)。
如果將空文件或目錄掛載到容器,容器中的該目錄又有文件,那么,這些文件將會(huì)被復(fù)制到主機(jī)上的目錄中。如果將非空的文件或目錄掛載到容器,容器中的該目錄也有文件,那么,容器中的文件將會(huì)被隱藏。
使用方式
-v/-mount 標(biāo)志
最初,-v 和 -volume 用于獨(dú)立的容器,--mount 用于 swarm server。但 docker 17.06 之后,也可以使用 --mount。兩者的區(qū)別在于,-v 將所有選項(xiàng)組合在一個(gè)字段中,--mount 則將它們分開。
新用戶應(yīng)使用
--mount語(yǔ)法,老用戶推薦使用--mount。
-
-v或--volume:由(:)分隔的字段組成。這些字段是有順序的。- 第一個(gè)字段,主機(jī)上的文件或目錄。
- 第二個(gè)字段,容器中的文件或目錄。
- 第三個(gè)字段,可選,且用逗號(hào)分隔,如:
ro,consistent,delegated,cached,z和Z。
-
--mount:由多個(gè)鍵值對(duì)組成,由逗號(hào)分隔,每一個(gè)由<key>=<value>元祖組成。鍵值對(duì)沒有順序。-
type,可以是bind,volume,tmpfs。 -
source,主機(jī)上的文件或目錄的路徑??赡苡?src,source指定。 -
destination,容器中的文件或目錄的路徑??赡苡?destination,dst,target指定。 -
readonly,如果存在,將更改 Propagation,可以是一個(gè)rprivate。 -
consistency,如果存在,可以是consistent,delegated或cached,只在 Mac 版有效。 -
--mount標(biāo)志不支持z或Z修改 selinux。
-
-v 和 --mount 的差異
使用 -v 和 --volume 綁定主機(jī)不存在的文件或目錄,將會(huì)自動(dòng)創(chuàng)建。始終創(chuàng)建的是一個(gè)目錄。
使用 --mount 綁定主機(jī)上不存在的文件或目錄,則不會(huì)自動(dòng)創(chuàng)建,會(huì)產(chǎn)生一個(gè)錯(cuò)誤。
使用 bind mount 啟動(dòng)容器
主機(jī)上的目錄 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"
}
],
這些信息表明了這是一個(gè) bind 掛載,源路徑和目的路徑,并且是可讀寫的,且 Propagation 設(shè)置為 rprivate。
停止容器:
$ docker container stop devtest
$ docker container rm devtest
配置 Propagation
Propagation 的在 bind mount 和 volume 中默認(rèn)為 rprivate。它只能在 bind mount 配置,并且只能在 Linux 主機(jī)上配置。這是一個(gè)高級(jí)選項(xiàng),許多用戶不需要配置它。
Propagation 是指在給定的掛載卷或命名卷中創(chuàng)建的掛載是否可以傳播到該掛載的副本??紤]一個(gè)掛載點(diǎn) /mnt,它被掛載在 /tmp。傳播設(shè)置控制是否掛載 /tmp/a 也可用 /mnt/a.每個(gè) Propagation 設(shè)置都有一個(gè)遞歸對(duì)應(yīng)點(diǎn)。在遞歸的情況下,考慮 /tml/a 被掛載為 /foo。傳播設(shè)置控制是否 /mnt/a 或 /tmp/a 將存在。
| Propagation 設(shè)置 | 描述 |
|---|---|
shared |
原始安裝的子安裝會(huì)暴露給副本安裝,并且副本安裝的子安裝也會(huì)傳播到原始安裝。 |
slave |
類似于共享的安裝,但僅在一個(gè)方向上。如果原始安裝顯示一個(gè)子安裝,副本安裝可以看到它。但是,如果副本安裝公開了子安裝,則原始安裝無(wú)法看到它。 |
private |
這座山是私人的。其中的子安裝不會(huì)暴露給副本安裝,并且副安裝的子安裝不會(huì)暴露給原始安裝。 |
rshared |
與共享相同,但是傳播也擴(kuò)展到嵌套在任何原始或副本安裝點(diǎn)內(nèi)的掛載點(diǎn)。 |
rslave |
與從屬設(shè)備相同,但傳播也延伸到嵌套在任何原始或副本安裝點(diǎn)內(nèi)的掛載點(diǎn)。 |
rprivate |
默認(rèn)。與私有相同,這意味著在原始或副本安裝點(diǎn)內(nèi)的任何位置都不會(huì)有安裝點(diǎn)向任一方向傳播。 |
在可以在安裝點(diǎn)上設(shè)置綁定傳播之前,主機(jī)文件系統(tǒng)需要已經(jīng)支持綁定傳播。有關(guān)綁定傳播的更多信息,請(qǐng)參閱 共享子樹 的 Linux內(nèi)核文檔。
以下示例將 target/ 目錄裝載到容器中兩次,第二個(gè)裝入設(shè)置 ro 選項(xiàng)和 rslave 綁定傳播選項(xiàng)。
在 --mount 和 -v 實(shí)例有同樣的結(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 選項(xiàng)來(lái)修改正在裝入容器的主機(jī)文件或目錄的 selinux 標(biāo)簽。這會(huì)影響主機(jī)本身的文件或目錄,并可能導(dǎo)致Docker范圍之外的后果。
- 該
z選項(xiàng)指示綁定安裝內(nèi)容在多個(gè)容器之間共享。 - 該
Z選項(xiàng)指示綁定安裝內(nèi)容是私有的和非共享的。
使用極端謹(jǐn)慎使用這些選項(xiàng)。綁定一個(gè)系統(tǒng)目錄,例如 /home或者 /usr 用這個(gè) Z 選項(xiàng),將會(huì)使你的主機(jī)無(wú)法工作,你可能需要手工重新標(biāo)記主機(jī)文件。
重要:在使用綁定安裝服務(wù)時(shí),selinux標(biāo)簽(:Z和:z)以及:ro被忽略。有關(guān)詳細(xì)信息,請(qǐng)參閱 moby/moby#32579。
這個(gè)例子設(shè)置 z 選項(xiàng)來(lái)指定多個(gè)容器可以共享綁定掛載的內(nèi)容:
使用 --mount 標(biāo)志來(lái)修改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 上運(yùn)行的 Docker 容器。
默認(rèn)情況下,這些共享是完全一致的,這意味著每次在 macOS 主機(jī)上發(fā)生寫入或通過容器中的掛載時(shí),都會(huì)將更改刷新到磁盤,以便共享中的所有參與者都具有完全一致的視圖。在某些情況下,完全一致可能會(huì)嚴(yán)重影響性能。Docker 17.05 和更高版本引入了選項(xiàng)來(lái)調(diào)整一個(gè)一個(gè),每個(gè)容器的一致性設(shè)置。以下選項(xiàng)可用:
-
consistent或者default:完全一致的默認(rèn)設(shè)置,如上所述。 -
delegated:容器運(yùn)行時(shí)的掛載視圖是權(quán)威的。在容器中進(jìn)行的更新可能在主機(jī)上可見之前可能會(huì)有延遲。 -
cached:macOS主機(jī)的掛載視圖是權(quán)威的。在主機(jī)上進(jìn)行的更新在容器中可見之前可能會(huì)有延遲。
這些選項(xiàng)在除 macOS 以外的所有主機(jī)操作系統(tǒng)上完全忽略。
在--mount和-v實(shí)例有同樣的結(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,僅存儲(chǔ)在主機(jī)系統(tǒng)的內(nèi)存中,不會(huì)寫入主機(jī)的文件系統(tǒng)。
相關(guān)用例
tmpfs,使用它的情況一般是,對(duì)安全比較重視以及不需要持久化數(shù)據(jù)。
使用方式
--tmpfs 和 --mount 的關(guān)系與前面兩種方式的關(guān)系不用多說。那它們之間的差異是:
-
--tmpfs不允許指定任何可配置選項(xiàng)。 -
--tmpfs不能用語(yǔ) 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 通過運(yùn)行 docker container inspect tmptest 并查找 Mounts 部分來(lái)驗(yàn)證安裝是掛載:
"Tmpfs": {
"/app": ""
},
刪除容器:
$ docker container stop tmptest
$ Docker container rm tmptest
指定 tmpfs 選項(xiàng)
tmpfs 掛載允許兩個(gè)配置選項(xiàng),這兩個(gè)都是不需要的。如果您需要指定這些選項(xiàng),則必須使用該 --mount 標(biāo)志,因?yàn)樵?--tmpfs 標(biāo)志不支持它們。
| 選項(xiàng) | 描述 |
|---|---|
| tmpfs-size | tmpfs 的大小,以字節(jié)為單位。無(wú)限制默認(rèn)。 |
| tmpfs-mode | tmpfs 的八進(jìn)制文件模式。例如,700 或者 0770。默認(rèn)為 1777 或世界可寫。 |
以下示例將設(shè)置 tmpfs-mode 為 1770,以便在容器內(nèi)不可世界讀取。
docker run -d \
-it \
--name tmptest \
--mount type=tmpfs,destination=/app,tmpfs-mode=1770 \
nginx:latest