本人最近在使用docker部署mysql時,在持久化mysql數(shù)據(jù)時遇到了一個有趣的問題,將排查過程及思考記錄在這里,以備后查。
先簡單描述下我遇到的問題:在mysql容器中創(chuàng)建了兩個數(shù)據(jù)庫,然后使用docker commit想要保存容器的修改為新的鏡像,發(fā)現(xiàn)只保存下來了新建的一個數(shù)據(jù)庫,而另一個并沒有被保存下來。最終通過查看docker文檔和自己的實驗,發(fā)現(xiàn)是mysql鏡像中指定了volume為mysql數(shù)據(jù)路徑所致。
具體講一下我遇到的問題及排查過程:首先我從dockerHub里拉取了最新的mysql鏡像來部署
docker pull mysql
然后基于這個鏡像創(chuàng)建一個容器
docker run --name mysqldock -e MYSQL_ROOT_PASSWORD=admin -e MYSQL_DATABASE=inst1 -d -p 3066:3066 mysql
這里的MYSQL_ROOT_PASSWORD指定了root賬號的密碼,MYSQL_DATABASE指定了在容器創(chuàng)建時同時創(chuàng)建的數(shù)據(jù)庫命。MYSQL_DATABASE可以不提供,這樣不會預創(chuàng)建數(shù)據(jù)庫。創(chuàng)建好名為mysqldock
的容器后,使用
docker exec -it mysqldock bash
進入容器,執(zhí)行mysql客戶端命令,查看mysql的庫,發(fā)現(xiàn)inst1已經(jīng)建好:
然后手動創(chuàng)建數(shù)據(jù)庫inst2,并且在兩個庫中創(chuàng)建一些表:
這時,我想把目前為止對mysqldock容器做的變更保存下來,所以就想到了使用docker commit
指令
docker commit mysqldock
docker commit 命令會將docker容器的變更保存下來,并且生成新的鏡像。生成新的鏡像后,我想看看之前創(chuàng)建的庫和表還在不在,就使用新的鏡像創(chuàng)建了新的容器mysqlnew
,并且進入容器查看mysql情況,神奇的現(xiàn)象出現(xiàn)了,mysql中竟然只有inst1庫,而沒有inst2,同時inst1里面是空的,我們創(chuàng)建的表也消失了:
這就讓我疑惑了,查看了下docker commit命令的說明:
沒毛病啊,基于容器的變化創(chuàng)建一個新的鏡像。為了驗證docker commit 命令的可用性,我在mysqldock中創(chuàng)建一個新的文件,再commit成新鏡像,再創(chuàng)建容器,查看發(fā)現(xiàn)新的容器的確是包含了新創(chuàng)建的文件,也就是說docker commit的確能夠基于容器的變化創(chuàng)建新的鏡像:
那我就丈二和尚摸不著頭腦了,為啥新建的文件可以保存下來,新建的庫就不行呢,新建的庫不也是在mysql數(shù)據(jù)文件路徑下新建的文件么?是時候求助官方文檔了,查閱了docker commit的官方文檔說明后,發(fā)現(xiàn)了在擴展說明中有這么一句話:
The commit operation will not include any data contained in volumes mounted inside the container.
意思是commit操作并不會包含容器內(nèi)掛載數(shù)據(jù)卷中的數(shù)據(jù)變化。難道是因為mysql容器的掛載數(shù)據(jù)卷引起的?(這里我就要吐槽一下了,docker --help好歹詳細點啊,這么重要的信息竟然都沒有顯示。)通過
docker inspect mysqlsock
查看mysqldock的屬性,發(fā)現(xiàn)Mounts
里有這樣的信息:
這說明了這個容器將容器內(nèi)的/var/lib/mysql路徑作為volume掛載。查看容器內(nèi)的該路徑發(fā)現(xiàn)的確是mysql數(shù)據(jù)庫的數(shù)據(jù)文件,這點在mysql的dockerHub主頁也能發(fā)現(xiàn):
我馬上試了一下在該文件下新建一個文件,并且docker commit,然后用新的鏡像創(chuàng)建新容器,文件果然不見了!
那么問題來了,學習挖掘機到底哪家強?為何commit之后inst2沒有了,但inst1還在?深究起來,連mysql root密碼也沒有變,還是之前設(shè)置的admin。發(fā)現(xiàn)沒有?數(shù)據(jù)庫inst1及root密碼都是我在創(chuàng)建mysqldock容器時通過-e參數(shù)指定的,莫非是在容器創(chuàng)建時通過-e創(chuàng)建的,就算是在volume里的也可以保存么?繼續(xù)研究docker commit命令的官方文檔,在擴展說明里發(fā)現(xiàn)了這么一句:
It can be useful to commit a container’s file changes or settings into a new image.
看到了么,file changes or settings。文件變更和設(shè)置,-e的不就是設(shè)置么,這點也可以通過docker inspect發(fā)現(xiàn),在Config下的Env參數(shù)中:
這么說就說得通了,雖然文件的確是發(fā)生了變化,但是由于文件是在容器掛載的數(shù)據(jù)卷中,所以這些變化沒有被commit,然而由于在創(chuàng)建mysqldock容器的時候設(shè)置了-e參數(shù),這些設(shè)置被容器保留了下來,commit命令使用這些設(shè)置構(gòu)建了新的鏡像。排查到這里,我們的問題是找到原因了,可怎么解決呢?我們該如何將mysql docker中修改的數(shù)據(jù)保存下來呢?通過查看mysql dockerHub主頁Where to Store Data一節(jié)中的說明,我們可以通過docker提供的數(shù)據(jù)掛載來實現(xiàn)。
docker的數(shù)據(jù)掛載分為三種,volume, bind mount和tmpfs,關(guān)于三種的具體說明,強烈推薦大家看一下官網(wǎng)的文檔。這邊簡單說明一下:
volume是由docker默認及推薦的掛載方式,volume由docker直接管理,同一個volume可以共享給多個容器使用,volume和容器的生命周期完全獨立,容器刪除時volume仍然存在,除非使用docker volume
相應命令刪除volume;缺點是volume在宿主機上比較難定位,在宿主機上直接操作volume比較困難。
bind mount是直接將宿主機文件系統(tǒng)上的文件路徑映射到容器中,兩邊雙向同步,顯而易見,有缺點也有優(yōu)點,優(yōu)點是可以直接訪問,也可以被別的程序使用,比如我們打包一個本地應用到本地/target路徑,我們就可以把這個路徑使用bind mount的方式掛在到依賴他的應用的docker容器中,這樣本地應用打包后,docker里的數(shù)據(jù)卷也會同時更新;缺點也是顯而易見的,因為你可以把任何文件路徑使用bind mount的方式綁定到容器中,這樣有可能一些安全問題,比如把宿主機的系統(tǒng)文件綁定到容器中。
tmpfs這種方式是使用宿主機的內(nèi)存作為存儲,不會寫到宿主機的文件系統(tǒng)中,和前兩種區(qū)別較大。
mysql dockerHub主頁中的推薦方式是在宿主機中新建一個專門用來存放mysql docker數(shù)據(jù)的文件路徑,同時在新建容器的時候?qū)⒃撀窂接成涞饺萜髦校簿褪鞘褂胋ind mount的方式,之所以不使用volume的方式是因為volume是由docker管理,在宿主機上比較難定位。
那對于我的情況,既已經(jīng)有一個容器使用了volume,想把volume里的數(shù)據(jù)在新的容器中使用bind mount方式掛載該怎么辦呢?我們可以先把mysqldock容器中所需要的文件拷貝出來到本地的/var/own/mysqldata,通過
docker cp mysqldock:/var/lib/mysql /var/own/mysqldata
然后在創(chuàng)建新的mysql容器時,掛載該文件即可
docker run -v /var/own/mysqldata:/var/lib/mysql --name mysqlnew -d mysql
這樣新的容器就可以保留mysqldock中的數(shù)據(jù)了,問題解決!當然,我們也可以使用docker推薦的volume方式掛載,首先找到mysqldock的volume,然后在運行新容器時指定該volume進行掛載就行了:
這種方式繁瑣?別急,還有更簡單的,在創(chuàng)建容器的時候,可以指定使用其他容器的volume,也就是共享其他容器的volume,使用--volumes-from參數(shù)
docker run --name mysqlvolumn2 --volumes-from mysqldock -d mysql
其實volume還可以在創(chuàng)建的時候進行命名,從而是查找起來不那么繁瑣,具體的參數(shù)就請大家參考官網(wǎng)或者--help了,其實官方更加推薦的是使用--mount代替-v參數(shù),官網(wǎng)上有詳盡的例子,大家也可以自行進行嘗試。
當然啦,純粹把docker作成數(shù)據(jù)容器其實并沒有太大意義,這里只是借這個問題窺探一下docker數(shù)據(jù)卷的一些用法。