我有一个在容器中运行的应用程序,该容器要求将一系列端口映射到该容器。
docker run -p 2000-3000:2000-3000 myapp
当我运行此docker命令时,我的开发vm陷入停顿。
然后查看进程,docker-proxy每个端口都有运行
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)
每个进程使用的专用存储器块(Pss在/proc/$pid/smaps)
Pss
/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规则,这是我期望在具有专用网络的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完全使用用户空间进程?
Nigel Brown在docker-proxy上写了一篇详细的文章,解释了如何以及为什么。
的docker-proxy,然后,是一个“捕获所有”用于允许容器端口转发到主机多克尔方法。但是,通常认为这docker- proxy是对上述问题的不佳解决方案,并且当暴露大量容器端口时,它将消耗大量内存。先前曾尝试删除对的依赖docker-proxy,但这与RHEL 6.x和CentOS 6.x中老化的内核的限制相抵触,Docker项目感到必须支持这些限制。因此,在docker- proxy当前版本1.5之前的所有Docker版本中,这些 仍然是Docker经验的主要组成部分。在我撰写本文时,即将发布1.6版,并且已经采取了一些措施来删除对docker- proxy,我将在另一篇文章中介绍。
docker- proxy
Docker现在包括一个守护程序运行时选项,以使用禁用用户态代理--userland- proxy=false。这是在v1.7中引入的。
--userland- proxy=false
禁用userland代理时,似乎存在一些边缘案例错误。还有IPV6问题
GitHub上存在一个开放的问题,默认情况下禁用userland代理(Docker不再支持RHEL6)。
除了以这种方式实现之外,似乎没有其他原因。一个进程应该能够处理容器的所有端口映射
该 代理实现和包装看起来干净和内置转到功能使用所以这可能只是转到最初的垃圾收集范围,允许它增长到5MB〜。
编辑 :内存使用已在Docker 1.12中得到了改进。每个端口仍然有一个进程,但是每个进程现在仅使用约750k的私有内存空间。