与 Docker 使用的网络瓶颈

    Docker 自 2013 年以来非常火热,无论是从 GitHub 上的代码活跃度,还是 Redhat 在 RHEL6.5 中集成对 Docker 的支持, 就连 Google 的 Compute Engine 也支持 Docker 在其之上运行。

    笔者使用 Docker 的原因和目的,可能与其他公司不太一样。我们一直存在”分发”需求,Docker 主要是用来屏蔽企业用户平台的不一致性。我们的企业用户使用的系统比较杂,仅仅主流系统就有 Ubuntu, Centos,RedHat,AIX 还有一些定制裁减系统等,可谓百花齐放。

    虽然 OpenResty 具有良好的跨平台特性,无奈我们的安全项目比较重,组件比较多,是不可能逐一适配不同平台的,工作量、稳定性等,难度和后期维护复杂度是难以想象的。如果您的应用和我们一样需要二次分发,非常建议考虑使用 Docker。这个年代是云的时代,二次分发其实成本很高,后期维护成本也很高,所以尽量做到云端。

    说说 Docker 与 OpenResty 之间的”坑”吧,你们肯定对这个更感兴趣。

    首次压测过程中发现 Docker 进程 CPU 占用率 100%,单机接口 4-5 万的 QPS 就上不去了。经过我们多方探讨交流,终于明白原来是网络瓶颈所致(OpenResty 太彪悍,Docker 默认的虚拟网卡受不了了 ^_^)。

    最终我们绕过这个默认的桥接网卡,使用 参数即可完成。

    多么简单,就这么一个参数,居然困扰了我们好几天。一度怀疑我们是否要忍受引入 Docker 带来的低效率网络。所以有时候多出来交流、学习,真的可以让我们学到好多。虽然这个点是我们自己挖出来的,但是在交流过程中还学到了很多好东西。

    Docker 支持的网络模式有:

    • none。关闭容器内的网络连接
    • host。允许容器使用 host 的网络堆栈信息。注意:这种方式将允许容器访问 host 中类似 D-BUS 之类的系统服务,所以认为是不安全的。
    • container。使用另外一个容器的网络堆栈信息。  

    None 模式

    将网络模式设置为 none 时,这个容器将不允许访问任何外部 router。这个容器内部只会有一个 loopback 接口,而且不存在任何可以访问外部网络的 router。

    Bridge 模式

    Docker 默认会将容器设置为 bridge 模式。此时在主机上面将会存在一个 docker0 的网络接口,同时会针对容器创建一对 veth 接口。其中一个 veth 接口是在主机充当网卡桥接作用,另外一个 veth 接口存在于容器的命名空间中,并且指向容器的 loopback。Docker 会自动给这个容器分配一个 IP ,并且将容器内的数据通过桥接转发到外部。

    Host 模式

    当网络模式设置为 host 时,这个容器将完全共享 host 的网络堆栈。host 所有的网络接口将完全对容器开放。容器的主机名也会存在于主机的 hostname 中。这时,容器所有对外暴露的端口和对其它容器的连接,将完全失效。

    Container 模式

    比如当前有一个绑定了本地地址 localhost 的 Redis 容器。如果另外一个容器需要复用这个网络堆栈,则需要如下操作: