1、存儲卷介紹
1.1 背景
(1)Docker的AUFS分層文件系統
Docker鏡像由多個只讀層疊加而成,啟動容器時,docker會加載只讀鏡像層並在鏡像棧頂部加一個讀寫層;
如果運行中的容器修改了現有的一個已經存在的文件,那該文件將會從讀寫層下面的只讀層復制到讀寫層,該文件版本仍然存在,只是已經被讀寫層中該文件的副本所隱藏,此即“寫時復制(COW)”機制。
描述:如果一個文件在最底層是可見的,如果在layer1上標記為刪除,最高的層是用戶看到的Layer2的層,在layer0上的文件,在layer2上可以刪除,但是只是標記刪除,用戶是不可見的,總之在到達最頂層之前,把它標記來刪除,對於最上層的用戶是不可見的,當標記一刪除,只有用戶在最上層建一個同名一樣的文件,才是可見的。
1.2 為什么要使用存儲卷
- 對於這類的操作,修改刪除等,一般效率非常低,如果對一於I/O要求比較高的應用,如redis在實現持化存儲時,是在底層存儲時的性能要求比較高。
- 假設底層運行一個存儲庫mysql,mysql本來對於I/O的要求就比較高,如果mysql又是運行在容器中自己的文件系統之上時,也就是容器在停止時,就意味着刪除,其實現數據存取時效率比較低,要避免這個限制要使用存儲卷來實現。
- 存在的問題:
- 存儲於聯合文件系統中,不易於宿主機訪問;
- 容器間數據共享不便
- 刪除容器其數據會丟失
- 解決方案:"卷(volume)"
"卷"是容器上的一個或多個"目錄";此類目錄可繞過聯合文件系統,與宿主機上的某目錄"綁定(關聯)"
1.3 存儲卷
(1)介紹
“卷”是容器上的一個或多個“目錄”,此類目錄可繞過聯合文件系統,與宿主機上的某個目錄“綁定(關聯)”;
類似於掛載一樣,宿主機的/data/web目錄與容器中的/container/data/web目錄綁定關系,然后容器中的進程向這個目錄中寫數據時,是直接寫在宿主機的目錄上的,繞過容器文件系統與宿主機的文件系統建立關聯關系,使得可以在宿主機和容器內共享數據庫內容,讓容器直接訪問宿主機中的內容,也可以宿主機向容器供集內容,兩者是同步的。
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 volume(docker管理卷): 只需要在容器內指定容器的掛載點是什么,而被綁定宿主機下的那個目錄,是由容器引擎daemon自行創建一個空的目錄,或者使用一個已經存在的目錄,與存儲卷建立存儲關系,這種方式極大解脫用戶在使用卷時的耦合關系,缺陷是用戶無法指定那些使用目錄,臨時存儲比較適合;
1.4 數據類型
Docker的鏡像是分層設計的,底層是只讀的,通過鏡像啟動 的容器添加了一層可讀寫的文件系統,用戶寫入的數據都保存在這一層當中,如果要將寫入的數據永久生效,需要將其提交為一個鏡像然后通過這個鏡像在啟動實例,然后就
會給這個啟動的實例添加一層可讀寫的文件系統, 目前 Docker的數據類型分為兩種, 一 是數據卷,二是數據容器 ,數據卷類似於掛載的一塊磁盤,數據容器是將數據保存在一個容器上。
2、使用數據卷
(1)數據卷實際上就是宿主機上的目錄或者是文件,可以被直接mount 到容器當中使用 。
(2)實際生成 環境中,需要針對不同類型的服務、不同類型的數據存儲要求做相應的規划, 最終保證服務的可擴展性、穩定性以及數據的安全性。
(3)為docker run 命令使用-v 選項即可使用volume,其實也就是將宿主機的文件掛載到容器中,默認掛載為rw的權限。
數據卷的特點及使用:
1、數據卷是目錄或者文件,並且可以在多個容器之間共同使用 。
2、對數據卷更改數據容器里面,會立即更新 。
3、數據卷的數據可以持久保存,即使刪除使用使用該容器卷的容器也不 影響 。
4、 在容器里面的寫入數據,不會影響到鏡像本身
數據卷使用場景:
1、 日志輸出
2、 靜態web頁面
3、應用配置文件
4、 多容器間目錄或文件共享
存儲卷用法選項:
- -v選項:將存儲卷掛載到哪個目錄下
- --volume-from選項:從哪個容器主機進行復制
實現宿主機volume卷掛載到容器中
1、將宿主機的代碼映射到tomcat容器指定的目錄下
1、先查看tomcat容器里邊的代碼內容
[root@centos-7 app1]# docker run -it -p 8080:8080 tomcat-app1:v1 # 啟動tomcat-app1:v1容器
2、到運行的tomcat-app1:v1的容器中查看代碼內容
[root@centos-7 ~]# docker ps #查看運行的容器 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 21f9df7830a5 tomcat-app1:v1 "/apps/tomcat/bin/ru…" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp, 8443/tcp nervous_grothendieck [root@centos-7 ~]# docker exec -it 21f9df7830a5 bash # 進入正在運行的容器內部 [root@21f9df7830a5 /]# ls apps bin data dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var [root@21f9df7830a5 /]# cd /data/tomcat/webapps/ [root@21f9df7830a5 webapps]# cat app1/index.html #查看容器缺省的代碼內容 tomcat web app1 page
3、訪問網頁也可以看到此時的內容
4、在宿主機上先創建一個存放代碼的目錄
[root@centos-7 ~]# cd /data/tomcat/app1/ [root@centos-7 app1]# echo 20200116 welcome to Shanghai > index.html
5、將宿主機的代碼數據映射到tomcat訪問的數據目錄下,並映射端口,此時volume映射的優先級高
[root@centos-7 app1]# docker run -it -v /data/tomcat/app1/:/data/tomcat/webapps/app1 -p 8080:8080 tomcat-app1:v1
6、查看網頁效果,此時就是宿主機的代碼覆蓋了容器內部的數據代碼
2、實現宿主機配置文件掛載到容器中
1、在宿主機修改catalina.sh腳本文件,並存放到指定的目錄下
[root@centos-7 bin]# mkdir /data/tomcat/bin [root@centos-7 bin]#cd /data/tomcat/bin # 在此目錄下將腳本文件存放在此
創建一個掛載容器log日志的目錄
[root@centos-7 bin]# mkdir /data/tomcat/logs
將目錄和catalina.sh腳本分別加上權限
# chmod 777 /data/tomcat/logs # 目錄先加上777權限 # chmod +x /data/tomcat/bin/catalina.sh # 加上執行權限
2、查看catalina.sh配置文件內容,最主要的部分在下面
3、開始掛載此文件到容器中,並設置為只讀文件,避免在容器中進行修改此文件,且將容器中的log日志進行掛載,避免容器壞掉而log日志在容器中,導致數據丟失
[root@centos-7 logs]# docker run -it -v /data/tomcat/bin/catalina.sh:/apps/tomcat/bin/catalina.sh -v /data/tomcat/logs/:/apps/tomcat/logs/ -p 8282:8080 tomcat-app1:v1
4、此時就可以在宿主機查看容器里邊的log日志
[root@centos-7 bin]# ll /data/tomcat/logs/ total 16 -rw-rw-r-- 1 2020 2020 7355 Jan 16 18:03 catalina.2020-01-16.log -rw-rw-r-- 1 2020 2020 7866 Jan 16 18:03 catalina.out -rw-rw-r-- 1 2020 2020 0 Jan 16 18:03 host-manager.2020-01-16.log -rw-rw-r-- 1 2020 2020 0 Jan 16 18:03 localhost.2020-01-16.log -rw-rw-r-- 1 2020 2020 0 Jan 16 18:03 localhost_access_log.2020-01-16.txt -rw-rw-r-- 1 2020 2020 0 Jan 16 18:03 manager.2020-01-16.log
5、此時catalina.sh的文件已經存放到文件中
[root@centos-7 bin]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 95d4f7d917bc tomcat-app1:v1 "/apps/tomcat/bin/ru…" 56 seconds ago Up 55 seconds 8443/tcp, 0.0.0.0:8282->8080/tcp wizardly_hypatia [root@centos-7 bin]# docker exec -it 95d4f7d917bc bash [root@95d4f7d917bc /]# cd /apps/tomcat/bin/ [root@95d4f7d917bc bin]# ls bootstrap.jar commons-daemon-native.tar.gz daemon.sh setclasspath.bat startup.bat tool-wrapper.bat catalina-tasks.xml commons-daemon.jar digest.bat setclasspath.sh startup.sh tool-wrapper.sh catalina.bat configtest.bat digest.sh shutdown.bat tomcat-juli.jar version.bat catalina.sh configtest.sh run_tomcat.sh shutdown.sh tomcat-native.tar.gz version.sh [root@95d4f7d917bc bin]# vim catalina.sh
數據卷容器
實現原理:
數據卷容器最大的功能是可以讓數據在多個docker容器之間共享,即可以讓B容器訪問A容器的內容,而容器C也可以訪問A容器的內容,即先要創建一個后台運行的容器作為Server,用於提供這個卷,可以為其他容器提供數據存儲服務其他使用此卷的容器作為client端。
實現共享的存儲卷容器掛載
1、創建共享存儲卷容器
1、創建一個volume-server的容器名,並將宿主機的訪問頁面index.html、catalina.sh、logs日志都映射到此日志中
[root@centos-7 ~]# docker run -it -d --name volume-server -v /data/tomcat/bin/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v /data/tomcat/logs:/apps/tomcat/logs -v /data/tomcat/app1/:/data/tomcat/webapps/app1/ tomcat-app1:v1 230d219e035c3fec1145c32060fed37e1e12154df0d010b34f614740f31ef9bd # 添加一個server服務器的容器,並將宿主機的文件映射到此容器中
2、--volume-from選項來復制容器中存儲卷
1、創建volume-client客戶端的容器,用--volume-from選項復制上一個volume-server容器里邊的存儲卷
[root@centos-7 ~]# docker run -it -d --name volume-client --volumes-from volume-server -p 8282:8080 tomcat-app1:v1 1f40e1b7b4eb9192260557ebef78aec51f239e66f994b2ae42b1c18a9849843b #此時映射的volume-client容器內容都是從colume-server容器來的
2、進入colume-client的容器內,也可以看到宿主機的文件內容
[root@centos-7 ~]# docker exec -it 1f40e1b7b4eb9192260557ebef78aec51f239e66f994b2ae42b1c18a9849843b bash [root@1f40e1b7b4eb /]# cat /data/tomcat/webapps/index.html 20200116 welcome to Shanghai # 可以看到是宿主機的index.html文件 [root@1f40e1b7b4eb /]# ls -l /apps/tomcat/logs/ total 92 -rw-rw-r-- 1 tomcat tomcat 42631 Jan 16 19:21 catalina.2020-01-16.log -rw-rw-r-- 1 tomcat tomcat 45697 Jan 16 19:21 catalina.out -rw-rw-r-- 1 tomcat tomcat 0 Jan 16 18:03 host-manager.2020-01-16.log -rw-rw-r-- 1 tomcat tomcat 0 Jan 16 18:03 localhost.2020-01-16.log -rw-rw-r-- 1 tomcat tomcat 0 Jan 16 18:03 localhost_access_log.2020-01-16.txt -rw-rw-r-- 1 tomcat tomcat 0 Jan 16 18:03 manager.2020-01-16.log
4、此時訪問volume-client的頁面,實際也是來自於volume-server容器的內容
3、再啟動一個容器復制volume-server容器的存儲卷
1、此時新建一個volume-client容器也能訪問宿主機的代碼網頁
[root@centos-7 ~]# docker run -it -d --name volume-client1 --volumes-from volume-server -p 8383:8080 tomcat-app1:v1 # 只需要修改映射端口,客戶端的名稱即可 ce0e8bd7e33249b8b9ec59e8c7c7678086958b62cac4bb16c43e92577aa0d77f
2、進入到volume-client2的容器中,也可以看到宿主機的訪問頁面
[root@centos-7 ~]# docker exec -it ce0e8bd7e33249b8b9ec59e8c7c7678086958b62cac4bb16c43e92577aa0d77f bash #進入volume-client2容器中 [root@ce0e8bd7e332 /]# cat /data/tomcat/webapps/index.html # 查看此客戶端的頁面 20200116 welcome to Shanghai
此時映射的端口:8383照樣也能訪問網頁
3、此時掛載后的client客戶端不會受server端容器的宕機而導致不能訪問,但是新的容器就不能基於volume-server服務容器進行創建新的容器。
[root@centos-7 ~]# docker ps # 查看image鏡像ID CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b4044ab23dc0 tomcat-app1:v1 "/apps/tomcat/bin/ru…" 38 seconds ago Up 37 seconds 8443/tcp, 0.0.0.0:8383->8080/tcp volume-client1 80556d0c9945 tomcat-app1:v1 "/apps/tomcat/bin/ru…" About a minute ago Up About a minute 8443/tcp, 0.0.0.0:8282->8080/tcp volume-client 54dd95b8d95c tomcat-app1:v1 "/apps/tomcat/bin/ru…" About a minute ago Up About a minute 8080/tcp, 8443/tcp volume-server [root@centos-7 ~]# docker rm -f 54dd95b8d95c #刪除server服務端的ID 54dd95b8d95c
此時客戶端的網頁還是可以訪問,此時,實驗就實現了一個容器去共享存儲卷,用--volume-from選項來復制此volume-server的文件,實現存儲卷共享的作用。
總結:
(1)在當前環境下,即使把提供卷的容器Server刪除,已經運行的容器 Client依然可以使用掛載的卷,因為容器是通過掛載訪問數據的,但是無法創建新的卷容器客戶端,但是再把卷容器Server創建后即可正,創建卷容器Client,
此方式可以用於線上數據庫 、共享數據目錄等環境因為即使數據卷容器被刪除了,其他已經運行的容器依然可以掛載使用。
(2)數據卷容器可以作為共享的方式為其他容器提供文件共享,類似於 NFS 共享,可以在生產中啟動一個實例掛載本地的目錄,然后其他的容器分別掛載此容器的目錄,即可保證各容器之間的數據一致性。