本人最近在使用docker部署mysql時(shí),在持久化mysql數(shù)據(jù)時(shí)遇到了一個(gè)有趣的問題锻狗,將排查過程及思考記錄在這里满力,以備后查。
先簡(jiǎn)單描述下我遇到的問題:在mysql容器中創(chuàng)建了兩個(gè)數(shù)據(jù)庫轻纪,然后使用docker commit想要保存容器的修改為新的鏡像油额,發(fā)現(xiàn)只保存下來了新建的一個(gè)數(shù)據(jù)庫,而另一個(gè)并沒有被保存下來刻帚。最終通過查看docker文檔和自己的實(shí)驗(yàn)潦嘶,發(fā)現(xiàn)是mysql鏡像中指定了volume為mysql數(shù)據(jù)路徑所致。
具體講一下我遇到的問題及排查過程:首先我從dockerHub里拉取了最新的mysql鏡像來部署
docker pull mysql
然后基于這個(gè)鏡像創(chuàng)建一個(gè)容器
docker run --name mysqldock -e MYSQL_ROOT_PASSWORD=admin -e MYSQL_DATABASE=inst1 -d -p 3066:3066 mysql
這里的MYSQL_ROOT_PASSWORD指定了root賬號(hào)的密碼崇众,MYSQL_DATABASE指定了在容器創(chuàng)建時(shí)同時(shí)創(chuàng)建的數(shù)據(jù)庫命掂僵。MYSQL_DATABASE可以不提供,這樣不會(huì)預(yù)創(chuàng)建數(shù)據(jù)庫顷歌。創(chuàng)建好名為mysqldock的容器后锰蓬,使用
docker exec -it mysqldock bash
進(jìn)入容器,執(zhí)行mysql客戶端命令眯漩,查看mysql的庫芹扭,發(fā)現(xiàn)inst1已經(jīng)建好:
inst1在容器初始化的時(shí)候已經(jīng)創(chuàng)建
然后手動(dòng)創(chuàng)建數(shù)據(jù)庫inst2,并且在兩個(gè)庫中創(chuàng)建一些表:
手動(dòng)創(chuàng)建inst2及兩個(gè)庫中的表
這時(shí)赦抖,我想把目前為止對(duì)mysqldock容器做的變更保存下來舱卡,所以就想到了使用docker commit指令
docker commit mysqldock
docker commit 命令會(huì)將docker容器的變更保存下來,并且生成新的鏡像摹芙。生成新的鏡像后灼狰,我想看看之前創(chuàng)建的庫和表還在不在宛瞄,就使用新的鏡像創(chuàng)建了新的容器mysqlnew浮禾,并且進(jìn)入容器查看mysql情況,神奇的現(xiàn)象出現(xiàn)了份汗,mysql中竟然只有inst1庫盈电,而沒有inst2,同時(shí)inst1里面是空的杯活,我們創(chuàng)建的表也消失了:
消失了的inst2和表
這就讓我疑惑了匆帚,查看了下docker commit命令的說明:
docker commit 說明
沒毛病啊,基于容器的變化創(chuàng)建一個(gè)新的鏡像旁钧。為了驗(yàn)證docker commit 命令的可用性吸重,我在mysqldock中創(chuàng)建一個(gè)新的文件互拾,再commit成新鏡像,再創(chuàng)建容器嚎幸,查看發(fā)現(xiàn)新的容器的確是包含了新創(chuàng)建的文件颜矿,也就是說docker commit的確能夠基于容器的變化創(chuàng)建新的鏡像:
新建文件出現(xiàn)在了新的鏡像中
那我就丈二和尚摸不著頭腦了,為啥新建的文件可以保存下來嫉晶,新建的庫就不行呢骑疆,新建的庫不也是在mysql數(shù)據(jù)文件路徑下新建的文件么?是時(shí)候求助官方文檔了替废,查閱了docker commit的官方文檔說明后箍铭,發(fā)現(xiàn)了在擴(kuò)展說明中有這么一句話:
The commit operation will not include any data contained in volumes mounted inside the container.
意思是commit操作并不會(huì)包含容器內(nèi)掛載數(shù)據(jù)卷中的數(shù)據(jù)變化。難道是因?yàn)閙ysql容器的掛載數(shù)據(jù)卷引起的椎镣?(這里我就要吐槽一下了诈火,docker --help好歹詳細(xì)點(diǎn)啊,這么重要的信息竟然都沒有顯示衣陶。)通過
docker inspect mysqlsock
查看mysqldock的屬性柄瑰,發(fā)現(xiàn)Mounts里有這樣的信息:
Mounts
這說明了這個(gè)容器將容器內(nèi)的/var/lib/mysql路徑作為volume掛載。查看容器內(nèi)的該路徑發(fā)現(xiàn)的確是mysql數(shù)據(jù)庫的數(shù)據(jù)文件剪况,這點(diǎn)在mysql的dockerHub主頁也能發(fā)現(xiàn):
/var/lib/mysql下的數(shù)據(jù)
我馬上試了一下在該文件下新建一個(gè)文件教沾,并且docker commit,然后用新的鏡像創(chuàng)建新容器译断,文件果然不見了授翻!
那么問題來了,學(xué)習(xí)挖掘機(jī)到底哪家強(qiáng)?為何commit之后inst2沒有了孙咪,但inst1還在堪唐?深究起來,連mysql root密碼也沒有變翎蹈,還是之前設(shè)置的admin淮菠。發(fā)現(xiàn)沒有?數(shù)據(jù)庫inst1及root密碼都是我在創(chuàng)建mysqldock容器時(shí)通過-e參數(shù)指定的荤堪,莫非是在容器創(chuàng)建時(shí)通過-e創(chuàng)建的合陵,就算是在volume里的也可以保存么?繼續(xù)研究docker commit命令的官方文檔澄阳,在擴(kuò)展說明里發(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è)置么低剔,這點(diǎn)也可以通過docker inspect發(fā)現(xiàn),在Config下的Env參數(shù)中:
mysqldock和mysqlnew有一樣的env參數(shù)
這么說就說得通了,雖然文件的確是發(fā)生了變化襟齿,但是由于文件是在容器掛載的數(shù)據(jù)卷中姻锁,所以這些變化沒有被commit,然而由于在創(chuàng)建mysqldock容器的時(shí)候設(shè)置了-e參數(shù)猜欺,這些設(shè)置被容器保留了下來屋摔,commit命令使用這些設(shè)置構(gòu)建了新的鏡像。排查到這里替梨,我們的問題是找到原因了钓试,可怎么解決呢?我們?cè)撊绾螌ysql docker中修改的數(shù)據(jù)保存下來呢副瀑?通過查看mysql dockerHub主頁Where to Store Data一節(jié)中的說明弓熏,我們可以通過docker提供的數(shù)據(jù)掛載來實(shí)現(xiàn)。
docker的數(shù)據(jù)掛載分為三種糠睡,volume, bind mount和tmpfs挽鞠,關(guān)于三種的具體說明,強(qiáng)烈推薦大家看一下官網(wǎng)的文檔狈孔。這邊簡(jiǎn)單說明一下:
volume是由docker默認(rèn)及推薦的掛載方式信认,volume由docker直接管理,同一個(gè)volume可以共享給多個(gè)容器使用均抽,volume和容器的生命周期完全獨(dú)立嫁赏,容器刪除時(shí)volume仍然存在,除非使用docker volume相應(yīng)命令刪除volume油挥;缺點(diǎn)是volume在宿主機(jī)上比較難定位潦蝇,在宿主機(jī)上直接操作volume比較困難。
bind mount是直接將宿主機(jī)文件系統(tǒng)上的文件路徑映射到容器中深寥,兩邊雙向同步攘乒,顯而易見,有缺點(diǎn)也有優(yōu)點(diǎn)惋鹅,優(yōu)點(diǎn)是可以直接訪問则酝,也可以被別的程序使用,比如我們打包一個(gè)本地應(yīng)用到本地/target路徑闰集,我們就可以把這個(gè)路徑使用bind mount的方式掛在到依賴他的應(yīng)用的docker容器中沽讹,這樣本地應(yīng)用打包后,docker里的數(shù)據(jù)卷也會(huì)同時(shí)更新返十;缺點(diǎn)也是顯而易見的妥泉,因?yàn)槟憧梢园讶魏挝募窂绞褂胋ind mount的方式綁定到容器中椭微,這樣有可能一些安全問題洞坑,比如把宿主機(jī)的系統(tǒng)文件綁定到容器中。
tmpfs這種方式是使用宿主機(jī)的內(nèi)存作為存儲(chǔ)蝇率,不會(huì)寫到宿主機(jī)的文件系統(tǒng)中迟杂,和前兩種區(qū)別較大刽沾。
mysql dockerHub主頁中的推薦方式是在宿主機(jī)中新建一個(gè)專門用來存放mysql docker數(shù)據(jù)的文件路徑,同時(shí)在新建容器的時(shí)候?qū)⒃撀窂接成涞饺萜髦信趴剑簿褪鞘褂胋ind mount的方式侧漓,之所以不使用volume的方式是因?yàn)関olume是由docker管理,在宿主機(jī)上比較難定位监氢。
那對(duì)于我的情況布蔗,既已經(jīng)有一個(gè)容器使用了volume,想把volume里的數(shù)據(jù)在新的容器中使用bind mount方式掛載該怎么辦呢浪腐?我們可以先把mysqldock容器中所需要的文件拷貝出來到本地的/var/own/mysqldata纵揍,通過
docker cp mysqldock:/var/lib/mysql /var/own/mysqldata
然后在創(chuàng)建新的mysql容器時(shí),掛載該文件即可
docker run -v /var/own/mysqldata:/var/lib/mysql --name mysqlnew -d mysql
這樣新的容器就可以保留mysqldock中的數(shù)據(jù)了议街,問題解決泽谨!當(dāng)然,我們也可以使用docker推薦的volume方式掛載特漩,首先找到mysqldock的volume吧雹,然后在運(yùn)行新容器時(shí)指定該volume進(jìn)行掛載就行了:
使用volume進(jìn)行掛載
這種方式繁瑣?別急涂身,還有更簡(jiǎn)單的雄卷,在創(chuàng)建容器的時(shí)候,可以指定使用其他容器的volume蛤售,也就是共享其他容器的volume龙亲,使用--volumes-from參數(shù)
docker run --name mysqlvolumn2 --volumes-from mysqldock -d mysql
其實(shí)volume還可以在創(chuàng)建的時(shí)候進(jìn)行命名,從而是查找起來不那么繁瑣悍抑,具體的參數(shù)就請(qǐng)大家參考官網(wǎng)或者--help了鳄炉,其實(shí)官方更加推薦的是使用--mount代替-v參數(shù),官網(wǎng)上有詳盡的例子搜骡,大家也可以自行進(jìn)行嘗試拂盯。
當(dāng)然啦,純粹把docker作成數(shù)據(jù)容器其實(shí)并沒有太大意義记靡,這里只是借這個(gè)問題窺探一下docker數(shù)據(jù)卷的一些用法贬养。
作者:SawyerZhou
鏈接:http://www.reibang.com/p/530d00f97cbf
來源:簡(jiǎn)書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)蕾羊,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處较解。