Docker 持久存儲介紹(十三)

1、Docker 數據存儲

咱們都知道 Docker 的數據能夠存在容器的可寫層,可是也存在如下幾點不足:mysql

  1. 當該容器再也不運行時,數據將不會持久存儲,若是另外一個進程須要它,就很難將數據從容器中取出。
  2. 容器的可寫層與 Docker Host 在容器運行時緊密耦合,你不能輕易地把數據移到別的地方。
  3. 寫入容器的可寫層須要一個 storage driver 來管理。storage driver 使用 Linux 內核提供一個文件系統。與使用直接寫入宿主文件系統的 volume 相比,這種額外的抽象下降了性能。

Docker 提供了三種不一樣的方式將數據從 Docker Host 掛載到 Docker 容器,並實現數據的讀取和存儲:volumes、bind mounts、tmpfs 。nginx

不管咱們選擇使用哪一種類型的掛載,數據從容器中看起來都同樣的,它在容器的文件系統中做爲目錄或單個文件展現。sql

咱們能夠經過數據存儲在 Docker Host 的方式來簡單的瞭解這三種掛載方式的不一樣,以下圖:docker

  • Volumes 存儲在 Docker Host 文件系統的一個路徑下,這個路徑是由 Docker 來進行管理,路徑默認是 /var/lib/docker/volumes/,非 Docker 的進程不能去修改這個路徑下面的文件,因此說 Volumes 是持久存儲數據最好的一種方式。
  • Bind mounts 能夠存儲在 Docker Host 文件系統的任何位置,它們甚至多是重要的系統文件或目錄,非 Docker 的進程或者 Docker 容器可能隨時對其進行修改,存在潛在的安全風險。
  • Tmpfs 只存儲在 Docker Host 的系統內存中,不會寫入到系統的文件系統中,不會持久存儲。

全部說使用 volumes 是咱們很是推薦的一種方式。ubuntu

2、Bind mount

一、詳細介紹

相對於 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 以後,咱們能夠統一使用參數--mountcurl

對於新接觸 Docker 的咱們來講建議使用--mount,老司機能夠繼續使用-v,可是咱們仍是建議使用--mount

-v or --volume 語法

它有三部分組成,使用:進行分割,這些字段必須以正確的順序排列,而且每一個字段的含義不明顯。

  • 第一個字段是 Docker Host 上的一個文件或者目錄。
  • 第二個字段是將要掛載到容器上的一個文件或者目錄。
  • 第三個字段是可選的,用來增長一些附加選項,好比 ro,consistent,delegated,cached,z,and Z。

--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 不會自動建立目錄,並生成一個錯誤。

三、使用場景

  1. 把 host 中的配置文件共享給 host 上面的容器。容器爲何自帶 DNS 解析呢,那是由於默認狀況下 host 把 /etc/resolv.conf 掛載到它上面的容器裏面。
  2. 在 Docker Host 上面的開發環境和容器直接共享程序的源代碼或者構建要素。例如,你能夠掛載一個 Maven 目錄到一個容器中,每當你在 Docker Host 從新創建 maven 項目,容器均可以直接獲取你從新構建的 maven 項目。
  3. 咱們能夠將源代碼目錄 mount 到容器中,在 host 中修改代碼就能看到應用的實時效果。
  4. 將 mysql 容器的數據放在 bind mount 裏,這樣 host 能夠方便地備份和遷移數據。
  5. 只須要向容器添加文件,不但願覆蓋整個目錄。

四、使用案例

存在目錄 bind mount

好比咱們想把 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

3、Volume

一、詳細介紹

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 具有如下幾點有點:

  • Volumes 的備份和遷移更加容易。
  • 可使用 Docker CLI 或者 Docker API 管理 volumes。
  • Volumes 既能夠在 Linux 的容器中使用,也能夠在 Windows 的容器中使用。
  • Volumes 在多容器中共享更加的安全。
  • Volume drivers 容許咱們把數據存儲在遠程主或雲提供商。

二、使用語法

咱們推薦使用--mount,全部這裏咱們只寫它的使用方法。

Key Value
type bind、volume、tmpfs ,如不指定,默認是 volume
source/src Docker Host 上的一個文件或者目錄
destination/dst/target 被掛載容器上的一個文件或者目錄
readonly 沒有參數,只寫這個詞便可
volume-opt 能夠指定更多的附加參數

三、使用場景

使用容器技術,volume 是最推薦的一種持久存儲數據的方式。volume 的一些使用場景以下:

  • 當咱們須要在多個正在運行的容器之間共享數據時,咱們須要volume 。若是咱們沒有明確指定建立它,那麼它第一次裝入容器時就會建立一個 volume。當容器中止或刪除掉,volume 仍然存在。多個容器能夠同時讀寫一個 volume。只有當咱們明確指定要刪除某個 volume 時,它纔會被刪除。
  • 當咱們須要把容器的數據永久存儲在一個遠程主機或者一個雲服務器上,咱們須要 volume。
  • 當咱們的 Docker Host 沒法保證能夠提供一個目錄或者文件來做爲數據存儲時,咱們也須要 volume,它能夠減小咱們對配置文件的依賴。
  • 當咱們須要備份數據,或者恢復數據,以及須要把數據從一個 Docker Host 遷移到另一個 Docker Host 的時候,volume 是咱們最好的一個選擇,咱們能夠停掉正在使用 volume 的容器,而後把 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 啓動容器

咱們查看一下剛剛建立的 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 啓動容器

咱們接着上個 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 啓動容器

以前的狀況都說咱們提早建立好 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 裏面的數據進行修改,這樣能夠保證全部的容器掛載的是相同的 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 目錄

4、tmpfs

一、詳細介紹

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/

相關文章
相關標籤/搜索