为什么80%的码农都做不了架构师?>>>   

深入理解Docker ulimit

【编者的话】Docker大规模应用后,如果你没踩过坑,说出去肯定没人信。昨天就遇到一个ulimit的经典问题:业务Container内ulimit值太小,导致启动失败。ulimit问题,老生常谈,但是在不同的场景与环境,表现出来的灵异,往往需要一些深入的分析才能找到本因。本次问题就与OS版本,Docker版本及配置方法息息相关,我们来回顾一下。

问题

1. 背景

微博平台业务经过去年三节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。

2. 现象

本次出现的问题现象:当人工重启服务器后,再启动业务Container,发现启动失败,并且能够重现。

PS: 用运维系统做部署时并未发现此问题。

3. 重现

重现的主要条件为:
1)版本:OS(CentOS 6.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篇,足够细致了。2013年时,我问过他一个问题,他还从源码角度去分析了,这种分享精神棒棒哒。这里就不再展开说了。

经典理论之Linux系统启动与环境变量加载顺序

1. Linux系统启动

这里就直接说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窗口了。

2. Linux环境变量加载顺序

对于环境变量加载顺序,各发行版本大同小异,这里也只说RedHat系的,其大致顺序如下:

--> /etc/profile     #全局环境变量,每个用户第一次登录时设置
-------->~/.bash_profile #用户级环境变量,每个用户第一次登录时设置
-------->~/.bash_login
-------->~/.profile
-->~/.bashrc   #用户级环境变量,每个用户登录时设置,打开新Shell时也设置
-->/etc/bashrc
-->~/.bash_logout #用户级环境变量,退出时执行

规则:后面的配置文件继续前面的变量及shell设置,相同的被覆盖。

3. 理论参考之Docker ulimit

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:复盘很简单吧,不过不理解上述原理,很多人还是一头雾水,至少我见到是这样。

新问题

探讨CentOS 7下的ulimit

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=slaveLimitNOFILE=1048576LimitNPROC=1048576LimitCORE=infinity

2)CentOS 6的话,Docker Daemon启动,并未设置默认值。参考:sysvinit-redhat。
3)而对于Debian类的系统,尽然也设置了默认值为:1048576。参考。

Docker 1.6版本对ulimit的设置:

在很多时候,对于单个容器来说,这样的ulimit实在是太高了。在Docker 1.6里,可以设置
1) 全局默认的ulimit:

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

2)在启动容器时,单独对其ulimit进行设置:

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

这里有一篇介绍,可以加深你的理解。

对于Docker ulimit的灵活设置,这里还有一个理论需要注意:

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:文章快速写成,思路如果不清晰或由错误点,请帮助指出,万分感谢。

转载于:https://my.oschina.net/fdhay/blog/726394

深入理解Docker ulimit相关推荐

  1. 10张图带你深入理解Docker容器和镜像--云平台技术栈07

    导读:之前发布了云平台技术栈(ps:点击可查看),本文主要说一下其中的Docker! 翻译:杨润青 这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(container)和镜像(image ...

  2. 理解Docker——深入引擎室

    点击关注 异步图书,置顶公众号 每天与你分享 IT好书 技术干货 职场知识 本文大概 3301 字 读完共需 9 分钟 本文主要内容 Docker的架构 在用户的宿主机上追溯Docker的内部结构 使 ...

  3. 用一个实际例子理解Docker volume工作原理

    要了解Docker Volume,首先我们需要理解Docker文件系统的工作原理.Docker镜像是由多个文件系统的只读层叠加而成.当一个容器通过命令docker run启动时,Docker会加载只读 ...

  4. 10张图带你深入理解Docker容器和镜像

    本文用图文并茂的方式介绍了容器.镜像的区别和Docker每个命令后面的技术细节,能够很好的帮助读者深入理解Docker. 这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(containe ...

  5. docker ip地址_理解 Docker 网络(番外) -- 《Docker 源码分析》勘误

    前言 本来打算这篇文章是分析 Docker Overlay 网络是如何建立以及如何手动实现 Docker 的跨主机通信的.但是在完成了上一篇文章之后,打算找一些文章或者书籍印证我的文章是否正确.这时看 ...

  6. 用一个实际例子理解Docker volume工作原理 1

    要了解Docker Volume,首先我们需要理解Docker文件系统的工作原理.Docker镜像是由多个文件系统的只读层叠加而成.当一个容器通过命令docker run启动时,Docker会加载只读 ...

  7. DockerONE 干货 深入理解Docker容器和镜像

    这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(container)和镜像(image)之间的区别,并深入探讨容器和运行中的容器之间的区别. 当我对Docker技术还是一知半解的时候,我 ...

  8. 10 张图带你深入理解Docker容器和镜像

    此文中部分信息.图片需要 fan qiang , 如果未能正常显示,文末有原文连接 . [Kubernetes培训通知]DockOne将会于2018年5月18日在上海举办Kubernetes技术培训, ...

  9. 深入理解Docker Volume(一)

    本文讲的是深入理解Docker Volume(一),[编者的话]本文主要介绍了Docker Volume的原理以及使用方式,是Docker入门教程的延伸.作者通过从数据的共享.数据容器.备份.权限以及 ...

最新文章

  1. 5 个用 Python 编写 web 爬虫的方法
  2. 《c++语言导学》——1.7 常量
  3. Linux 如何快速找到运行中的进程
  4. python获取url文件名_python httplib / urllib获取文件名
  5. java文件名特殊字符_Java 8:用名字读取特殊字符的文件
  6. centos7.5 设置Mysql开机自启动
  7. Codeforces 1491 D. Zookeeper and The Infinite Zoo (二进制处理)
  8. Elasticsearch的Scroll操作
  9. mfc怎么获取进程的线程数_Python多线程获取小米应用商店App,看看我是怎么做到的
  10. (转)Windows系统、Linux系统 和 Mac OS操作系统 历史由来 与 区别?
  11. android 拦截外拨电话,Android拦截外拨电话程序示例
  12. USACO-Section2.1 Ordered Fractions(简单数据处理)
  13. Fancybox丰富的弹出层效果
  14. matlab runtime安装目录,matlab compiler runtime怎么安装
  15. 密封槽设计标准_密封槽设计标准
  16. CGO 之 Dll调用
  17. WIN10鼠标乱跳问题解决办法
  18. 门户通专访爱思网创始人韩笑:SNS网站必然走向实用化!
  19. 清华大学计算机系前景好么,华为正式宣布选择西工大,而不是清华计算机系,网友:为什么?...
  20. A.C.E的JAVA学习笔记--5/14/21

热门文章

  1. 移动端布局三种视口_什么是视口?移动端浏览器中的3种视口
  2. 微信公众平台开发入门教程
  3. idea_设置项目编码
  4. 有哪些值得推荐的计算机专业的竞赛?
  5. oracle的ofs,windows2003+oracle ofs 双机
  6. 恒源云(GPUSHARE)_Teacher Forcing训练小技巧来啦~
  7. VUE 保证输入框无法输入半角数字以外的字符
  8. 腹有诗书气自华,记2016年读过的好书
  9. 需要程序猿了解的 89 个操作系统核心知识
  10. 使用Matlab读取二进制数据文件