Docker實戰(zhàn)警示:此處大坑小洼,請繞行
Docker成為云計算領(lǐng)域的新寵兒已經(jīng)是不爭的事實,作為高速發(fā)展的開源項目,難免存在這樣或那樣的瑕疵。筆者最近在開發(fā)實戰(zhàn)中曾經(jīng)跌進去一些坑,有些坑還很深,寫出來分享,相當(dāng)于是在坑邊掛個警示牌,避免大家重蹈覆轍。話不多說,一起來領(lǐng)略Docker的大坑小洼。
1.Docker中同種類型不同tag的鏡像并非可互相替代
問題描述
Docker 中同種類型的鏡像,一般會用tag來進行互相區(qū)分。如Docker中的mysql鏡像,鏡像tag有很多種,有5.6.17,5.6.22,latest 等。用戶的環(huán)境中若已經(jīng)熟練使用mysql:5.6.17,并不代表用戶如果使用mysql:5.6.22,環(huán)境依舊工作。
原因剖析
不同tag同種類型的Docker鏡像,會因為以下的原因?qū)е络R像差異。 (1).Docker鏡像內(nèi)容不同。同種類型Docker鏡像的tag不同,很大程度上是因為鏡像中應(yīng)用版本的差異。Dockerfile代表 Docker鏡像的制作流程,換言之是Dockerfile的不同,導(dǎo)致Docker鏡像的不同。 (2).Docker鏡像的entrypoint.sh不同。entrypoint.sh代表容器中應(yīng)用進程按照何種形式啟動,entrypoint.sh的差異直接導(dǎo)致應(yīng)用容器的使用差異。舉例說明:mysql:5.6.17和mysql:5.6.22的 entrypoint.sh存在很大差異,兩者對于隔離認為重要的環(huán)境變量的定義就不一致,使用的差異自然存在。
解決方案
不同tag的同類型鏡像作為替代品時,需謹慎。查看Docker鏡像layer層的差異,查閱Dockerfile與entrypoint.sh的差異,可以提供起碼的保障。
2.不同時間段使用tag為latest的鏡像,效果不盡相同
問題描述
在一個時間點使用latest鏡像,應(yīng)用容器運行正常;之后的另一個時間點按照相應(yīng)的Dockerfile,build出鏡像再運行應(yīng)用容器,失效。
原因剖析
Docker 官方關(guān)于同種類型Docker鏡像的latest標(biāo)簽,并未***賦予某一指定的Docker鏡像,而是會變化。舉例說明:某一個時間點ubuntu鏡像的 latest標(biāo)簽屬于ubuntu:12.04,之后的另一時間點,該latest標(biāo)簽屬于ubuntu:14.04,若Dockerfile在這兩個時間點進行build時,結(jié)果必然相異。原因回歸至上文的***個坑。
解決方案
慎用latest標(biāo)簽,***不用,Docker鏡像都使用指定的tag。
3.使用fig部署依賴性強的容器時出錯
問題描述
使用fig部署兩個有依賴關(guān)系的容器A和B,容器A內(nèi)部應(yīng)用的啟動依賴于容器B內(nèi)應(yīng)用的完成。容器A內(nèi)應(yīng)用程序嘗試連接容器B內(nèi)部應(yīng)用時,由于容器B內(nèi)應(yīng)用程序并未啟動完畢,導(dǎo)致容器A應(yīng)用程序啟動失敗,容器A停止運行。
原因剖析
容器的啟動分為三個階段,依次為dockerinit、entrypoint.sh以及cmd,三個階段都會消耗時間,不同的容器消耗的時間不一,這主要取決于docker容器中entrypoint和command到底做了什么樣的操作。如mysql容器B的啟動,首先執(zhí)行dockerinit;然后通過 dockerinit執(zhí)行entrypoint.sh,由于entrypoint.sh執(zhí)行過程中需要執(zhí)行mysql_install_db等操作,會占據(jù)較多時間;***由entrypoint.sh來執(zhí)行cmd,運行真正的應(yīng)用程序mysqld。綜上所述,從啟動容器到mysqld的運行,戰(zhàn)線拉得較長,整個過程docker daemon都認為mysql容器存活,而mysqld正常運行之前,mysql容器并未提供mysql服務(wù)。如果fig中的容器A要訪問mysql容器 B時,雖然fig會簡單辨別依賴關(guān)系,讓B先啟動,再啟動A,當(dāng)fig無法辨別容器應(yīng)用的狀態(tài),導(dǎo)致A去連接B時,B中應(yīng)用仍然未啟動完畢,最終A一場退出。
解決方案
對自身環(huán)境有起碼的預(yù)估,如從容器B的啟動到容器B內(nèi)應(yīng)用的啟動完畢,所需多少時間,從而在容器A內(nèi)的應(yīng)用程序邏輯中添加延時機制;或者使得A內(nèi)應(yīng)用程序邏輯中添加嘗試連接的機制,等待容器B內(nèi)應(yīng)用程序的啟動完畢。 筆者認為,以上解決方案只是緩解了出錯的可能性,并未根除。
4.Swarm管理多個Docker Node時,Docker Node注冊失敗
問題描述
筆者的Docker部署方式如下:在vSphere中安裝一臺ubuntu 14.04的虛擬機,在該虛擬機上安裝docker 1.4.1;將該虛擬機制作vm使用的鏡像;創(chuàng)建虛擬機節(jié)點時通過該鏡像創(chuàng)建,從而虛擬機中都含有已經(jīng)安裝好的docker。如果使用Swarm管理這些虛擬機上的docker daemon時,僅一個Docker Node注冊成功,其他Docker Node注冊失敗,錯誤信息為:docker daemon id已經(jīng)被占用。
原因剖析
如果多個Docker Host上的Docker Daemon ID一樣的話,Swarm會出現(xiàn)Docker Node注冊失敗的情況。原理如下: (1).Docker Daemon在啟動的時候,會為自身賦一個ID值,這個ID值通過trustKey來創(chuàng)建,trustkey存放的位置為~/.docker /key.json。 (2).如果在IaaS平臺,安裝了一臺已經(jīng)裝有docker的虛擬機vm1,然后通過制作vm1的鏡像,再通過該鏡像在IaaS平臺上創(chuàng)建虛擬機 vm2,那么vm1與vm2的key.json文件將完全一致,導(dǎo)致Docker Daemon的ID值也完全一致。
解決方案
(1) 創(chuàng)建虛擬機之后,刪除文件~/.docker/key.json ,隨后重啟Docker Daemon。Docker Daemon將會自動生成該文件,且內(nèi)容不一致,實現(xiàn)多Docker Host上Docker Daemon ID不沖突。 (2)創(chuàng)建虛擬機鏡像時,刪除key.json文件。 建議使用方案二,一勞永逸。
5.Docker容器的DNS問題
問題描述
Dockerfile在build的過程中只要涉及訪問外網(wǎng),全部失效。
原因剖析
用戶在創(chuàng)建docker容器的時候,不指定dns的話,Docker Daemon默認給Docker Container的DNS設(shè)置為8.8.8.8和8.8.4.4。而在國內(nèi)這個特殊的環(huán)境下,這兩個DNS地址并不提供穩(wěn)定的服務(wù)。如此一來,只要 Docker Container內(nèi)部涉及到域名解析,則立即受到影響。
docker run命令dns失效的解決方案
使用docker run命令啟動容器的時候,設(shè)定–dns參數(shù),參數(shù)值為受信的DNS地址,必須保證該DNS地址Docker Container可訪問。
以上解決方案的弊端
如果按以上做修改,僅適用于docker run命令。而使用docker build的時候其實是多個docker run的疊加,由于docker build沒有dns參數(shù)的傳入,因此docker container不能保證域名的成功解析。
docker build命令dns失效的解決方案
啟動Docker Daemon的時候設(shè)定DOCKER_OPTS,添加–dns參數(shù),這樣可以保證所有的docker run默認使用這個DNS地址。
以上這些坑深淺不一,但基本上還都集中在Docker外圍的配置,行為模式等方面。最近雖然在Docker的坑里摔得鼻青臉腫,但是“Docker虐我千百遍,我待Docker如初戀”的情懷始終不變,這貨一定是云計算的未來,我堅信。前方的大坑,我來了,duang。。。 。。。
原文:Docker 的大坑小洼