目錄
一、Docker數據存儲簡介
我們知道Docker的數據可以存在容器的可寫層。但是當容器當容器不再運行時,數據不能持久存儲,我們無法從容器外部獲得這些數據;並且,Docker的可寫層與宿主機(Host)在容器運行時緊密耦合,我們不能輕易地把數據移動到別的地方。
Docker提供了三種方式,把數據從Host掛載到Docker容器中,並實現持久化存儲。這三種方式分別是Bind mounts, Volumes,Tmpfs.
- Bind mounts:可以實現將Host中的任意路徑掛載到容器中,可以存儲在任意位置,非Docker的進程或者容器可能隨時對其進行修改,存在一定的風險。
- Volumes:實現將Docker中的某一個volume掛載到容器中,存儲在Docker Host文件系統的某一個路徑下,默認是/var/lib/docker/volumes,由Docker進行管理,非Docker的進程不能修改該路徑下的文件,比較安全。
- Tmpfs:存儲在Host系統的內存中,沒有在磁盤上進行存儲,不能實現持久化。
二、Bind mounts
1.簡介
Bind mounts可以將Host上的任意路徑進行掛載,包括重要的文件系統路徑,因此就存在一個缺點,我們可以通過容器更改Host的文件系統,進行文件的增刪改等操作,若我們將文件系統的路徑進行掛載時,這會嚴重影響到系統的安全性,包括那些非Docker進程。
2.如何使用
在單容器情況下,使用Bind mount可以用 -v 或者 --volume,在swarm集羣中,我們使用 --mount,在Docker17.06之後,可以統一使用 --mount,這也是非常推薦使用的。
-v 或者 --volume語法
它有三部分組成,使用 :
進行分割,這些字段必須以正確的順序排列,並且每個字段的含義不明顯。
- 第一個字段是 Docker Host 上的一個文件或者目錄。
- 第二個字段是將要掛載到容器上的一個文件或者目錄。
- 第三個字段是可選的,用來增加一些附加選項,比如 ro,consistent,delegated,cached,z,and Z。
--mount語法
它由一組鍵值對組成,由,
進行分割
Key | Value |
---|---|
type | bind、volume、tmpfs,如不指定,默認是 volume |
source或者src | Docker Host 上的一個文件或者目錄 |
destination或者dst或者target | 被掛載容器上的一個文件或者目錄 |
readonly | 默認可讀可寫,加上readonly表示是隻讀的文件系統 |
bind-propagation | rprivate、private、rshared、shared、rslave、slave |
兩者的區別
使用-v
的時候,如果在 Docker Host 不存在要掛載的文件或者目錄,Docker 將會自動進行創建,通常是一個目錄。
使用--mount
的時候,如果在 Docker Host 不存在要掛載的文件或者目錄,Docker 不會自動創建目錄,並生成一個錯誤
3.使用場景
- 把 host 中的配置文件共享給 host 上面的容器。容器爲什麼自帶 DNS 解析呢,那是因爲默認情況下 host 把 /etc/resolv.conf 掛載到它上面的容器裏面。
- 在 Docker Host 上面的開發環境和容器直接共享程序的源代碼或者構建要素。例如,你可以掛載一個 Maven 目錄到一個容器中,每當你在 Docker Host 重新建立 maven 項目,容器都可以直接獲取你重新構建的 maven 項目。
- 我們可以將源代碼目錄掛載到容器中,在 host 中修改代碼就能看到應用的實時效果。
- 將 mysql 容器的數據放在 bind mount 裏,這樣 host 可以方便地備份和遷移數據。
- 只需要向容器添加文件,不希望覆蓋整個目錄。
4.動手實踐
在下面的例子中,我們使用nginx鏡像來創建容器,進行舉例說明,若沒有該鏡像先獲取,默認使用latest標籤。
docker pull nginx
在Host上創建目錄(因爲通過--mount進行掛載,要求我們在Host上先要有這個目錄)
mkdir /root/sample/html
運行容器
docker run -d -it --name devtest -p 80:80 --mount type=bind,source=/root/sample/html,target=/usr/share/nginx/html nginx:latest
使用docker inspect xx 查看容器是否正確掛載,Source爲宿主機上的路徑,Destination爲容器上的路徑,RW表示是可讀可寫的模式,也是默認的模式。
docker inspect devtest
進入到容器中可以看到目錄爲空(因爲Host中的相應的路徑下沒有內容)
docker exec -it devtext bash
在html目錄下創建文件index.html並寫入內容:This is a bind mount test!
vim index.html
可以通過暫停容器、銷燬容器驗證文件是否還存在。從下面的圖中我們可以看到,關閉容器後,便不能訪問80端口了;然而在移除容器之後,index.html還在容器中,因此可以實現持久化。
docker stop devtest
docker rm devtest
以上爲默認的讀寫方式運行容器。
若我們僅僅是想讀取Host上的文件,而不想修改其中的內容,我們可以將默認的讀寫模式修改爲只讀模式:
docker run -d -it --name devtest -p 80:80 --mount type=bind,source=/root/sample/html,target=/usr/share/nginx/html,readonly nginx:latest
然後我們進入容器測試一下
docker exec -it devtest bash
echo "readonly test" > /usr/share/nginx/html/index.html
在外部修改文件進行測試:
echo "Host write" >> /root/sample/html/index.html
cat /root/sample/html/index.html
內部查看
最後還有一種單文件掛載的方式,但意義不是很大。
docker run -d -it --name devtest -p 80:80 --mount type=bind,source=/root/sample/html/index.html,target=/usr/share/nginx/html/index.html nginx:latest
三、Volume
1.詳細介紹
- Volume 完全由 Docker 來進行管理,比如 volume 的創建,我們可以使用命令
docker volume create
來簡單的創建一個 volume,當容器或者服務創建的時候,Docker 也可以自動的創建一個 volume。- 當我們創建了一個 volume,它存儲在 Docker Host 的存儲目錄下,並有Docker進行管理。
- 一個給定的 volume 可以同時掛載到多個容器中。當沒有容器使用 volume 時, volume 對 Docker 仍然是可用的並且不會被自動刪除,使用docker volume rm 可以對volume進行刪除
- 我們在掛載 volume 時,可以對其命名,也可以是默認隨機生成的名字。如果我們沒有指定名稱,當 volume 第一次掛載到一個容器時,Docker 會用一個隨機字符串對其進行命名,這樣可以保證 volume 在 Docker Host 的唯一性。
- Volume 還支持使用 volume drivers,它允許您將數據存儲掛載到遠程主機或雲提供商上等。
volume對比bind方式的優點如下:
- Volumes 的備份和遷移更加容易。
- 可以使用 Docker CLI 或者 Docker API 管理 volumes。
- Volumes 既可以在 Linux 的容器中使用,也可以在 Windows 的容器中使用。
- Volumes 在多容器中共享更加的安全。
- Volume drivers 允許我們把數據存儲在遠程主或雲提供商。
2.使用場景
- 當我們需要在多個正在運行的容器之間共享數據時,我們需要volume 。如果我們沒有明確指定創建它,那麼它第一次裝入容器時就會創建一個 volume。當容器停止或刪除掉,volume 仍然存在。多個容器可以同時讀寫一個 volume。只有當我們明確指定要刪除某個 volume 時,它纔會被刪除。
- 當我們需要把容器的數據永久存儲在一個遠程主機或者一個雲服務器上,我們需要 volume。
- 當我們的 Docker Host 無法保證可以提供一個目錄或者文件來作爲數據存儲時,我們也需要 volume,它可以減少我們對配置文件的依賴。
- 當我們需要備份數據,或者恢復數據,以及需要把數據從一個 Docker Host 遷移到另外一個 Docker Host 的時候,volume 是我們最好的一個選擇,我們可以停掉正在使用 volume 的容器,然後把 volume 的目錄備份下來即可,volume 的目錄一般在
/var/lib/docker/volumes/
下。
3.使用舉例
在使用volume進行持久化時,我們先要創建一個volume,可以通過以下的命令查看volume的具體信息:
docker volume create my_vol
docker volume inspect my_vol
我們剛剛創建了一個名爲my_vol的volume,可以進入到以上的路勁中查看,可以看到是一個空的卷,然後我們用這個空的volume啓動一個容器。
ll /var/lib/docker/volumes/my_vol/_data
docker run -d -it --name devtest -p 80:80 --mount source=my_vol,target=/usr/share/nginx/html nginx:latest
通過docker inspect devtest查看容器的詳細情況。
然後,我們再次查看Host上的volume所在的文件夾,發現多了兩個文件,因爲我們用一個空的volume來啓動容器時,會將容器中的內容複製到我們的Host上。
ll /var/lib/docker/volumes/my_vol/_data
訪問80端口,可以看到能訪問到nginx的歡迎頁
curl localhost
刪除容器,可以看到volume仍然還在。
下面我們用有數據的volume來啓動容器,看看會有什麼效果。我們對my_vol裏面的index.html進行修改,並刪除50x.html
echo "This is a volume mount test" > /var/lib/docker/volumes/my_vol/_data/index.html
rm -f 50x.html
然後啓動容器,並直接訪問容器
docker run -d -it --name devtest -p 80:80 --mount source=my_vol,target=/usr/share/nginx/html nginx:latest
我們可以看到,用一個現有的volume也是可以進行掛載的,並且將文件掛載到容器中。
最後,我們看一下沒有提前創建volume,會是什麼情況。
我們直接啓動容器,其中的source=my_vol2,這個volume在我們的Host上並沒有提前創建。
docker run -d -it --name devtest -p 80:80 --mount source=my_vol2,target=/usr/share/nginx/html nginx:latest
然後我們查看volume的情況
docker volume ls
docker volume inspect my_vol
可以看到自動給我們創建了一個名爲my_vol2的卷,並且其存放位置也是/var/lib/docker/volumes,因此也是受到docker管理的。
查看數據情況,能看到將容器中的文件直接複製到了Host上。
ll /var/lib/docker/volumes/my_vol2/_data
和bind mount方式相同,若我們需要將讀寫模式改爲只讀模式,只需加上readonly標識符即可。
docker run -d -it --name devtest -p 80:80 --mount source=my_vol2,target=/usr/share/nginx/html,readonly nginx:latest
下面對bind mount和volume兩種方式做一個對比
不同點 | bind mount | volume |
---|---|---|
Source位置 | 可以任意指定 | /var/lib/docker/volumes/... |
Host源地址爲空 | 覆蓋掉容器的內容 | 容器內數據複製到 volume |
是否支持單個文件 | 支持 | 不支持,只能是目錄 |
權限控制 | 讀寫或者只讀 | 讀寫或者只讀 |
移植性 | 弱,與 host path 綁定 | 強,無需指定 host 目錄 |
四、Tmpfs
Tmpfs方式將數據存儲在Host內存中,在容器的整個生命週期內被容器使用,不能持久化。出於安全原因,或者是提升容器的性能,比如我們的程序需要寫入很多不需要存儲的狀態數據時,我們就會使用 tmpfs。
使用的語法和以上兩者類似,主要是將type指定爲tmpfs
Key | Value |
---|---|
type | bind、volume、tmpfs,如不指定,默認爲 volume |
destination(或者dst/target) | 容器中的路徑 |
tmpfs-type(或者tmpfs-mode) | 附加參數 |
使用舉例
docker run -d -it --name devtest -p 80:80 --mount type=tmpfs,destination=/usr/share/nginx/html nginx:latest
docker inspect devtest
參考:https://docs.docker.com/storage/
參考:https://blog.51cto.com/wzlinux/2047637