一、背景
话说笔者在上次的博客里简单的讲了一下调整MySQL最大连接数的方法。在文章的最后笔者提到了还有一些特殊情况比如说Docker中,会导致MySQL的最大连接数被限制在一个值上。今天笔者就要来讲一下为什么在Docker环境中会出现这个问题。
这次的问题也是在公司实习的时候碰到的。当时导师要笔者去部署一个LAMP环境(不要问笔者为什么用docker还要把apache+PHP和MySQL塞在一起,这个问题您得问笔者导师),然后要调整一下Apache和MySQL的最大连接数。在调整结束之后笔者就想着要不进去看看有没有设置成功,就进入Docker容器的MySQL控制台,查看MySQL的最大连接数,结果是笔者之前设置的最大连接数并没有生效,MySQL的最大连接数被限制在了214。
二、解决方案
今天就先讲一下解决方案吧,因为当时遇到这个问题的时候真的是没有一点思路。去百度搜索了很久都没有找到什么靠谱的结果。
最后抱着赌一赌的决心去Google上用英文搜了一下,结果在第一页就搜到了有人在stackoverflow提出的类似问题《Increasing mysql max_connections to 1024 in a docker container》,里面提出了一个解决方案:
在启动容器时加入参数:
--ulimit nofile=65536:65536
好的,那让我们试验一下行不行,在启动容器的时候加入--ulimit参数:
用docker exec命令进入容器,检查MySQL的max_connections变量值:
成功了!
三、思考
问题是解决了,但到底是为什么呢?
我们来看一下Docker官方对ulimit这个参数是怎么解释吧。
ulimit这个参数最初出现于Docker 1.6版本,在官方博客《Docker 1.6: Engine & Orchestration Updates, Registry 2.0, & Windows Client Preview》里,他们对ulimit参数的是这么解释的:
Ulimits
Up until now, containers inherit the ulimit settings from the docker daemon. This tends to be extremely high to account for production workloads, but is not ideal inside the container. Ulimits allow you to limit the resources of a given process (you may be familiar with the command line tool ulimit). With this new feature, you can now specify the default ulimit settings for all containers, when configuring the daemon. For example:
docker -d --default-ulimit nproc=1024:2048
This will set a soft limit of 1,024 and a hard limit of 2,048 child processes for all containers. You can set this option multiple times for different ulimit values: --default-ulimit nproc=1024:2408 --default-ulimit nofile=100:200
These settings can be overwritten when creating a container as such:
docker run -d --ulimit nproc=2048:4096 httpd
This will overwrite the default nproc value passed into the daemon.
Thanks to Brian Goff for this patch. If you are interested in the original pull request you can view it here.
个人渣翻:
Ulimits
直至当前,容器通过从docker守护进程继承ulimit设置,这对于生产工作量来说往往是非常高的,但在容器的内部却并不理想。Ulimits将允许您去限制给予进程的资源(您可能熟悉命令行工具ulimit)。通过这个新功能,您现在可以在设置守护程序时向所有的容器指定默认的ulimit设置。例如:
docker -d --default-ulimit nproc = 1024:2048
这将为所有容器设置1024个软限制和2048个硬限制的子进程限制。您可以对不同的ulimit值多次设置此选项:--default-ulimit nproc=1024:2408 --default-ulimit nofile=100:200
这些设置可以在创建容器本身时被重写:
docker run -d --ulimit nproc=2048:4096 httpd
这将重写之前传入守护程序中的默认nproc值。
感谢Brian Goff提供的补丁。如果您对原始的pull request感兴趣,您可以去这里查看。
简单的说呢就是,在Docker容器将继承Docker守护进程的ulimit设置,我们通过在启动容器的时候加上--ulimit nofile=65536:65536参数,重写了容器内部的nofile限制值。
四、其他修改默认设置方法
由于笔者要部署的容器都需要提高MySQL的最大连接数,每次启动的时候都要设置ulimit参数会变的非常繁琐。如果使用文档中提到的docker -d --ulimit这种方式如果以后突然要返回默认值就比较麻烦了。有没有什么一劳永逸且设置方便取消也方便的方法呢?答案当然是有的——修改docker守护进程ulimit设置。
由于这里用的系统是CentOS 6.9,所以不存在docker.service文件。但我们可以通过修改服务启动脚本来实现:
vim /etc/init.d/docker
在文件的开始部分加入以下代码:
ulimit -u 65536 -HSn 65536
保存后退出vim。
现在我们回到容器中,这次我们不添加--ulimit参数,看看能不能解除MySQL最大连接数214的限制。
和刚刚一样,用docker exec进入lamp容器内部,检查MySQL当前max_connections变量值:
可以看到,现在就算没有加入--ulimit参数,MySQL最大连接数也可以突破214的限制了。
五、写在最后
为什么在第四章笔者给出了一种修改服务配置文件的方式而不是通过官方提供的docker -d --ulimit方法去调整docker守护进程的ulimit值呢?主要的原因大概就是docker官方在后面的更新中,也是使用修改服务配置文件的方式设置了默认的ulimit设置。
在写本文时,笔者因为需要截图,所以打算在自己的笔记本中搭建环境,但一开始用的是CentOS 7系统,在实际的操作过程中就算没有加--ulimit参数,容器内的MySQL也可以突破最大连接数为214的限制,Ubuntu 16.04也是如此。只有CentOS 6才出现了这个问题。通过查看CentOS 7和Ubuntu 16.04的docker.service文件后,笔者发现在docker.service中,已经默认加入了LimitNOFILE和LimitNPROC这两项设置并默认给予了一个非常大的数值,所以这个问题就不会出现在CentOS 7和Ubuntu 16.04上了。
CentOS 7 docker.service文件部分截图:
Ubuntu16.04 docker.service文件部分截图:
所以笔者个人觉得通过修改docker守护进程的配置文件来设置ulimit值的方法更为妥善。
小柊
2017年8月2日 23:54:41