生产上发现ingress有个pod超时,重启pod正常以后过几十秒状态又变为异常,检查容器日志报错项:
2021/03/19 01:51:07 [emerg] 263#263: epoll_create() failed (24: No file descriptors available)
对比POD里边的nginx.conf参数,发现有些worker开头的参数比较奇怪。例如worker_processes很大,但是worker_rlimit_nofile很小。
daemon off;
worker_processes 64;
worker_rlimit_nofile 1024;
worker_shutdown_timeout 240s ;
events {
multi_accept on;
worker_connections 16384;
use epoll;
}
临时将worker_rlimit_nofile的值设置为102400,再reload了nginx发现就正常了。
翻了一下nginx-ingress-controller的源码,查找这些值是怎么计算出来的。其中重要的文件如下:
可以发现一些细节。
由于当前Ingress controller没有资源限制,看到的资源是看到宿主机的cpu core,一般都挺大的,例如64。根据实现规则,假设系统的open files限制,假设配置102400,那么 1024000/64-1024 =576.小于1024,那么就设置为1024。所以会看到一个很小的nofile设置。
当前的nginx-ingress-controller增加了一个init容器来进行最大打开文件数的控制:
sysctl -w fs.file-max=1048576
最终在容器里边看到的情况是
bash-5.0$ ulimit -n
809600
bash-5.0$ sysctl fs.file-max
fs.file-max = 1048576
程序读取的就是809600,这个值是怎么来的呢。宿主机看到的是,也是完全不搭边,如下所示。
dggpsprahi09192:~ # ulimit -n
1024
dggpsprahi09192:~ # sysctl fs.file-max
fs.file-max = 1048576
那么肯定是docker还做了一点手脚了。查阅suse、docker的材料。最终发现还有几个地方可以控制。
dggpsprahi09192:~ # grep Limit /usr/lib/systemd/system/docker.service
# Having non-zero Limit*s causes performance problems due to accounting overhead
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity
StartLimitBurst=3
StartLimitInterval=60s
路径在/etc/docker/daemon.json,不过这个宿主机没有这个文件。
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 64000,
"Soft": 64000
}
},
dggpsprahi09192:/opt/docker # cat /etc/sysconfig/docker
DOCKER_OPTS=" -b none --icc=false --log-level='info' --iptables=true -s devicemapper --default-ulimit nofile=809600:809600 --default-ulimit nproc=131072:131072 --live-restore --storage-driver=devicemapper --storage-opt dm.basesize=10G --storage-opt dm.mountopt=nodiscard --storage-opt dm.fs=ext4 --storage-opt dm.blkdiscard=false --storage-opt=dm.thinpooldev=/dev/mapper/docker-thinpool --log-opt max-size=10m --log-opt max-file=5 --log-driver=json-file --log-level=info --userland-proxy=false"
注意里边有设置–default-ulimit nofile=809600:809600,就是控制最大打开文件数的,和docker中看到的保持一致。
可以直接通过configmap来配置,覆盖自动计算的方式。参数如下:
json:"worker-processes,omitempty"
json:"max-worker-connections,omitempty"
json:"max-worker-open-files,omitempty"
详细可以参考官方文档的ConfigMaps。
了解nginx-ingress-controller的参数是如何设置之后,考虑到宿主机上的docker都是标准设置的,所以容器里边看到的ulimit -n是一致,结果是可预计的。 另一方面ingress controller不是独享宿主机资源的,还是要限制资源。最终决定通过限制资源来解决,保持资源是Guaranteed的,其他具体的worker参数根据资源自动计算。