目錄html
咱們都知道 Docker 的數據能夠存在容器的可寫層,可是也存在如下幾點不足:mysql
Docker 提供了三種不一樣的方式將數據從 Docker Host 掛載到 Docker 容器,並實現數據的讀取和存儲:volumes、bind mounts、tmpfs 。nginx
不管咱們選擇使用哪一種類型的掛載,數據從容器中看起來都同樣的,它在容器的文件系統中做爲目錄或單個文件展現。sql
咱們能夠經過數據存儲在 Docker Host 的方式來簡單的瞭解這三種掛載方式的不一樣,以下圖:docker
全部說使用 volumes 是咱們很是推薦的一種方式。ubuntu
相對於 volume,bind mount 具備有限的功能。咱們使用 bind mount 時,host 上的文件或目錄被掛載到容器中。掛載時須要咱們指定文件或目錄在 host 上的完整路徑。安全
bind mount 是很是高效的,但它依賴 host 的文件系統的目錄結構。若是打算部署新的 Docker 應用,咱們能夠考慮使用 volume 而命名,否則你不能使用 Docker CLI 命令直接管理 bind mount。bash
使用 bind mount 的一個缺點是,咱們能夠經過在容器中運行的進程更改 host 的文件系統,包括建立、修改或刪除重要的系統文件或目錄。這會嚴重影響系統的安全,甚至影響 host 上面非 Docker 的進程。服務器
以前咱們使用 bind mount 可使用-v
或者--volume
,這個參數在單容器的狀況下使用,在 swarm 集羣中使用--mount
,從 Docker 17.06 以後,咱們能夠統一使用參數--mount
。curl
對於新接觸 Docker 的咱們來講建議使用--mount
,老司機能夠繼續使用-v
,可是咱們仍是建議使用--mount
。
它有三部分組成,使用:
進行分割,這些字段必須以正確的順序排列,而且每一個字段的含義不明顯。
它由一組鍵值對組成,由,
進行分割,每一個值爲 <key>=<value>
。
Key | Value |
---|---|
type | bind、volume、tmpfs,如不指定,默認是 volume |
source/src | Docker Host 上的一個文件或者目錄 |
destination/dst/target | 被掛載容器上的一個文件或者目錄 |
readonly | 沒有參數,只寫這個詞便可 |
bind-propagation | rprivate、private、rshared、shared、rslave、slave |
consistency | consistent、delegated、cached,只在 Mac 系統上生效 |
使用-v
的時候,若是在 Docker Host 不存在要掛載的文件或者目錄,Docker 將會自動進行建立,一般是一個目錄。
使用--mount
的時候,若是在 Docker Host 不存在要掛載的文件或者目錄,Docker 不會自動建立目錄,並生成一個錯誤。
好比咱們想把 Docker Host 的目錄source/html/
掛載到 nginx 容器的/usr/share/nginx/html/
,咱們對 html 目錄的更改但願能夠馬上在容器 html 目錄生效,這樣咱們就能夠很是方便的修改網頁的文件了。
docker run -d \ -it \ --name devtest \ -p 80:80 \ --mount type=bind,source="$(pwd)"/html,target=/usr/share/nginx/html/ \ nginx:latest
使用命令docker inspect devtest
來查看掛載是否正確掛載。
"Mounts": [ { "Type": "bind", "Source": "/root/sample/html", "Destination": "/usr/share/nginx/html", "Mode": "", "RW": true, "Propagation": "rprivate" } ],
從裏面能夠出這是一個 bind mount,而且是隻讀掛載。
咱們在 html 目錄下建立一個 index.html,而且寫入內容爲「This is a bind mount test!」。而且訪問本地的 80 端口查看結果。
從這裏例子之中咱們能夠看出若是咱們掛載到容器的目錄中有文件,文件會被咱們的源地址文件進行覆蓋。
咱們把容器銷燬掉,查看一下咱們建立的文件是否還存在。
docker stop devtest docker rm devtest
咱們發現文件仍是存在的,可見,即便容器沒有了,bind mount 也還在。這也合理,bind mount 是 host 文件系統中的數據,只是借給容器用用,哪能隨便就刪了啊。
另外,bind mount 時還能夠指定數據的讀寫權限,默認是可讀可寫,可指定爲只讀:
docker run -d \ -it \ --name devtest \ -p 80:80 \ --mount type=bind,source="$(pwd)"/html,target=/usr/share/nginx/html/,readonly\ nginx:latest
查看掛載詳情,看看是否是隻讀模式。
"Mounts": [ { "Type": "bind", "Source": "/root/sample/html", "Destination": "/usr/share/nginx/html", "Mode": "", "RW": false, "Propagation": "rprivate" } ],
咱們命令docker exec -ti devtest bash
進入到容器內部,修改文件測試一下。
readonly 設置了只讀權限,在容器中是沒法對 bind mount 數據進行修改的。只有 host 有權修改數據,提升了安全性。
除了制定目錄外,咱們也能夠指定單個文件進行覆蓋,以下:
docker run -d \ -it \ --name devtest \ -p 80:80 \ --mount type=bind,source="$(pwd)"/html/index.html,target=/usr/share/nginx/html/index.html \ nginx:latest
Volume 徹底由 Docker 來進行管理,好比 volume 的建立,咱們可使用命令 docker volume create
來簡單的建立一個 volume,當容器或者服務建立的時候,Docker 也能夠自動的建立一個 volume。
當咱們建立了一個 volume,它存儲在 Docker Host 的存儲目錄下。當咱們把 volume 掛載入容器時,此目錄就是掛載到容器中的目錄。這相似於 bind mount 的工做方式,不一樣的是 volume 是由 Docker 來管理而且和 Docker Host 的核心功能進行隔離。
一個給定的 volume 能夠同時掛載到多個容器中。當沒有容器的使用 volume 時, volume 對 Docker 仍然是可用的而且不會被自動刪除。咱們可使用命令docker volume prune
來刪除一個已經不使用的 volume。
咱們在掛載 volume 時,能夠對其命名,也能夠是默認隨機生成的名字。若是咱們沒有指定名稱,當 volume 第一次掛載到一個容器時,Docker 會用一個隨機字符串對其進行命名,這樣能夠保證 volume 在 Docker Host 的惟一性。
Volume 還支持使用 volume drivers,它容許您將數據存儲掛載到遠程主機或雲提供商上等。
Volumes 對比 bind mounts 具有如下幾點有點:
咱們推薦使用--mount
,全部這裏咱們只寫它的使用方法。
Key | Value |
---|---|
type | bind、volume、tmpfs ,如不指定,默認是 volume |
source/src | Docker Host 上的一個文件或者目錄 |
destination/dst/target | 被掛載容器上的一個文件或者目錄 |
readonly | 沒有參數,只寫這個詞便可 |
volume-opt | 能夠指定更多的附加參數 |
使用容器技術,volume 是最推薦的一種持久存儲數據的方式。volume 的一些使用場景以下:
/var/lib/docker/volumes/<volume-name>
下。不像 bind mount,咱們首先須要建立一個 volume。
docker volume create my_vol
列出 volumes:
root@ubuntu:~# docker volume ls DRIVER VOLUME NAME local my_vol
查看指定卷的詳細信息:
root@ubuntu:~# docker volume inspect my_vol [ { "CreatedAt": "2017-12-07T10:27:26+08:00", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/my_vol/_data", "Name": "my_vol", "Options": {}, "Scope": "local" } ]
刪除卷:
docker volume rm my_vol
咱們查看一下剛剛建立的 volume 裏面是否有數據
root@ubuntu:~# ll /var/lib/docker/volumes/my_vol/_data total 8 drwxr-xr-x 2 root root 4096 Dec 7 10:27 ./ drwxr-xr-x 3 root root 4096 Dec 7 10:27 ../
咱們看到裏面並無數據,那咱們啓動容器查看一下。
docker run -d \ -it \ -p 80:80 \ --name devtest \ --mount source=my_vol,target=/usr/share/nginx/html \ nginx:latest
使用命令docker inspect devtest
查看一下掛載詳情。
"Mounts": [ { "Type": "volume", "Name": "my_vol", "Source": "/var/lib/docker/volumes/my_vol/_data", "Destination": "/usr/share/nginx/html", "Driver": "local", "Mode": "z", "RW": true, "Propagation": "" } ],
咱們再次查看一下 Source 目錄。
root@ubuntu:~# ll /var/lib/docker/volumes/my_vol/_data total 16 drwxr-xr-x 2 root root 4096 Dec 7 09:33 ./ drwxr-xr-x 3 root root 4096 Dec 6 20:18 ../ -rw-r--r-- 1 root root 537 Nov 21 22:28 50x.html -rw-r--r-- 1 root root 612 Nov 21 22:28 index.html
咱們能夠看到 volume 的內容跟容器原有 /usr/share/nginx/html 徹底同樣,由於咱們掛載的 volume 是剛剛建立沒有數據的,容器原有的數據會被複制到 volume 中,咱們一樣的能夠對其進行修改操做,直接反映到容器中。
咱們刪掉容器查看一下 volume 的數據是否被刪除。
docker stop devtest docker rm devtest
再次進行查看。
root@ubuntu:~# ll /var/lib/docker/volumes/my_vol/_data total 16 drwxr-xr-x 2 root root 4096 Dec 7 10:13 ./ drwxr-xr-x 3 root root 4096 Dec 6 20:18 ../ -rw-r--r-- 1 root root 537 Nov 21 22:28 50x.html -rw-r--r-- 1 root root 31 Dec 7 10:13 index.html
咱們能夠看到,數據沒有被刪除。
咱們接着上個 volume 進行測試,咱們知道里面已經存在文件 index.html,咱們更改裏面的內容,而且把 50x.html 刪掉。
root@ubuntu:~# ll /var/lib/docker/volumes/my_vol/_data total 12 drwxr-xr-x 2 root root 4096 Dec 7 10:16 ./ drwxr-xr-x 3 root root 4096 Dec 6 20:18 ../ -rw-r--r-- 1 root root 31 Dec 7 10:13 index.html root@ubuntu:~# cat /var/lib/docker/volumes/my_vol/_data/index.html This is a volume mount test!
確認好 volume 以後,咱們啓動容器。
docker run -d \ -it \ -p 80:80 \ --name devtest \ --mount source=my_vol,target=/usr/share/nginx/html \ nginx:latest
而後訪問一下容器。
root@ubuntu:~# curl localhost This is a volume mount test!
咱們能夠看到,當咱們的 volume 裏面有數據的時候,容器內的數據就被 volume 覆蓋了,一樣的,當咱們刪除容器以後,volume 裏面的數據會依然存在的。
以前的狀況都說咱們提早建立好 volume 進行掛載的,此次咱們不提早建立,直接指定,看看會出現什麼狀況。
docker run -d \ -it \ -p 80:80 \ --name devtest \ --mount source=my_vol2,target=/usr/share/nginx/html \ nginx:latest
咱們能夠看到,也建立成功了,此次咱們對 volume 的命名爲 my_vol2。
咱們使用名稱查看一下 volume 的狀況。
root@ubuntu:~# docker volume ls DRIVER VOLUME NAME local 15a731acddee296080b56ddd5faf27748bdfbc422ce2e6b9574ca755e878f434 local aaf128a2672ffe8994bd080c83bcd4540796a47fd404a8c85e2e1f48f3086855 local e157aff9c4db4bcf935484b99f119dbe8faeac2de6408197f25b6f1ea798975c local my_vol local my_vol2
咱們能夠看到 Docker 給咱們自動建立了一個 volume,那咱們使用docker inspect devtest
查看一下掛載詳情。
"Mounts": [ { "Type": "volume", "Name": "my_vol2", "Source": "/var/lib/docker/volumes/my_vol2/_data", "Destination": "/usr/share/nginx/html", "Driver": "local", "Mode": "z", "RW": true, "Propagation": "" } ],
咱們猜想,這種狀況應該和第一種空 volume 掛載相似,volume 裏面的內容應該是容器複製過來的,咱們查看一下是否這樣的。
root@ubuntu:~# ll /var/lib/docker/volumes/my_vol2/_data total 16 drwxr-xr-x 2 root root 4096 Dec 7 10:38 ./ drwxr-xr-x 3 root root 4096 Dec 7 10:38 ../ -rw-r--r-- 1 root root 537 Nov 21 22:28 50x.html -rw-r--r-- 1 root root 612 Nov 21 22:28 index.html
狀況確實如咱們所說,從這裏咱們能夠看出,若是使用 bind mount,咱們的源目錄必須存在,否則docker 會報錯,然而咱們使用 volume,若是源不存在,docker 會爲咱們進行建立。
這是由於 bind mount 掛載的路徑並非 docker 進行管理的,他沒有權限隨便建立目錄,而後 volume 是 docker 進行管理的,它能夠在本身的存儲目錄下面建立 volume。
當咱們想把容器內的數據導出來時,使用這種方式很是方便。
在某些狀況下,咱們使用多容器進行掛載的時候,咱們不容許容器對 volume 裏面的數據進行修改,這樣能夠保證全部的容器掛載的是相同的 volume。
docker run -d \ -it \ -p 80:80 \ --name=nginxtest \ --mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \ nginx:latest
使用命令docker inspect nginxtest
查看一下掛載狀況。
"Mounts": [ { "Type": "volume", "Name": "nginx-vol", "Source": "/var/lib/docker/volumes/nginx-vol/_data", "Destination": "/usr/share/nginx/html", "Driver": "local", "Mode": "z", "RW": false, "Propagation": "" } ],
一樣的咱們能夠看到容器的數據被複制到了 volume 裏面,咱們進入容器,修改文件看看。
root@ubuntu:~# docker exec -ti nginxtest bash root@28f1d32e08be:/# echo "nginxtest" > /usr/share/nginx/html/index.html bash: /usr/share/nginx/html/index.html: Read-only file system
能夠看到,容器內部是不能修改 volume 裏面的數據的。
到這裏咱們簡單對比一下 bind mount 和 volume 的不一樣點。
不一樣點 | bind mount | volume |
---|---|---|
Source位置 | 能夠任意指定 | /var/lib/docker/volumes/... |
空 source | 覆蓋掉容器的內容 | 容器內數據複製到 volume |
是否支持單個文件 | 支持 | 不支持,只能是目錄 |
權限控制 | 讀寫或者只讀 | 讀寫或者只讀 |
移植性 | 弱,與 host path 綁定 | 強,無需指定 host 目錄 |
tmpfs 不在磁盤上持久存儲,也不在 Docker Host 容器裏面存儲,他存儲在 host 的內存中,它能夠在容器的整個生命週期內被容器所使用。
當你不須要持久保留數據在 host 或容器內。這多是出於安全緣由,或者是提高容器的性能,好比咱們的程序須要寫入不少不須要存儲的狀態數據時,咱們就會使用 tmpfs。
一樣的,咱們能夠在單容器的狀況下使用--tmpfs
,而且不能指定參數,在集羣的狀況下使用--mount
,能夠指定一些參數,具體以下:
Key | Value |
---|---|
type | bind、volume、tmpfs,如不指定,默認爲 volume |
destination/dst/target | 容器中的路徑 |
tmpfs-type/tmpfs-mode | 一些附加參數 |
使用 tmpfs 啓動容器。
docker run -d \ -it \ -p 80:80 \ --name tmptest \ --mount type=tmpfs,destination=/usr/share/nginx/html \ nginx:latest
容器對目錄全部的讀寫操做都在內存中。
咱們也能夠指定 tmpfs 的權限狀況。
docker run -d \ -it \ -p 80:80 \ --name tmptest \ --mount type=tmpfs,destination=/usr/share/nginx/html,tmpfs-mode=1770 \ nginx:latest
參考文檔:https://docs.docker.com/engine/admin/volumes/