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