介紹
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
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命令控制的。

