Docker系列05—Docker 存儲卷詳解

本文收錄在容器技術學習系列文章總目錄html

1、存儲卷介紹

1.1 背景

1docker AFUS 分層文件系統mysql

  docker鏡像由多個只讀層疊加面成,啓動容器時,docker加載只讀鏡像層並在鏡像棧頂部加一個讀寫層;nginx

  若是運行中的容器修改了現有的一個已經存在的文件,那該文件將會從讀寫層下面的只讀層複製到讀寫層,該文件版本仍然存在,只是已經被讀寫層中該文件的副本所隱藏,此即寫時複製(COW)」機制git

 

2)示意圖github

  描述:若是一個文件在最底層是可見的,若是在layer1上標記爲刪除,最高的層是用戶看到的Layer2的層,在layer0上的文件,在layer2上能夠刪除,可是隻是標記刪除,用戶是不可見的,總之在到達最頂層以前,把它標記來刪除,對於最上層的用戶是不可見的,當標記一刪除,只有用戶在最上層建一個同名同樣的文件,纔是可見的。web

 

1.2 爲何要使用存儲卷

  •  對於這類的操做,修改刪除等,通常效率很是低,若是對一於I/O要求比較高的應用,如redis在實現持化存儲時,是在底層存儲時的性能要求比較高。
  •  假設底層運行一個存儲庫mysql,mysql原本對於I/O的要求就比較高,若是mysql又是運行在容器中本身的文件系統之上時,也就是容器在中止時,就意味着刪除,其實現數據存取時效率比較低,要避免這個限制要使用存儲捲來實現。
  •  存在的問題:
    •  存儲於聯合文件系統中,不易於宿主機訪問;
    •  容器間數據共享不便
    •  刪除容器其數據會丟失

 

1.3 存儲卷

1)介紹redis

  「是容器上的一個或多個目錄,此類目錄可繞過聯合文件系統與宿主機上的某個目錄綁定(關聯)sql

  相似於掛載同樣,宿主機的/data/web目錄與容器中的/container/data/web目錄綁定關係,而後容器中的進程向這個目錄中寫數據時,是直接寫在宿主機的目錄上的,繞過容器文件系統與宿主機的文件系統創建關聯關係,使得能夠在宿主機和容器內共享數據庫內容,讓容器直接訪問宿主機中的內容,也能夠宿主機向容器供集內容,二者是同步的docker

  mount名稱空間原本是隔離的,可讓兩個原本是隔離的文件系統,在某個子路徑上創建必定程度的綁定關係,從而使得在兩個容器之間的文件系統的某個子路徑上再也不是隔離的,實現必定程度上共享的效果。數據庫

  在宿主機上可以被共享的目錄(能夠是文件)就被稱爲volume

 

2)存儲卷做用

  優勢是容器中進程所生成的數據,都保存在存儲捲上,從而脫離容器文件系統自身後,當容器被關閉甚至被刪除時,都不用擔憂數據被丟失,實現數據能夠脫離容器生命週期而持久,當再次重建容器時,若是可讓它使用到或者關聯到同一個存儲捲上時,再建立容器,雖然不是以前的容器,可是數據仍是那個數據,特別相似於進程的運行邏輯,進程自己不保存任何的數據,數據都在進程以外的文件系統上,或者是專業的存儲服務之上,因此進程每次中止,只是保存程序文件,對於容器也是同樣;容器就是一個有生命週期的動態對象來使用,容器關閉就是容器刪除的時候,可是它底層的鏡像文件仍是存在的,能夠基於鏡像再從新啓動容器。

  可是容器有一個問題,通常與進程的啓動不太同樣,就是容器啓動時選項比較多,若是下次再啓動時,很容器會忘記它啓動時的選項,因此最好有一個文件來保存容器的啓動,這就是容器編排工具的做用。通常狀況下,是使用命令來啓動操做docker,可是能夠經過文件來讀,也就讀文件來啓動,讀所須要的存儲卷等,可是它也只是操做一個容器,這也是須要專業的容器編排工具的緣由。

  另外一個優點就是容器就能夠不置於啓動在那臺主機之上了,如幾臺主機後面掛載一個NFS,在各自主機上建立容器,而容器上經過關聯到宿主機的某個目錄上,而這個目錄也是NFS所掛載的目錄中,這樣容器若是中止或者是刪除均可以不限制於只能在原先的宿主機上啓動才能夠,能夠實現全集羣範圍內調試容器的使用,當再分配存儲、計算資源時,就不會再侷限於單機之上,能夠在集羣範圍內創建起來,基本各類docker的編排工具都能實現此功能,可是後面嚴重依賴於共享存儲的使用。

 

3)配合各服務應用狀態分析

  考慮到容器應用是須要持久存儲數據的,多是有狀態的,若是考慮使用NFS作反向代理是不必存儲數據的,應用能夠分爲有狀態和無狀態,有狀態是當前此次鏈接請求處理必定此前的處理是有關聯的,無狀態是先後處理是沒有關聯關係的,大多數有狀態應用都是數據持久存儲的,如mysql,redis有狀態應用,在持久存儲,如nginx做爲反向代理是無狀態應用,tomcat能夠是有狀態的,可是它有可能不須要持久存儲數據,由於它的session都是保存在內存中就能夠的,會致使節點宕機而丟失session,若是有必要應該讓它持久,這也算是有狀態的。

  應用狀態象限:是否有狀態或無狀態,是否須要持久存儲,能夠定立一個正軸座標系,第一象限中是那些有狀態須要存儲的,像mysql,redis等服務,有些有有狀態可是無需進行存儲的,像tomcat把會話保存在內存中時,無狀態也無須要存儲的數據,如各類反向代理服務器nginx,lvs請求鏈接都是看成一個獨立的鏈接來調度,本地也不須要保存數據,第四象限是無狀態,可是須要存儲數據是比較少見。

  運維起來比較難的是有狀態且須要持久的,須要大量的運維經驗和大量的操做步驟才能操做起來的,如作一個Mysql主從須要運維知識、經驗整合進去才能實現所謂的部署,擴展或縮容,出現問題後修復,必需要了解集羣的規模有多大,有多少個主節點,有多少個從節點,主節點上有多少個庫,這些都要一清二楚,才能修復故障,這些就強依賴於運維經驗,無狀態的如nginx一安裝就能夠了,並不複雜,對於無狀態的應用能夠迅速的實現複製,在運維上實現自動化是很容易的,對於有狀態的現狀比較難脫離運維人員來管理,即便是k8s在使用上也暫時沒有成熟的工具來實現。

  總之:對於有狀態的應用的數據,不使用存儲卷,只能放在容器本地,效率比較低,而致使一個很嚴重問題就是沒法遷移使用,並且隨着容器生命週期的中止,還不能把它刪除,只能等待下次再啓動狀態才能夠,若是刪除了數據就可能沒了,由於它的可寫層是隨着容器的生命週期而存在的,因此只要持久存儲數據,存儲卷就是必需的

  docker存儲卷難度:對於docker存儲卷運行起來並不太麻煩,若是不本身藉助額外的體系來維護,它自己並無這麼強大,由於docker存儲卷是使用其所在的宿主機上的本地文件系統目錄,也就是宿主機有一塊磁盤,這塊磁盤並無共享給其餘的docker主要,而後容器所使用的目錄,只是關聯到宿主機磁盤上的某個目錄而已,也就是容器在這宿主機上中止或刪除,是能夠從新再建立的,可是不能調度到其餘的主機上,這也是docker自己沒有解決的問題,因此docker存儲卷默認就是docker所在主機的本地,可是本身搭建一個共享的NFS來存儲docker存儲的數據,也能夠實現,可是這個過程強依賴於運維人員的能力

 

1.4 存儲卷原理

  •  volume於容器初始化之時會建立,由base image提供的卷中的數據會於此期間完成複製
  •  volume的初意是獨立於容器的生命週期實現數據持久化,所以刪除容器之時既不會刪除卷,也不會對哪怕未被引用的卷作垃圾回收操做
  •  卷爲docker提供了獨立於容器的數據管理機制
    •  能夠把鏡像想像成靜態文件,例如程序,把卷類比爲動態內容,例如數據,因而,鏡像能夠重用,而卷能夠共享
    •  卷實現了程序(鏡像)"數據()「分離,以及程序(鏡像)「"製做鏡像的主機分離,用記製做鏡像時無須考慮鏡像運行在容器所在的主機的環境 

 

1.5 存儲卷分類

Docker有兩種類型的卷,每種類型都在容器中存在一個掛載點,但其在宿主機上位置有所不一樣;

  • Bind mount volume(綁定掛載卷):在宿主機上的路徑要人工的指定一個特定的路徑在容器中也須要指定一個特定的路徑兩個已知的路徑創建關聯關係
  • Docker-managed volumedocker管理卷): 只須要在容器內指定容器的掛載點是什麼,而被綁定宿主機下的那個目錄,是由容器引擎daemon自行建立一個空的目錄,或者使用一個已經存在的目錄,與存儲卷創建存儲關係,這種方式極大解脫用戶在使用卷時的耦合關係,缺陷是用戶沒法指定那些使用目錄,臨時存儲比較適合;

 

2、使用存儲卷

docker run 命令使用-v 選項便可使用volume

  •  docker-managed volume
docker run -it -name rbox1 -v /data busybox   #/data指定docker的目錄
docker inspect -f {{.Mounts}} rbox1   查看rbox1容器的卷,卷標識符及掛載的主機目錄
  •  bind-mount volume
docker run -it -v HOSTDIR:VOLUMEDIR --name rbox2 busybox  #宿主機目錄:容器目錄
docker inspect -f {{.Mounts}} rbox2

 

2.1 使用 docker-managed volume

1)建立容器b1

[root@along ~]# docker run --name b1 -it -v /data --rm busybox 
/ # ls /data/
/ #

注意:不要關閉此終端,另起一個終端進行一下操做;由於--rm 選項:一旦容器關閉,當即刪除容器

 

2)查詢存儲卷信息

[root@along ~]# docker inspect b1
... ...
        "Mounts": [
            {
                "Type": "volume",
                "Name": "ca18526588ba7cbe3934086807a95415644aec17119c811338efb1db2c5f5201",
                "Source": "/var/lib/docker/volumes/ca18526588ba7cbe3934086807a95415644aec17119c811338efb1db2c5f5201/_data",
                "Destination": "/data",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
... ...

 

3)由於inspect查詢的結果是列表的形式、因此能夠精確查詢結果

[root@along ~]# docker inspect -f {{.Mounts}} b1
[{volume ca18526588ba7cbe3934086807a95415644aec17119c811338efb1db2c5f5201 /var/lib/docker/volumes/ca18526588ba7cbe3934086807a95415644aec17119c811338efb1db2c5f5201/_data /data local  true }]

 

4)在宿主機的存儲卷目錄添加任意東西

[root@along ~]# cd /var/lib/docker/volumes/ca18526588ba7cbe3934086807a95415644aec17119c811338efb1db2c5f5201/_data
[root@along _data]# echo "hello" > test.html

 

5)在容器b1中查看,並在容器中任意修改存儲卷

/ # cat /data/test.html 
hello
/ # echo "world" >> /data/test.html 

 

6)在宿主機上查看認證

[root@along _data]# cat test.html 
hello
world

 

2.2 使用 docker mount volume

1)建立容器b2

[root@along ~]# docker run --name b2 -it -v /data/volumes/b2:/data --rm busybox 
/ # ls /data/
/ # 

注:若是設置存儲卷的目錄不存在,會自動建立

 

2)查詢存儲卷信息

[root@along ~]# docker inspect -f {{.Mounts}} b2
[{bind  /data/volumes/b2 /data/web/html   true rprivate}]

 

3)在宿主機的存儲捲上進行簡單操做

[root@along ~]# cd /data/volumes/b2/
[root@along b2]# echo "<h1>Bustbox httpd server</h1>" > index.html

 

4)在容器中驗證

/ # cat /data/index.html 
<h1>Bustbox httpd server</h1>

 

5)即便容器被刪除,再新建立容器b3,修改存儲卷路徑,存儲卷也不會改變,證實持久功能

[root@along ~]# docker run --name b3 -it -v /data/volumes/b2:/data/web/html --rm busybox 
/ # cat /data/web/html/index.html 
<h1>Bustbox httpd server</h1>

 

6)多個docker容器同時關聯到同一個宿主機的目錄中

實現共享使用同一個存儲卷,容器之間的數據共享

[root@along ~]# docker run --name b4 -it -v /data/volumes/b2:/data/ --rm busybox
/ # cat /data/index.html 
<h1>Bustbox httpd server</h1>

 

2.3 volumes-from 基於已有容器的存儲器,建立容器

1)先建立一個 infracon container

[root@along ~]# docker run --name infracon -it -v /data/infracon/volume/:/data/web/html busybox:latest 
/ # echo "<h1>Nginx server</h1>" > /data/web/html/index

宿主機的存儲卷能夠查詢

[root@along ~]# cat /data/infracon/volume/index.html   
<h1>Nginx server</h1>

 

2)基於infracon container 的存儲器,啓動一個 nginx container

[root@along ~]# docker run --name nginx --network container:infracon --volumes-from infracon -it --rm busybox:latest 
/ # cat /data/web/html/index.html 
<h1>Nginx server</h1>

其實,對nginx 這個容器來講,volume 的本質沒變,它只是將infracon 容器的/data/web/html 目錄映射的主機上的目錄映射到自身的/data/web/html 目錄。

[root@along ~]# docker inspect -f {{.Mounts}} nginx
[{bind  /data/infracon/volume /data/web/html   true rprivate}]

可是,其好處是,能夠無論其目錄的臨時性而不斷地重複使用它。

 

3Volume 刪除和孤單 volume 清理

3.1 在刪除容器時刪除 volume

可使用 docker rm -v 命令在刪除容器時刪除該容器的卷。

[root@along ~]#  docker run --name web2 -v /data/ -d nginx:1.14-alpine
59a3db695835a9f1a8be97c0ca0f70bc792f5303302264dba913c7c1b6d81ebd
[root@along ~]# docker volume ls
DRIVER              VOLUME NAME
local               17ac2071805d1609cf5501f81bec81d3d19467ea5a0c3428d2e77b414607775b
local               1e28bac2454d8c92ba39e8e22b9d88004284310a776e50dc379282de63c0e149
[root@along ~]# docker kill web2
web2
[root@along ~]# docker rm -v web2
web2
[root@along ~]# docker volume ls
DRIVER              VOLUME NAME
local               1e28bac2454d8c92ba39e8e22b9d88004284310a776e50dc379282de63c0e149

 

3.2 批量刪除孤單 volumes

從上面的介紹能夠看出,使用 docker run -v 啓動的容器被刪除之後,在主機上會遺留下來孤單的卷。可使用下面的簡單方法來作清理:

[root@along ~]# docker volume ls -qf dangling=true
1e28bac2454d8c92ba39e8e22b9d88004284310a776e50dc379282de63c0e149
[root@along ~]# docker volume rm $(docker volume ls -qf dangling=true)
1e28bac2454d8c92ba39e8e22b9d88004284310a776e50dc379282de63c0e149
[root@along ~]# docker volume ls
DRIVER              VOLUME NAME

 

3.3 github 上有不少腳本能夠自動化地清理孤單卷

好比:

相關文章
相關標籤/搜索