【編者的話】Docker大規模應用後,若是你沒踩過坑,說出去確定沒人信。昨天就遇到一個ulimit的經典問題:業務Container內ulimit值過小,致使啓動失敗。ulimit問題,老生常談,可是在不一樣的場景與環境,表現出來的靈異,每每須要一些深刻的分析才能找到本因。本次問題就與OS版本,Docker版本及配置方法息息相關,咱們來回顧一下。linux
微博平臺業務通過去年三節Docker化後,已穩定運行半年多了,因爲採用的都是較保守的版本,雖然也踩過不少坑,但都在可控內。最近正與RD一塊兒推動一個大項目,前提也是平臺業務全Docker化,這部分已完成90%了。其基本信息以下:
1)OS版本:CentOS 6.5
2)JDK: 1.7.0_25 Tomcat: 7.0.42
3)Docker:1.3.2
4)Docker Registry:1.0
其中:正在也推動升級OS到CentOS 7,Docker到1.6.2。git
本次出現的問題現象:當人工重啓服務器後,再啓動業務Container,發現啓動失敗,而且可以重現。
PS: 用運維繫統作部署時並未發現此問題。
github
重現的主要條件爲:
1)版本:OS(CentOS 6.5),Docker 1.3.2,Docker Daemon隨開機啓動而啓動。
2)主機配置:ulimit設置爲200000,配置在/etc/profile。
3)操做:手工reboot機器後,登陸,啓動業務容器,啓動起來後,一會就失敗。docker
1)服務器重啓,此過程正常。
2)業務Container啓動後不久失敗,經分析爲容器內ulimit不對,只得到默認的值:1024。後面會講這個值爲何是1024。
3)重啓Docker Daemon進程,再啓動Container,發現一切正常。經查看容器內ulimit值爲:主機設置的200000。
現象搞清楚後,且能重現,解決問題就很簡單了,方法不少。後面細說。shell
一句話總結,當服務器重啓後,Docker Daemon隨之系統啓動而啓動,當啓動Container時,因未獲取到主機設置的ulimit值而致使啓動後一下子就失敗了,重啓Docker Deamon後解決(PS:這不是解決問題的方法,這只是瞎貓撞到死耗子)。安全
關於ulimit問題,我相信只要是作過SA,或者玩過服務器的RD都應該遇到過,各類現象,其理論依據其實很簡單,你們能夠參考@淘寶褚霸 博客的文章,有4篇,足夠細緻了。2013年時,我問過他一個問題,他還從源碼角度去分析了,這種分享精神棒棒噠。這裏就再也不展開說了。bash
這裏就直接說CentOS(Redhat類)的啓動過程:你們都知道目前Linux系統啓動最經常使用的就是兩種:init(SysVinit系)與Systemd系兩大陣營,兩者的對比能夠參考此文。Systemd主要是CentOS 7及以後的版本採用,而以前的發行版均採用SysVinit系,而咱們此次出現問題的是CentOS 6.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窗口了。ide
對於環境變量加載順序,各發行版本大同小異,這裏也只說RedHat系的,其大體順序以下:
--> /etc/profile #全局環境變量,每一個用戶第一次登陸時設置 -------->~/.bash_profile #用戶級環境變量,每一個用戶第一次登陸時設置 -------->~/.bash_login -------->~/.profile -->~/.bashrc #用戶級環境變量,每一個用戶登陸時設置,打開新Shell時也設置 -->/etc/bashrc -->~/.bash_logout #用戶級環境變量,退出時執行
規則:後面的配置文件繼續前面的變量及shell設置,相同的被覆蓋。
Docker在1.6版本及以後,才支持ulimit相關選項,看GitHub上,應該是有人提了PR,後來官方纔支持的。 在1.6版本以前,Docker Container繼承自Docker Daemon的ulimit設置。 參考文章見Docker blog 之ulimit部分。
通過以上詳細介紹及理論引導,其實對於問題覆盤就很簡單了,咱們簡要過一下重點:
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:覆盤很簡單吧,不過不理解上述原理,不少人仍是一頭霧水,至少我見到是這樣。
1. 問題描述
當OS爲CentOS 6.5,Docker爲1.3.2版本狀況下的問題理清後,想試試7,因而在7下依然部署了Docker 1.3.2,並進行測試,新問題來了,當主機(Host)上未進行設置時(即爲默認1024),啓動Container,發現Container內的ulimit是1048576。修改主機的ulimit,再重啓Docker Daemon,啓動Container,Container內仍是1048576,好奇怪。
2. 分析
通過與同事一塊兒看了下Docker源碼,Daemon啓動那部分,一會兒就明朗了。能夠看到,Docker Daemon針對不一樣的系統版本,其對ulimit的默認值設置大有差異。
1)CentOS 7 採用Systemd進行系統初始化,自動會調用Systemd下的啓動腳本docker.service,其申明默認值以下:
[Service] ExecStart=/usr/bin/docker -d -H fd:// MountFlags=slave LimitNOFILE=1048576 LimitNPROC=1048576 LimitCORE=infinity
2)CentOS 6的話,Docker Daemon啓動,並未設置默認值。參考:sysvinit-redhat。
3)而對於Debian類的系統,盡然也設置了默認值爲:1048576。參考。
在不少時候,對於單個容器來講,這樣的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 容器名
這裏有一篇介紹,能夠加深你的理解。
1) Docker容器默認移除sys_resource(Linux能力),於是ulimit -n設置只能改小沒法改大,改大會報錯:ulimit: open files: cannot modify limit: Operation not permitted。
2) CentOS 7下docker run
可使用–privileged
選項來不移除Linux能力,但Docker默認移除這個Linux能力確定是有安全方面的考量,所以儘可能別用該選項。
3) CentOS 6下要使用–privileged,Docker版本不能>=1.0.1,不然會報錯;stat /dev/.udev/db/cpuid:cpu0: no such file or directory
。
通過上面的討論,相應把問題應該說清楚了,也解釋清楚了。那麼CentOS 6下,除了上述的手動重啓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
1) @ARGV 指出,/etc/init.d/functions
會被全部隨系統啓動的服務調用,建議直接設置在../init.d/docker
啓動腳本里,建議有效,感謝指正。不過,這個至關於去修改Docker Daemon的自啓動腳本了。
2) @枯木-Linux,與之交流,發現最好的方案,仍是直接修改 /etc/sysconfig/docker 配置文件。贊一個。
白天出的問題,一個多小時把它理清並解決了,感受仍是蠻好的。深夜,頭腦清醒,就想寫篇長微博分享給你們,問題雖不難,但崇尚分享精神老是很好的。
PS:文章快速寫成,思路若是不清晰或由錯誤點,請幫助指出,萬分感謝。