ulimit 与 容器

介绍

ulimit 可以设置当前进程以及其子进程的资源使用量,此处讨论我们启动的docker 容器的资源限制。

ulimit使用可以参考ulimit 使用docker run 参数介绍

列表如下:

选项 [options] 含义 例子
-a 显示当前所有的 limit 信息。 ulimit – a;显示当前所有的 limit 信息。
-c 最大的 core 文件的大小, 以 blocks 为单位。 ulimit – c unlimited; 对生成的 core 文件的大小不进行限制。
-d 进程最大的数据段的大小,以 Kbytes 为单位。 ulimit -d unlimited;对进程的数据段大小不进行限制。
-f 进程可以创建文件的最大值,以 blocks 为单位。 ulimit – f 2048;限制进程可以创建的最大文件大小为 2048 blocks。
-l 最大可加锁内存大小,以 Kbytes 为单位。 ulimit – l 32;限制最大可加锁内存大小为 32 Kbytes。
-m 最大内存大小,以 Kbytes 为单位。 ulimit – m unlimited;对最大内存不进行限制。
-n 可以打开最大文件描述符的数量。 ulimit – n 128;限制最大可以使用 128 个文件描述符。
-p 管道缓冲区的大小,以 Kbytes 为单位。 ulimit – p 512;限制管道缓冲区的大小为 512 Kbytes。
-s 线程栈大小,以 Kbytes 为单位。 ulimit – s 512;限制线程栈的大小为 512 Kbytes。
-t 最大的 CPU 占用时间,以秒为单位。 ulimit – t unlimited;对最大的 CPU 占用时间不进行限制。
-u 用户最大可用的进程数。 ulimit – u 64;限制用户最多可以使用 64 个进程。
-v 进程最大可用的虚拟内存,以 Kbytes 为单位。 ulimit – v 200000;限制最大可用的虚拟内存为 200000 Kbytes。

关于 -H(Hard) 和 -S(Soft)参数:
-S 可以理解为当前生效的配置值
-H 可以理解为最大配置值,只能由root用户提升
参照链接,默认情况下ulimit的使用应该是等价于 -HS,也就是同时修改当前配置值和最大值。也就是说非root用户在一个shell窗口内只能降低配置,而不能提升配置

各项参数在docker容器中对应值

当前窗口设置ulimit

首先测试在当前窗口下设置ulimit值,然后创建容器
我们此处最大打开文件数(系统默认值为1024),我们创建一个能打开1200个文件的容器

ulimit -Hn 258
docker run --rm -it --ulimit nofile=1200 -v /home/mao/Documents/test_ulimit:/ulimit daocloud.io/python:3-onbuild bash

可以看到容器创建成功,测试同时打开文件数量

确实能够打开1200个文件句柄,看来运行环境下的ulimit对其不起作用

系统配置ulimit

设置没有生效应该是很正常的现象,因为docker是一个CS架构的,我们当前窗口只是用于发送创建命令,而不是真正的执行者,真正的执行者是 Docker Daemon。因此,我们尝试设置Docker Daemon的ulimit,

我们来设置系统参数
编辑 /etc/security/limits.conf,添加两行

root    hard    nofile  900
*       hard    nofile  900
root    soft    nofile  900
*       soft    nofile  900

重启系统,查看ulimit

[root@localhost ~]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 31087
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 900
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kby![](https://images2018.cnblogs.com/blog/1117030/201806/1117030-20180621162543780-1252623657.png)
tes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 31087
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
[root@localhost ~]# docker run --rm -it --ulimit nofile=1200 -v /home/mao/Documents/test_ulimit:/ulimit daocloud.io/python:3-onbuild bash

但是创建容器依旧成功,在容器内运行正常,能够打开1200个文件。

这就很奇怪了,我们来看看进程的限制

[root@localhost Documents]# ps aux | grep docker
root       1142  0.4  1.0 503536 83648 ?        Ssl  17:03   0:02 /usr/bin/dockerd
root       1432  0.2  0.2 236876 22672 ?        Ssl  17:03   0:01 docker-containerd --config /var/run/docker/containerd/containerd.toml
root       2751  0.0  0.2 176752 16684 pts/0    Sl+  17:07   0:00 docker run --rm -it --ulimit nofile=1200 -v /home/mao/Documents/test_ulimit:/ulimit daocloud.io/python:3-onbuild bash
root       2764  0.0  0.0   7640  3188 ?        Sl   17:07   0:00 docker-containerd-shim -namespace moby -workdir /var/lib/docke /containerd/daemon/io.containerd.runtime.v1.linux/moby/ca96469d3d0325f370312f76181a0054b2eb76a11961498fbb83677c1ca6afe7 -address /var/run/docker/containerd/docker-containerd.sock -containerd-binary /usr/bin/docker-containerd -runtime-root /var/run/docker/runtime-runc
root       2868  0.0  0.0 112708   972 pts/1    S+   17:12   0:00 grep --color=auto docker
[root@localhost Documents]# cat /proc/1142/limits
Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max data size             unlimited            unlimited            bytes
Max stack size            8388608              unlimited            bytes
Max core file size        unlimited            unlimited            bytes
Max resident set          unlimited            unlimited            bytes
Max processes             unlimited            unlimited            processes
Max open files            65536                65536                files
Max locked memory         65536                65536                bytes
Max address space         unlimited            unlimited            bytes
Max file locks            unlimited            unlimited            locks
Max pending signals       31087                31087                signals
Max msgqueue size         819200               819200               bytes
Max nice priority         0                    0
Max realtime priority     0                    0
Max realtime timeout      unlimited            unlimited            us
[root@localhost Documents]# cat /proc/2764/limits
Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max data size             unlimited            unlimited            bytes
Max stack size            8388608              unlimited            bytes
Max core file size        unlimited            unlimited            bytes
Max resident set          unlimited            unlimited            bytes
Max processes             unlimited            unlimited            processes
Max open files            65536                65536                files
Max locked memory         65536                65536                bytes
Max address space         unlimited            unlimited            bytes
Max file locks            unlimited            unlimited            locks
Max pending signals       31087                31087                signals
Max msgqueue size         819200               819200               bytes
Max nice priority         0                    0
Max realtime priority     0                    0
Max realtime timeout      unlimited            unlimited            us

我们设置的900限制并没有生效,后来想了想,看到这篇文章,原因可能是系统守护进程是先启动的docker服务,然后才读取的limits.conf参数,因此这个参数设置并没有生效。

调整docker.service

既然是系统服务的原因,那么我们就去调整系统服务参数

查看 /lib/systemd/system/docker.service

[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target docker.socket firewalld.service
Wants=network-online.target
Requires=docker.socket

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd://
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=1048576
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity
# Uncomment TasksMax if your systemd version supports it.
# Only systemd 226 and above support this version.
TasksMax=infinity
TimeoutStartSec=0
# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes
# kill only the docker process, not all processes in the cgroup
KillMode=process
# restart the docker process if it exits prematurely
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s

[Install]
WantedBy=multi-user.target

在这个文件中我们可以看到系统的打开文件最大值为1048576,我们可以试试创建相应的容器
尝试运行docker run -d --name=b1 --ulimit nofile=1048576 busybox top,成功运行
尝试运行docker run -d --name=b2 --ulimit nofile=1048577 busybox top,报错:

但是这个参数是否真的有效呢?
使用了这个参数之后,我们查看 /proc/pid/limits,可以看到容器进程的limits确实被限制到了这个范围内。但是这个参数仅仅是docker run 的默认参数,你依然可以通过--ulimit参数超出这个限制。
比如我在docker.service中设置LimitNOFILE=990,但是我们仍然可以创建--ulimit nofile=1200的容器。

这个时候就出现了一个奇怪的现象:

[root@localhost system]# vi docker.service
[root@localhost system]# systemctl daemon-reload
[root@localhost system]# systemctl restart docker
[root@localhost system]# docker run --rm -it --ulimit nofile=1200 -v /home/mao/Documents/test_ulimit:/ulimit daocloud.io/python:3-onbuild bash
root@bd021ea0b5db:/usr/src/app#

然后重新启动一个shell

[root@localhost ~]# ps aux | grep docker
root       2835  1.0  0.8 544764 65920 ?        Ssl  17:49   0:00 /usr/bin/dockerd
root       2840  1.1  0.2 301292 21608 ?        Ssl  17:49   0:00 docker-containerd --config /var/run/docker/containerd/containerd.toml
root       2949  1.5  0.2 176752 18664 pts/0    Sl+  17:49   0:00 docker run --rm -it --ulimit nofile=1200 -v /home/mao/Documents/test_ulimit:/ulimit daocloud.io/python:3-onbuild bash
root       2965  0.1  0.0   7512  2704 ?        Sl   17:49   0:00 docker-containerd-shim -namespace moby -workdir /var/lib/docke /containerd/daemon/io.containerd.runtime.v1.linux/moby/bd021ea0b5dba32f14b27af41177251d6e5742f739e8fcfe54116c55b272c19c -address /var/run/docker/containerd/docker-containerd.sock -containerd-binary /usr/bin/docker-containerd -runtime-root /var/run/docker/runtime-runc
root       3074  0.0  0.0 112704   968 pts/1    S+   17:49   0:00 grep --color=auto docker
[root@localhost ~]# cat /proc/2965/limits
Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max data size             unlimited            unlimited            bytes
Max stack size            8388608              unlimited            bytes
Max core file size        unlimited            unlimited            bytes
Max resident set          unlimited            unlimited            bytes
Max processes             unlimited            unlimited            processes
Max open files            990                  990                  files
Max locked memory         65536                65536                bytes
Max address space         unlimited            unlimited            bytes
Max file locks            unlimited            unlimited            locks
Max pending signals       31087                31087                signals
Max msgqueue size         819200               819200               bytes
Max nice priority         0                    0
Max realtime priority     0                    0
Max realtime timeout      unlimited            unlimited            us

可以看到虽然它的容器进程的limits限制是990,但是我们创建的容器的ulimit却依然可以达到1200
在容器中进行测试:

root@bd021ea0b5db:/ulimit# ulimit -n
1200
root@bd021ea0b5db:/ulimit# python test_fh.py
open 1 file descriptors
open 2 file descriptors
open 3 file descriptors
...
open 1195 file descriptors
open 1196 file descriptors
open 1197 file descriptors
Traceback (most recent call last):
  File "test_fh.py", line 9, in <module>
OSError: [Errno 24] Too many open files: 'file1197'

看来真的可以同时打开1200个文件,这一点很奇怪。可能是容器的隔离机制做的很好的原因。

另外一个问题是这个1048576是从哪里来的呢,我从Ubuntu16.04的docker.service中看到的这个值,但是centos7中打开看到的却是“infinity”,但是创建容器的时候参数最大还是只能为1048576。
因此我认为这是系统中的一个最大值,但是这个最大值是否有效呢?
我们可以查看 /etc/sysctl.conf,看到一个参数 fs.file-max=786960。创建了一个--ulimit nofile=1048576 的容器,在其中打开文件,最终发现创建文件数量达到70万左右的时候就会报错退出了,因此,/etc/sysctl.conf中的值才是真正的系统限制。

其他参数

顺便测一下docker.service中的其他设置,中设置

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd
ExecReload=/bin/kill -s HUP $MAINPID
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.

LimitFSIZE=666666
LimitCPU=666666
LimitDATA=666666
LimitSTACK=666666
LimitCORE=666666
LimitRSS=666666
LimitMEMLOCK=666666
LimitNPROC=666666
LimitNOFILE=666666
LimitMEMLOCK=666666
LimitLOCKS=666666

重启docker服务后,运行容器

[root@localhost system]# docker run --rm -it  busybox sh
/ # ulimit -a
-f: file size (blocks)             1302
-t: cpu time (seconds)             666666
-d: data seg size (kb)             651
-s: stack size (kb)                651
-c: core file size (blocks)        1302
-m: resident set size (kb)         651
-l: locked memory (kb)             651
-p: processes                      666666
-n: file descriptors               666666
-v: address space (kb)             unlimited
-w: locks                          666666
-e: scheduling priority            0
-r: real-time priority             0
/ # exit

可以看到这些默认参数均生效了。

结论

docker的ulimit参数在docker.service中创建,作用于默认容器。创建容器时可以通过--ulimit参数来修改,可以超过设置的值。但是一旦设置之后,无法在容器内提升。

ulimit参数存在一个上限,这个上限是由系统决定的,实际使用过程中也有一个上限,这个上限是由sysctl命令控制的。

posted @ 2018-06-21 16:26  四度  阅读(4044)  评论(0编辑  收藏  举报