Docker的大坑小窪 一

2021-06-28 23:01:35 字數 3089 閱讀 3146

posted on march 2, 2015march 2, 2015

by 孫巨集亮

docker成為雲計算領域的新寵兒已經是不爭的事實,作為高速發展的開源專案,難免存在這樣或那樣的瑕疵。筆者最近在開發實戰中曾經跌進去一些坑,有些坑還很深,寫出來分享,相當於是在坑邊掛個警示牌,避免大家重蹈覆轍。話不多說,一起來領略docker的大坑小窪。

docker中同種型別的映象,一般會用tag來進行互相區分。如docker中的mysql映象,映象tag有很多種,有5.6.17,5.6.22,latest等。使用者的環境中若已經熟練使用mysql:5.6.17,並不代表使用者如果使用mysql:5.6.22,環境依舊工作。

不同tag同種型別的docker映象,會因為以下的原因導致映象差異。 (1).docker映象內容不同。同種型別docker映象的tag不同,很大程度上是因為映象中應用版本的差異。dockerfile代表docker映象的製作流程,換言之是dockerfile的不同,導致docker映象的不同。 (2).docker映象的entrypoint.sh不同。entrypoint.sh代表容器中應用程序按照何種形式啟動,entrypoint.sh的差異直接導致應用容器的使用差異。舉例說明:mysql:5.6.17和mysql:5.6.22的entrypoint.sh存在很大差異,兩者對於隔離認為重要的環境變數的定義就不一致,使用的差異自然存在。

不同tag的同型別映象作為替代品時,需謹慎。檢視docker映象layer層的差異,查閱dockerfile與entrypoint.sh的差異,可以提供起碼的保障。

在乙個時間點使用latest映象,應用容器執行正常;之後的另乙個時間點按照相應的dockerfile,build出映象再執行應用容器,失效。

docker官方關於同種型別docker映象的latest標籤,並未永久賦予某一指定的docker映象,而是會變化。舉例說明:某乙個時間點ubuntu映象的latest標籤屬於ubuntu:12.04,之後的另一時間點,該latest標籤屬於ubuntu:14.04,若dockerfile在這兩個時間點進行build時,結果必然相異。原因回歸至上文的第乙個坑。

慎用latest標籤,最好不用,docker映象都使用指定的tag。

使用fig部署兩個有依賴關係的容器a和b,容器a內部應用的啟動依賴於容器b內應用的完成。容器a內應用程式嘗試連線容器b內部應用時,由於容器b內應用程式並未啟動完畢,導致容器a應用程式啟動失敗,容器a停止執行。

容器的啟動分為三個階段,依次為dockerinit、entrypoint.sh以及cmd,三個階段都會消耗時間,不同的容器消耗的時間不一,這主要取決於docker容器中entrypoint和command到底做了什麼樣的操作。如mysql容器b的啟動,首先執行dockerinit;然後通過dockerinit執行entrypoint.sh,由於entrypoint.sh執行過程中需要執行mysql_install_db等操作,會佔據較多時間;最後由entrypoint.sh來執行cmd,執行真正的應用程式mysqld。綜上所述,從啟動容器到mysqld的執行,戰線拉得較長,整個過程docker daemon都認為mysql容器存活,而mysqld正常執行之前,mysql容器並未提供mysql服務。如果fig中的容器a要訪問mysql容器b時,雖然fig會簡單辨別依賴關係,讓b先啟動,再啟動a,當fig無法辨別容器應用的狀態,導致a去連線b時,b中應用仍然未啟動完畢,最終a一場退出。

對自身環境有起碼的預估,如從容器b的啟動到容器b內應用的啟動完畢,所需多少時間,從而在容器a內的應用程式邏輯中新增延時機制;或者使得a內應用程式邏輯中新增嘗試連線的機制,等待容器b內應用程式的啟動完畢。 筆者認為,以上解決方案只是緩解了出錯的可能性,並未**。

筆者的docker部署方式如下:在vsphere中安裝一台ubuntu 14.04的虛擬機器,在該虛擬機上安裝docker 1.4.1;將該虛擬機器製作vm使用的映象;建立虛擬機器節點時通過該映象建立,從而虛擬機器中都含有已經安裝好的docker。如果使用swarm管理這些虛擬機器上的docker daemon時,僅乙個docker node註冊成功,其他docker node註冊失敗,錯誤資訊為:docker daemon id已經被占用。

如果多個docker host上的docker daemon id一樣的話,swarm會出現docker node註冊失敗的情況。原理如下: (1).docker daemon在啟動的時候,會為自身賦乙個id值,這個id值通過trustkey來建立,trustkey存放的位置為~/.docker/key.json。 (2).如果在iaas平台,安裝了一台已經裝有docker的虛擬機器vm1,然後通過製作vm1的映象,再通過該映象在iaas平台上建立虛擬機器vm2,那麼vm1與vm2的key.json檔案將完全一致,導致docker daemon的id值也完全一致。

(1).建立虛擬機器之後,刪除檔案~/.docker/key.json ,隨後重啟docker daemon。docker daemon將會自動生成該檔案,且內容不一致,實現多docker host上docker daemon id不衝突。 (2).建立虛擬機器映象時,刪除key.json檔案。 建議使用方案二,一勞永逸。

dockerfile在build的過程中只要涉及訪問外網,全部失效。

使用者在建立docker容器的時候,不指定dns的話,docker daemon預設給docker container的dns設定為8.8.8.8和8.8.4.4。而在國內這個特殊的環境下,這兩個dns位址並不提供穩定的服務。如此一來,只要docker container內部涉及到網域名稱解析,則立即受到影響。

(1)使用docker run命令啟動容器的時候,設定–dns引數,引數值為受信的dns位址,必須保證該dns位址docker container可訪問。 (2)如果按以上做修改,適用於docker run命令。而使用docker build的時候其實是多個docker run的疊加,由於docker build沒有dns引數的傳入,因此docker container不能保證網域名稱的成功解析。

啟動docker daemon的時候設定docker_opts,新增–dns引數,這樣可以保證所有的docker run預設使用這個dns位址。   以上這些坑深淺不一,但基本上還都集中在docker外圍的配置,行為模式等方面。

最近雖然在docker的坑里摔得鼻青臉腫,但是「docker虐我千百遍,我待docker如初戀」的情懷始終不變,這貨一定是雲計算的未來,我堅信。前方的大坑,我來了,duang。。。 。。。

記憶體釋放的一些大坑

1 include2 3 include4 5 6 int main 7 33 問題1 free 釋放的原理是什麼,因為即使記憶體釋放後,也是有可能能對對其進行操作賦值訪問操作的,所以怎麼證明記憶體已經被釋放了?問題2 char p char malloc 100 p 6 char q p 6 fr...

踩了乙個MyBatis的大坑

最近在專案中做了乙個上傳檔案 類雲盤 功能。返回檔案列表的時候需要left join 使用者表,取得上傳者的名字。專案用的是mybatis,寫好之後發現,對於每乙個上傳者,只取到了他上傳的最後乙個檔案,看起來好像是變成了inner join。但是我在mysql workbench裡面用同樣的語句得到...

docker中的小技巧

這篇文章下的指令都是我在工作年中常用到的一些操縱docker中container的小技巧。docker run rm name auto exit test busybox latest echo hello world使用 read only標誌建立容器時,會將掛載的容器檔案系統設定為唯讀,防止容...