问题描述:
使用Docker run container 的时候, 容器在启动几秒后自动退出 , 或者不退出,但里面的服务无法启动成功。 此例的服务是用 java -jar 来启动一个服务。 使用 docker logs 来查看日志时,只有一句日志: library initialization failed - unable to allocate file descriptor table - out of memoryAborted (core dumped)
解决思路及过程:
- 1 既然是报内存分配过程中,由于内存不够而 Aborted。 那就增大点儿内存再试试, 结果并没有成功。 我在其它机器启动这个java服务只分配了2G内存就启动成功,此处增大到8G, 仍无法成功。 所以这并不是内存不足导致的。
- 2 上网搜索,得到答案是需要设置 ulimit 下 nofile 和 nproc 这两个参数。 搜索到的结果有两种, 一种是说主机系统默认值是1024, 这个太小需要调大值 65535 或 100000 或 1048576 。 另一种是说docker启动容器时,有时这个值太大,需要调低一些。 总的来说就是这个值大小不合适,需要调整。 调整步骤有两个:
- 调整主机的系统默认值:
- 我先用 ulimit -n 查看了一下, 系统默认值是1024 。 然后在 /etc/profile 中添加 ulimit -n 65535 。 然后使用 source /etc/profile 命令使配置生效。
- 调整Docker下的默认值, 这个网上的方法很多,此处就随便说两种
- 方法一: 在执行 docker run 命令时添加参数 --ulimit nofile=65535:65535 --ulimit nproc=65535:65535
例如:
docker run -itd -p 7010:7010 --name service-a 64b87045a6fa --ulimit nofile=65535:65535 --ulimit nproc=65535:65535
- 方法二:
- 在 /etc/systemd/system/ 目录下, 创建 docker.service.d 目录
- 进入该目录,创建一个文件,名为 docker.conf
- 在文件中加入以下配置:
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --default-ulimit nofile=65535:65535 -H fd://
- 啥意思呢? 就是说在执行docker命令时,直接默认使用该参数, 不需要在docker run 命令中定义了。
- 执行 systemctl daemon-reload (重启Docker daemon)
- 执行 systemctl restart docker (重启Docker 服务)
两边都配置好后, 再次开始测试, 然鹅结果依旧不行。 因为帖子上有很多赞,说明这个方法确实解决了一些道友的问题, 但我的问题不是如此。
- 3 我觉得也许是我第二步的配置有问题,但自己没发现导致问题依然存在。 于是就进入container中, 瞅瞅 container 中的 ulimit -n 值会是多少。 此时报了另外一个错: bash: ulimit: open files: cannot get limit: Operation not permitted.
再次搜索, 发现在Docker run命令中加上 --privileged=true 即可以有权限在container中执行这些命令。
例如: docker run -itd --privileged=true -p 7010:7010 --name service-a 64b87045a6fa 。 然后进入容器再次执行 ulimit -n 命令, 成功的输出了 65535 . 说明之前的配置确实是生效了的。
然鹅,就在这时发现, 原本几秒后就挂了的container, 这次不挂了。 以前不挂的container里面, java服务也能启来了, 不在报上面的错了。 问题解决了。 问题出在了这个 --privileged=true 参数上
分析:
- 虽然问题解决了, 但不能保证是 --privileged=true 的作用,还是这个参数再加上 nofile 和 nproc 的配置一起产生的作用。 然后我在另外一台同样的机器上,不改nofile 和 nproc, 只使用–privileged=true,发现也可以生效, 所以是 --privileged=true 单独作用产生的结果。
Privileged:
By default, Docker containers are “unprivileged” and cannot, for example, run a Docker daemon inside a Docker container. This is because by default a container is not allowed to access any devices, but a “privileged” container is given access to all devices (see the documentation on cgroups devices).
When the operator executes docker run --privileged, Docker will enable access to all devices on the host as well as set some configuration in AppArmor or SELinux to allow the container nearly all the same access to the host as processes running outside containers on the host. Additional information about running with --privileged is available on the Docker Blog.
这两句是官方文档给的解释。 意思是默认情况下,docker container 是以unprivileged方式运行的, 如果以 privileged 方式运行,就可以在一个container中再run一个docker daemon。之所以这样, 是因为 privileged 模式下, container可以有权访问主机里的任何设备, 还允许在AppArmor或SELinux中去修改一些配置, 也就是几乎可以在container中干和主机一样的事儿。
说实话,读了这个解释,我依然没明白为啥加了privileged就可以解决这个问题。 猜测大概是没有这个权限, container无法读取到主机的某些配置信息,但具体是哪个信息,还不得而知, 继续挖掘中…
—THE END—