为什么Docker会运行这么多的进程来映射端口到我的应用程序?

我有一个应用程序运行在一个容器中,需要一系列的端口映射到它。

docker run -p 2000-3000:2000-3000 myapp 

当我运行这个docker命令时,我的开发vm停下来。

然后看看进程,每个端口都有一个docker-proxy运行

 $ ps -ef ... root 19796 7835 0 03:31 ? 00:00:00 docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 4000 -container-ip 172.17.0.4 -container-port 3000 root 19804 7835 0 03:31 ? 00:00:00 docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 3999 -container-ip 172.17.0.4 -container-port 2999 root 19812 7835 0 03:31 ? 00:00:00 docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 3998 -container-ip 172.17.0.4 -container-port 2998 ... $ ps -ef | grep -c docker-proxy 1003 

他们都是docker守护者的孩子

 root@default-docker:~# pstree -p init(1)-+-VBoxService(1251) |-acpid(1277) |-crond(1235) |-docker(7835)-+-docker-containe(7841)-+-docker-containe(8031)---gitlab-ci-multi(8048) | | |-docker-containe(9678)---mysqld(9693) | | `-docker-containe(20577)---registry(20591) | |-exe(19796) | |-exe(19804) | |-exe(19812) 

每个进程使用大量的私有内存(在/proc/$pid/smaps

 $ for pid in $(pgrep exe); do printf "pid:%5s mem:%5s\n" $pid $(awk '/^Pss:/{t=t+$2}END{print t}' /proc/$pid/smaps); done ... pid:28534 mem: 4011 pid:28543 mem: 3817 pid:28552 mem: 4001 

对于每个端口也有DNAT规则,这就是我所期望的在具有私有networking的Linux主机上完成的规则​​。

 root@default-docker:~# iptables -t nat -vnL DOCKER Chain DOCKER (2 references) pkts bytes target prot opt in out source destination ... 0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:4000 to:172.17.0.4:3000 0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:3999 to:172.17.0.4:2999 0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:3998 to:172.17.0.4:2998 ... 

为什么Docker每个端口启动一个进程?
为什么每个进程需要4-6MB的内存?
为什么Docker完全使用用户空间进程?

为什么Docker完全使用用户空间进程?

奈杰尔布朗已经写了一篇关于docker代理的详细文章,解释了如何和为什么。

然后, docker-proxy就是一个允许容器端口转发到Docker主机的“全部捕获”方法。 然而,一般认为docker-proxy是解决上述突出问题的一个优雅的解决scheme,当大量的容器端口暴露出来时,会消耗大量的内存。 之前曾尝试去除docker-proxy的依赖docker-proxy ,但是这与RHEL 6.x和CentOS 6.x中老化内核的局限性是相矛盾的,Docker项目感觉有责任支持RHEL 6.x和CentOS 6.x。 因此,Docker docker-proxy仍然是所有Docker版本到当前版本1.5的Docker体验的主要组成部分。 正如我所写,版本1.6是即将发布的,并且已经采取了一些措施来删除docker-proxy的自动需求,我将在另一篇文章中介绍。

Docker现在包含一个守护进程运行时选项来禁用用户态代理--userland-proxy=false 。 这是在v1.7中引入的。

在禁用userland代理时,似乎存在 一些边界案例错误 。 也有IPV6问题

有一个开放的GitHub问题,用于默认禁用userland代理 (Docker不再支持RHEL6)。

为什么Docker每个端口启动一个进程?

除此之外,似乎还没有其他原因。 一个进程应该能够处理一个容器的所有端口映射

为什么每个进程需要4-6MB的内存?

代理实现和包看起来是干净的,并使用内置的Gofunction,所以这可能只是Go的初始垃圾收集限制,允许它增长到〜5MB。

编辑 :内存使用情况在Docker 1.12中得到了改进 。 每个端口仍然有一个进程,但是现在每个进程只使用〜750k的私有内存空间。