docker實踐入門之三
檔案系統
說完了網路,現在來說儲存。
對於redis這樣的應用來說,我們通常並不需要它的持久化儲存,只是拿它做快取而已,所以儲存問題並不重要,但是對於mysql這樣的資料庫來說,儲存就非常重要了。
前面說到了container和image的區別,但並不完全,它們還有一個重要區別就在於儲存。docker使用了一套自己的檔案儲存機制,基於aufs、devicemapper、btrfs、vfs等檔案系統,它的特點是分層的:
image 自帶的儲存內容都是隻讀的,當它執行成一個container時,docker就會在上面新增一層可寫層,container中執行的程式對檔案系統的寫 操作都是儲存在這個可寫層裡,而且當這個程式執行完成,這個可寫層自動固化為一個新的只讀層疊加在原來image的只讀檔案系統上,執行下一個程式的時候 又新增一個新的可寫層。當進行commit操作時,就是把這些執行過程式的各個層合併生成一個新的image。
同樣的,如果不進行commit操作,那麼當一個container執行完被刪除後,所有未儲存的內容都會丟失,這對於資料庫應用這樣需要持久化的情況,是肯定不能接受的。
Volume對映
所以,實際上對於這類應用,你需要通過檔案系統對映,把主機上的檔案目錄對映到container裡,這樣container裡的程式儲存到這個對映目錄下的內容實際上都會存在於主機的檔案系統,這樣即使container被銷燬,儲存的內容還在。
映 射的方法有兩種,一個是構建image時在Dockerfile裡用VOLUME命令指定,但這樣不夠靈活,因為會對映到host機上的一個特定目錄,不 好找。但有些情況並不需要關注它存在哪裡,只要是存在host機上即可,比如資料庫,這時用這種方式比較簡單。如果需要查詢volume在host機的位 置,可以用如下命令:
docker inspect -f {{.volumes}} container
另一個更好的方法是在執行container時指定:
docker run -d -v /host/path:/container/path --name mysql-server mysql_image
其中的-v引數就是指定資料夾對映。
另一種需要對映檔案目錄的應用場景類似這樣:
令狐裝了一個arch linux,自帶的gcc版本比較高,然而他需要編譯的程式碼必須用gcc4.9編譯,不能用高版本。因為gcc這種東西依賴太可怕了,刪除高版本重灌低版本是不現實的,所以我建議他用docker來解決,方法就是:
- pull一個gcc4.9的image
- 以互動方式執行起來後,安裝上必要的庫和工具,然後commit成一個新的image(比如gcclhc),當然更好的辦法是用Dockerfile來構建image
- 需要用它編譯時就這麼做:
docker run -it -v /host/path_to_source_code:/container/path gcclhc /bin/bash
然後在這個container環境裡用gcc編譯/container/path下的程式碼即可,最後回到host的/host/path_to_source_code取得編譯結果。
是不是比裝個虛擬機器要方便很多。
Volume container
最後說一下volume container,顧名思義就是一個只做volume用的container。因為它只做volume用,所以不需要以-d方式執行起來即可使用,很節約資源。
那 麼用什麼樣的image來執行呢?雖然用一些小的image來跑好像可以節約儲存空間,但實際上這並不是最節約的。因為docker的container 是在image上加一層讀寫層,所以同一個image跑兩個container時會共用下面的只讀層,不會增加任何儲存消耗,所以如果要把volume container用來做資料庫儲存的話,用資料庫image來跑才是最節約的,因為沒有任何額外的儲存消耗。
例如:
docker run --name dbvol postgres echo "DB volume." docker run -d --name pgserver --volumes-from dbvol postgres
注意,第一句那個volume container並沒有用-d引數,所以執行完echo就退出了(但是隻要不rm的話,container還在,所以volume也在)。第二個container就使用了第一個的volume來儲存資料。
還有一點需要補充說明的是,不用的volume記得刪除,預設對container執行rm操作時並不會刪除volume,需要加上-v引數,或者在run的時候加上--rm引數。