深入理解docker ulimit

jopen 9年前發布 | 42K 次閱讀 Docker

 

一 問題:

1,背景:

微博平臺業務經過去年三節Docker化后,已穩定運行半年多了,由于采用的都是較保守的版本,雖然也踩過很多坑,但都在可控內。最近正與RD一起推進一個大項目,前提也是平臺業務全Docker化,這部分已完成90%了。其基本信息如下:

1)OS版本:Centos6.5

2)JDK: 1.7.0_25 Tomcat: 7.0.42

3)Docker:1.3.2

4)Docker Registry: 1.0

其中:正在也推進升級OS到centos7,docker到1.6.2

2,現象:

本次出現的問題現象:當人工重啟服務器后,再啟動業務Container,發現啟動失敗,并且能夠重現。

ps: 用運維系統做部署時并未發現此問題。

3,重現:

重現的主要條件為:

1)版本:os(centos6.5),docker 1.3.2, docker daemon隨開機啟動而啟動

2)主機配置:ulimit設置為200000,配置在/etc/profile

3)操作:手工reboot機器后,登錄,啟動業務容器,啟動起來后,一會就失敗。

4,分析:

1)服務器重啟,此過程正常

2)業務Container啟動后不久失敗,經分析為容器內ulimit不對,只獲得默認的值:1024. 后面會講這個值為什么是1024.

3)重啟Docker daemon進程,再啟動Container,發現一切正常。經查看容器內ulimit值為:主機設置的200000.

現象搞清楚后,且能重現,解決問題就很簡單了,方法很多。后面細說。

5,總結:

一句話總結:當服務器重啟后,Docker daemon隨之系統啟動而啟動,當啟動Container時,因未獲取到主機設置的ulimit值而導致啟動后一會兒就失敗了,重啟Docker Deamon后解決(ps:這不是解決問題的方法,這只是瞎貓撞到死耗子)。

二 經典理論之ulimit

關于ulimit問題,我相信只要是做過SA,或者玩過服務器的RD都應該遇到過,各種現象,其理論依據其實很簡單,大家可以參考@淘寶褚霸 博客的文章,有4篇,足夠細致了。13年時,我問過他一個問題,他還從源碼角度去分析了,這種分享精神棒棒噠。地址為: http://blog.yufeng.info/archives/tag/ulimit 。這里就不在展開說了。

三 經典理論之Linux系統啟動與環境變量加載順序

1,Linux系統啟動

這里就直接說CentOS(Redhat類)的啟動過程:大家都知道目前Linux系統啟動最常用的就是兩種:init(SysVinit系)與Systemd系兩大陣營,二者對比可參考:

http://images.linoxide.com/sys ... t.pdf 。Systemd主要是centos7及之后的版本采用,而之前的發行版均采用SysVinit系,而我們這次出現問題的是centos6.5,也就是SysVinit系。下面來看下它的啟動過程:

參考圖:

具體過程:

1)加載BIOS的硬件信息,執行BIOS內置程序

2)讀取MBR(Master Boot Record)中Boot Loader中的引導信息

3)加載內核Kernel boot到內存中

4)內核開始執行/sbin/init,并加載/etc/inittab,執行rc.sysinit進行初始化

5)啟動核心的外掛模塊/etc/modules.conf

6)按照啟動級別(服務器默認是3)執行/etc/rc.d/下運行腳本

即:[guansheng@xx-xx-xx-yf-core rc3.d]# pwd

/etc/rc.d/rc3.d/

這個過程會把chkconfig --list中看到3級下on的服務全部啟起來。

7)執行/bin/login程序

到這,你就可以看到登錄的tty窗口了。

2,Linux環境變量加載順序

對于環境變量加載順序,各發行版大同小異,這里也只說redhat系的:其大致順序如下:

--> /etc/profile #全局環境變量,每個用戶第一次登錄時設置

-->~/.bash_profile #用戶級環境變量,每個用戶第一次登錄時設置

-->~/.bash_login

-->~/.profile

-->~/.bashrc #用戶級環境變量,每個用戶登錄時設置,打開新Shell時也設置

-->/etc/bashrc

-->~/.bash_logout #用戶級環境變量,退出時執行

ps:其他不細說了。

規則:后面的配置文件繼續前面的變量及shell設置,相同的被覆蓋。

3,理論參考之docker ulimit:

docker在1.6版本及之后,才支持ulimit相關選項,看github上,應該是有人提了pr,后來官方才支持的,地址: https://github.com/docker/docker/pull/9437 。 在1.6版本之前,docker container繼承自 docker daemon的ulimit設置。 參考文章見docker blog 之ulimit部分:

地址: https://blog.docker.com/2015/0 ... -1-6/

四 問題復盤

經過以上詳細介紹及理論引導,其實對于問題復盤就很簡單了,我們簡要過一下重點:

1,由于我們人工重啟服務器后,根據上面的啟動過程可知,docker daemon在系統啟動時已經起來了,此時在用戶未登錄的情況下,并不會讀取我們設置的、/etc/profile下的ulimit配置,所以 docker daemon會以1024的值進行進程的啟動。

2,那么后續創建的container由于docker版本為1.3.2,是繼續自docker daemon的值,而造成在container只能看到1024的ulimit值,而業務上依賴大量的mc,mcq,redis,mysql及http 等,自然1024不夠用,而啟動失敗。

3,當用戶登錄后,重啟docker daemon后,進程自然會能讀取到 該用戶的環境變量,從而使ulimit設置為200000. 之后再啟動container就再無問題了。

ps:復盤很簡單吧,不過不理解上述原理,很多人還是一頭霧水,至少我見到是這樣。

五 新問題

探討centos7下的ulimit

1,問題描述:

當os為centos6.5,docker為1.3.2版本情況下的問題理清后,想試試7,于是在7下依然部署了docker 1.3.2,并進行測試,新問題來了,當主機(host)上未進行設置時(即為默認1024),啟動container,發現container內的 ulimit是1048576. 修改主機的ulimit,再重啟dokcer daemon,啟動container,container內還是1048576,好奇怪。

2,分析:

經過與同事一起看了下docker源碼,daemon啟動那部分,一下子就明朗了。

源碼部分: https://github.com/docker/dock ... /init

可以看到,docker daemon針對不同的系統版本,其對ulimit的默認值設置大有差別。

1), centos7 采用systemd進行系統初始化,自動會調用systemd下的啟動腳本docker.service,其申明默認值如下:

[Service]

ExecStart=/usr/bin/docker -d -H fd://

MountFlags=slave

LimitNOFILE=1048576

LimitNPROC=1048576

LimitCORE=infinity

2), centos6的話,docker daemon啟動,并未設置默認值。參考:sysvinit-redhat

3), 而對于debian類的系統,盡然也設置了默認值為:1048576. 參考:

https://github.com/docker/dock ... ocker

docker 1.6版本對ulimit的設置:

在很多時候,對于單個容器來說,這樣的ulimit實在是太高了。在Docker 1.6里,可以設置1) 全局默認的ulimit:

docker -d --default-ulimit nproc=1024:2048

docker -d --default-ulimit nofile=20480:40960 nproc=1024:2048

2) 在啟動容器時,單獨對其ulimit進行設置:

docker run -d --ulimit nofile=20480:40960 nproc=1024:2048 容器名

這里有一篇介紹,可以加深你的理解: http://dockone.io/article/302

對于docker ulimit的靈活設置,這里還有一個理論需要注意:

1) docker容器默認移除sys_resource(Linux能力),因而ulimit -n設置只能改小無法改大,改大會報錯:ulimit: open files: cannot modify limit: Operation not permitted。

2) centos7下docker run可以使用–privileged選項來不移除Linux能力,但docker默認移除這個Linux能力肯定是有安全方面的考量,因此盡量別用該選項

3) centos6下要使用–privileged,docker版本不能>=1.0.1,否則會報錯;stat /dev/.udev/db/cpuid:cpu0: no such file or directory。

六 優雅解決

經過上面的討論,相應把問題應該說清楚了,也解釋清楚了。那么centos6下,除了上述的手動重啟docker daemon方法解決外,還有其他方法嗎?答案是肯定的,有很多方法,這里簡要說一種吧,思路類似。

即:若使用sysV服務,則在/etc/init.d/functions最開頭添加一行:ulimit -u 204800 -HSn 204800

原理為:docker服務啟動腳本第一行會去執行它。

[guansheng@xx-xx-xx-yf-core ~]# ll /etc/rc.d/rc3.d/ |grep docker

lrwxrwxrwx 1 guansheng root 16 Jul 3 19:25 S95docker -> ../init.d/docker

ps: 這里網友@ARGV 指出,/etc/init.d/functions會被所有隨系統啟動的服務調用,建議直接設置在../init.d/docker啟動腳本里,建議有效,感謝指正。

七 總結

白天出的問題,一個多小時把它理清并解決了,感覺還是蠻好的。深夜,頭腦清醒,就想寫篇長微博分享給大家,問題雖不難,但崇尚分享精神總是很好的。

 

 
 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!