天天看點

一文說清楚Dockerfile 中VOLUME到底有什麼用?

寫在開頭

相信大部分人對docker run -v這個參數都比較熟悉,無非就是把主控端目錄和容器目錄做映射,以便于容器中的某些檔案可以直接儲存在主控端上,實作容器被删除之後資料還在,比如我們把mysql裝在容器中,肯定不能說容器被删mysql所有的資料也都不在了。第二個作用是也可以用來實作多容器共享同一份檔案。

但如果玩過dockerfile的話就知道dockerfile還有個VOLUME指令,如

這個指令很容易和啟動時的-v指令搞混淆,他們之間到底有什麼差別呢,什麼時候需要使用volume呢?

volume指令指定的位置在容器被删除以後資料檔案會被删除嗎?如果-v和volume指定了同一個位置,會發生什麼事呢?

volume和run -v的差別,什麼時候需要使用volume

容器運作時應該盡量保持容器存儲層不發生寫操作,對于資料庫類需要儲存動态資料的應用,其資料庫檔案應該儲存于卷(volume)中。為了防止運作時使用者忘記将動态檔案所儲存目錄挂載為卷,在Dockerfile 中,我們可以事先指定某些目錄挂載為匿名卷,這樣在運作時如果使用者不指定挂載,其應用也可以正常運作,不會向容器存儲層寫入大量資料。

那麼​<code>​Dockerfile​</code>​中的​<code>​VOLUME​</code>​指令實際使用中是不是就是跟​<code>​docker run​</code>​中的​<code>​-v​</code>​參數一樣是将主控端的一個目錄綁定到容器中的目錄以達到共享目錄的作用呢?

并不然,其實​<code>​VOLUME​</code>​指令隻是起到了聲明了容器中的目錄作為匿名卷,但是并沒有将匿名卷綁定到主控端指定目錄的功能。

當我們生成鏡像的​<code>​Dockerfile​</code>​中以​<code>​Volume​</code>​聲明了匿名卷,并且我們以這個鏡像​<code>​run​</code>​了一個容器的時候,​<code>​docker​</code>​會在安裝目錄下的指定目錄下面生成一個目錄來綁定容器的匿名卷(這個指定目錄不同版本的docker會有所不同),我目前的目錄為:​<code>​/var/lib/docker/volumes/{容器ID}​</code>​。

總結: ​<code>​volume​</code>​隻是指定了一個目錄,用以在使用者忘記啟動時指定​<code>​-v​</code>​參數也可以保證容器的正常運作。比如mysql,你不能說使用者啟動時沒有指定​<code>​-v​</code>​,然後删了容器,就把mysql的資料檔案都删了,那樣生産上是會出大事故的,是以mysql的dockerfile裡面就需要配置​<code>​volume​</code>​,這樣即使使用者沒有指定​<code>​-v​</code>​,容器被删後也不會導緻資料檔案都不在了。還是可以恢複的。

volume指令指定的位置在容器被删除以後資料檔案會被删除嗎

​<code>​volume​</code>​與​<code>​-v​</code>​指令一樣,容器被删除以後映射在主機上的檔案不會被删除。

如果-v和volume指定了同一個位置,會發生什麼事呢?

會以​<code>​-v​</code>​設定的目錄為準,其實​<code>​volume​</code>​指令的設定的目的就是為了避免使用者忘記指定​<code>​-v​</code>​的時候導緻的資料丢失,那麼如果使用者指定了​<code>​-v​</code>​,自然而然就不需要volume指定的位置了。

總結

其實一般的dockfile如果不是資料庫類的這種需要持久化資料到磁盤上的應用,都是無需指定​<code>​volume​</code>​的。指定​<code>​volume​</code>​隻是為了避免使用者忘記指定​<code>​-v​</code>​時導緻的資料全部在容器中,這樣的話容器一旦被删除所有的資料都丢失了。

那麼為什麼dockerfile中不提供一個能夠映射為主機目錄:容器目錄這樣的指令呢?其實這樣的設計是有道理的,如果在dockerfile中指定了主機目錄,這樣dockerfile就不具備了可移植性了,畢竟每個人所需要映射的目錄可能是不同的,那麼最好的辦法就是把這個權利交給每個運作這個dockerfile的人,是以才會有 ​<code>​run -v 主機目錄:容器目錄​</code>​ 這樣的指令。