关于docker:docker容器高性能之文件句柄

4次阅读

共计 4441 个字符,预计需要花费 12 分钟才能阅读完成。

一、前言

  前段时间共事遇到了一个容器内文件句柄透露的问题,容器外部偶发性呈现 Too many open files 报错。过后的文件句柄配置如下:

问题是不是来了,主机的文件句柄和容器的文件句柄数有关系吗?是什么关系呢?这就是以下要探索探讨的问题。

  两头记录了试验的过程,导致篇幅有点长,心急的小伙伴们能够间接跳转至四章节《论断剖析》。

二、docker 容器启动流程

  docker 是典型的 client-server 类型的利用,能够应用 docker version 命令查看服务端和客户端的信息:

docker 启动容器的大抵流程如下:

注: 右边黄色文字框中对应的是在 Linux 下的常驻过程名称;

重点在 runc 创立容器子过程的时候,会传入相干的 namespace 参数进行资源隔离(用户隔离是基于 Linux 的 CLONE_NEWUSER 这个 namespace);

三、本地试验过程

  正好本地有个现成的 war 包,就用了 tomcat 容器来测试,用 Dockerfile 将 war 拷贝到 webapps 下就行了。
Dockerfile如下:

FROM tomcat:8.5.72-jdk8

RUN useradd dockertester -m -d /home/dockertester \
    && gpasswd -a dockertester dockertester \
    && chown dockertester:dockertester -R /usr/local/tomcat

ADD --chown=dockertester pftest.war /usr/local/tomcat/webapps

USER dockertester

CMD ["catalina.sh", "run"]
  • CentOS Linux release 7.4.1708 (Core)
  • docker-19.03.9
  • tomcat:8.5.72-jdk8

(一)应用 Linux 默认配置,2000 并发测试

环境筹备 1:

  1. 应用 Dockerfile 构建利用镜像;
  2. 应用locust【比拟省资源】进行并发申请,须要本人写压测脚本;
  3. ulimit -n查看以后文件句柄数限度(默认普通用户都是 1024):
 [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) 7216
 max locked memory       (kbytes, -l) 64
 max memory size         (kbytes, -m) unlimited
 open files                      (-n) 1024
 pipe size            (512 bytes, -p) 8
 POSIX message queues     (bytes, -q) 819200
 real-time priority              (-r) 0
 stack size              (kbytes, -s) 8192
 cpu time               (seconds, -t) unlimited
 max user processes              (-u) 7216
 virtual memory          (kbytes, -v) unlimited
 file locks                      (-x) unlimited
  1. cat /proc/sys/fs/file-max查看零碎最大句柄数限度(默认 181953);
[root@localhost ~]# cat /proc/sys/fs/file-max
181953
  1. 在容器内查看 java 过程以后文件句柄数限度状况(以后容器内 java 过程文件句柄数最大为 1048576,这数值哪来的前面解释):
dockertester@9798be4fb19c:/usr/local/tomcat$ cat /proc/$(ps -A | grep java | awk '{print $1}')/limits | grep "files"
Max open files            1048576              1048576              files     
  • 测试执行 2 分钟,没有任何报错。


  • 在容器内查看以后应用句柄数,发现曾经句柄数应用曾经达到3488,远超默认的1024
[root@localhost ~]# docker exec -it tomcat-test bash
dockertester@9798be4fb19c:/usr/local/tomcat$ cat /proc/sys/fs/file-nr
3488    0       181953

测试小结

  docker 容器内的文件句柄数限度不受 Linux 宿主机用户文件句柄数限度。应用普通用户启动容器内利用也是一样,本次试验就是新建了 dockertester 用户。

(二)批改容器的 limit 参数,2000 并发测试

  1. 环境筹备 1 的配置都不必批改;
  2. 容器启动命令增加限度参数:--ulimit nofile=1024:1024,容器启动实现后,在容器内查看 java 过程句柄数限度(曾经胜利批改为 1024):
dockertester@123da4ac778d:/usr/local/tomcat$ cat /proc/$(ps -A | grep java | awk '{print $1}')/limits | grep "files"
Max open files            1024                 1024                 files
  • 压测不到 1 分钟开始呈现大量报错:


  • 查看以后零碎句柄数应用状况,放弃在 2432(超出 1024 的那些是 Linux 宿主机其余过程占用的):
dockertester@123da4ac778d:/usr/local/tomcat$ cat /proc/sys/fs/file-nr
2432    0       181953
  • 查看 tomcat 的日志,能够发现大量 Too many open files 报错:

测试小结

  docker 容器内的句柄数受启动参数 --ulimit nofile=1024:1024 的限度。

(三)批改 Linux 零碎最大文件句柄数限度,2000 并发测试

环境筹备 2:

  1. 去掉容器启动命令增加限度参数:--ulimit nofile=1024:1024
  2. 批改零碎最大文件句柄数限度:
[root@localhost ~]# cat /proc/sys/fs/file-max
181953
[root@localhost ~]# echo 2000 > /proc/sys/fs/file-max
[root@localhost ~]# cat /proc/sys/fs/file-max
2000
  1. 容器内查看以后 java 过程文件句柄数限度(还是之前默认的 1048576):
dockertester@c8e0796a224a:/usr/local/tomcat$ cat /proc/$(ps -A | grep java | awk '{print $1}')/limits | grep "files"
Max open files            1048576              1048576              files
  • 压测执行不到 1 分钟,开始大量报错:


  • 在容器内查看以后文件句柄数应用状况,文件句柄数曾经用满:
bash: start_pipeline: pgrp pipe: Too many open files in system
bash: /bin/cat: Too many open files in system
  • tomcat 日志中存在大量 Too many open files 报错:

测试小结

  docker 容器内的句柄数受宿主机系统文件句柄数限度(/proc/sys/fs/file-max/etc/sysctl.conf 文件配置的参数)

四、论断剖析

  • 注: 本文不适用于 Rootless 形式装置的 docker 利用,针对这种形式装置的 docker 还未钻研过。

(一)namespace 过程隔离【隔离不齐全】

  1. docker 启动容器会基于 namespace 做过程隔离,且领有 CAP_SYS_RESOURCE 的能力,像文件句柄这样的参数 docker 能够进行重设,然而重设之后还是受宿主机零碎参数的限度,即 /proc/sys/fs/file-max/etc/sysctl.conf文件配置的参数;
     
  2. 默认 docker 内过程的启动用户为 root,容器内的root 权限要比宿主机的权限稍低。失常状况下,容器内无奈批改相似 /proc/sys/fs/file-max 这些系统配置,会呈现只读文件报错 bash: /proc/sys/fs/file-max: Read-only file system。但如果在docker run 的时候增加 --privileged 参数,就能够批改这些内核参数,而且宿主机的配置也会被同步批改,这是非常危险⚠️的事;
     
  3. docker 和宿主机共享内核,因而 cat /proc/sys/fs/file-max 这类查看零碎级别的命令在容器内和容器外执行都是一样的。还有 top 命令看到的 CPU 核数并不是 docker 启动参数限度的核数,还是宿主机的核数。

(二)反对 namespace 的内核配置项

参见官网文档:Configure namespaced kernel parameters (sysctls) at runtime

IPC(用于隔离过程间通信所需的资源)
  • kernel.msgmax, kernel.msgmnb, kernel.msgmni, kernel.sem, kernel.shmall, kernel.shmmax, kernel.shmmni, kernel.shm_rmid_forced
  • fs.mqueue 结尾的参数,fs.mqueue.*
Network 相干:
  • 相似 net.* 的配置参数;
  • 如果应用了 --network=host 选项,则不容许以 docker run --sysctl net.ipv4.ip_forward=1 someimage 形式启动;

(三)容器内过程默认文件句柄数的起源

  分割第二章的容器启动过程,容器过程 runc的子过程(创立完完结),runccontainerd 的子过程。在 fork 子过程的时候,子过程会继承父过程的所有信息,所以容器内过程默认文件句柄数是继承的 containerd 过程的句柄数限度。能够命令查看,其实就是之前默认的 1048576;

cat /proc/$(ps -A | grep containerd-PID | awk '{print $1}')/limits | grep "files"

containerd 过程的句柄数限度在 containerd.service 文件中默认设置了 1048576

  • 新版本的不是在 docker.service 中设置的了
  • 从 Docker 1.11 开始,Docker Daemon 不再蕴含任何运行时的代码,这部分逻辑迁徙独自的 OCI 兼容层,而 containerd 就在 daemonrunc所在的 OCI 层之间,负责容器的创立,而销毁工作交给shim

(四)一个有意思的用户命名空间映射

  在 Dockerfile 构建利用镜像的时候,如果指定非 root 启动容器,那么 docker 默认会映射 uid1000的用户,如果本地宿主机没有设置 uid 为 1000 的用户,容器启动后,ps -ef会看到用户那一列间接就显示 1000。

正文完
 0