基于docker部署mysql的數(shù)據(jù)持久化問題

本人最近在使用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)建好:

inst1在容器初始化的時候已經(jīng)創(chuàng)建

然后手動創(chuàng)建數(shù)據(jù)庫inst2,并且在兩個庫中創(chuàng)建一些表:

手動創(chuàng)建inst2及兩個庫中的表

這時,我想把目前為止對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)建的表也消失了:

消失了的inst2和表

這就讓我疑惑了,查看了下docker commit命令的說明:

docker commit 說明

沒毛病啊,基于容器的變化創(chuàng)建一個新的鏡像。為了驗證docker commit 命令的可用性,我在mysqldock中創(chuàng)建一個新的文件,再commit成新鏡像,再創(chuàng)建容器,查看發(fā)現(xiàn)新的容器的確是包含了新創(chuàng)建的文件,也就是說docker commit的確能夠基于容器的變化創(chuàng)建新的鏡像:

新建文件出現(xiàn)在了新的鏡像中

那我就丈二和尚摸不著頭腦了,為啥新建的文件可以保存下來,新建的庫就不行呢,新建的庫不也是在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里有這樣的信息:

Mounts

這說明了這個容器將容器內(nèi)的/var/lib/mysql路徑作為volume掛載。查看容器內(nèi)的該路徑發(fā)現(xiàn)的確是mysql數(shù)據(jù)庫的數(shù)據(jù)文件,這點在mysql的dockerHub主頁也能發(fā)現(xiàn):

/var/lib/mysql下的數(shù)據(jù)

我馬上試了一下在該文件下新建一個文件,并且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ù)中:

mysqldock和mysqlnew有一樣的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相應(yīng)命令刪除volume;缺點是volume在宿主機上比較難定位,在宿主機上直接操作volume比較困難。
bind mount是直接將宿主機文件系統(tǒng)上的文件路徑映射到容器中,兩邊雙向同步,顯而易見,有缺點也有優(yōu)點,優(yōu)點是可以直接訪問,也可以被別的程序使用,比如我們打包一個本地應(yīng)用到本地/target路徑,我們就可以把這個路徑使用bind mount的方式掛在到依賴他的應(yīng)用的docker容器中,這樣本地應(yīng)用打包后,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進行掛載就行了:

使用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ù)卷的一些用法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 剛出生的嬰兒,甚至是懷孕時的母親感覺孩子什么都不知道,就算你說話,唱歌它們也沒反應(yīng),但是我們可能都錯了。是大...
    濰坊谷德DDM徐芳閱讀 241評論 0 0
  • 最近勵志教育文章的一個高頻題目是:莫讓少年時代的放縱換來一生的卑微。言辭犀利,戳人軟肋。于是很多類似的“卑微...
    牛立生閱讀 284評論 0 1
  • 2008年的時候,還在上高二的我大概是為了嘗試一下那種瞇著眼睛飄飄欲仙的感覺點上了人生的第一支煙,后來又在一群男生...
    困在碗里的魚閱讀 413評論 0 1
  • 遇到一位面試者,三十五六歲的年紀,從事業(yè)編換到國企,共待過兩家單位。現(xiàn)在的工作也做了五六年了,最近動了跳槽的心思。...
    譽燦松松閱讀 259評論 1 0
  • 在韋伯看來,所謂學者、學術(shù),尤其是人文社科類,不是教你怎么更好的去認識世界,以及如何讓人生更有意義。 這種價值選擇...
    飛行少女_閱讀 175評論 0 0