NSDI 2019 Notes

前言

NSDI 2019 里有两篇容器网络相关的话题,这篇还是比较有意思的,the morning paper 也谈到了这篇文章:https://blog.acolyer.org/2019/03/22/slim-os-kernel-support-for-a-low-overhead-container-overlay-network/。原版的视频、Slides、文章在 NSDI 官网都可以看:https://www.usenix.org/conference/nsdi19/presentation/zhuo。同时作者在 Github 上开源了实现:https://github.com/danyangz/Slim

大致思路是容器里的应用的流量送到另一个容器里的应用需要经过四次协议栈。

除了底层物理机的协议栈之外,主要是有一层 network namespace:

因此主要思路就是绕过这一层 stack,其效果还是不错的:

  • memcached 吞吐提高 71%,延迟降低 42%,CPU 占用减少 56%
  • Nginx CPU 占用减少 22-24%
  • PostgreSQL CPU 占用介绍 22%
  • Kafka CPU 占用减少 10%

介绍

容器网络往往使用 overlay 网络,但是 overlay 网络会带来显著地性能影响。测试显示 overlay 网络和 host 网络相比的吞吐会下降 23~48%,每个报文的延迟会增长 34~85%,CPU 占用会提高 93%,现有的加速技术往往是针对虚拟化的,对容器支持不够。

这里的核心问题就是一个包要在一个物理机上穿越两次协议栈,来回就是四次。这种设计显示受虚拟化的影响,因为虚拟机是有自己的协议栈的,宿主机不知道任何 Guest 的协议栈知识,但是容器不然,宿主机知道每个网络连接的完整信息。

因此作者设计了一种容器网络,核心思想就是让一个物理机上报文只经过一次协议栈。

这个设计有几个挑战:

  1. 网络虚拟化不能要求应用作出修改
  2. 需要支持与现在的网络相同的网络策略(network policy)
  3. 支持现在的容器网络相同的安全模型

Slim 的优点在上一节描述过了,缺点也有几个:

  1. 增加了连接建立的复杂性,因此连接建立慢了 106%;
  2. 不支持容器热迁移;
  3. network policy 由 packet-based 转为了 connection-based;
  4. 只支持 TCP 这种状态协议

背景

容器网络一般有几种通信模型:

  • bridge mode
  • host mode
  • macvlan mode
  • overlay mode

image.png-25.1kB

其中:

  • bridge mode 只用于同 host 通信;
  • host mode 性能好,但是管理和部署非常复杂,实际上像 Kubernetes 都不支持这种模型;
  • macvlan mode 类似于硬件机制(例如 SR-IOV),macvlan 让容器 IP 可以在 host 网络路由,但增加了数据中心网络路由的复杂性,因此大部分 cloud provider 已经 block 了 macvlan mode;
  • 所以最终最流行的方案还是 overlay,这个类似于虚拟机,目前有很多这样的实现,例如 Weave、Flannel、Docker Overlay 等。

overlay 网络的核心之一是 vswitch,提供了:

  • network bridging,允许同 host 通信
  • network tunneling,跨物理网络通信

vswitch 一般用 ovs,使用 vxlan 作为 tunneling protocol。

然后就是各种 network policy(例如 access control、rate limit、 QoS 等),具体实现往往通过 iptables、ovs 等。

这种 overlay 网络的问题很明显,就是基于 packet,因此在 network namespace 要经过打包、封包,在 host 上协议栈要再来一次包识别、封包,到了对端则要再解包、识别,再解包再识别,因此 overhead 就很重了。

下面的表格展示了实测中 overlay 带来的性能成本:

WX20190423-125222.png-69.5kB

下图展示了通过 Weave 实测的 CPU 占用成本:

WX20190423-125411.png-52.6kB

可以看到 CPU 主要陈本在 soft irq 上。

当然现在有一些技术来解决这个问题,例如 packet steering,创建多个 queue,对应每个 CPU 核心,用 hash 来 map 报文和 queue,这样跨核心的成本可以下降很多,下面的表格展示了使用 packet steering 带来吞吐和延迟提升:

image.png-60.5kB

packet steering 提升了 91% 的 TCP 吞吐,但并不能缓解延迟,而且 CPU 占用也有影响。

整体来说,这种容器网络,特别是 overlay 这种我们可以统称为 packet-based virtualization,可以用下图概括:

image.png-112.3kB

设计

Slim 通过减少报文进协议栈的次数来优化,其思路可以简称为 connection-based virtualization:

image.png-95.2kB

设计时有几个设计目标:

  1. 方便部署,支持未修改的应用;
  2. flexible,支持各种 network policy;
  3. 安全,容器不能去获得物理网络的信息们不能直接在 host network 上创建连接,或者提高 traffic priority

image.png-37.4kB

上图是 Slim 的整体设计,有三个主要组件:

  1. 用户态有一层叫做 SlimSocket,与应用相连接;
  2. 用户态的 SlimRouter,跑在 namespace 里;
  3. 一个小的可选的内核模块 SlimKernModule,可以实现一些高级功能,例如动态更改 access control rules、安全配置等

这里面 SlimSocket 暴露了 POSIX socket 接口,来 intercept 应用的 socket 相关 sys call。当 SlimSocket 探测到应用尝试建立连接,就发送请求到 SlimRouter,SlimRouter 负责创建连接,再以文件描述符(fd)返回给应用,下面应用就是用这个 fd 来 send/receive 报文。因为 SlimSocket 是 POSIX 接口,Slim 会将 SlimSocket 动态链接到应用,所以应用不需要改动。

下面是解决控制策略、安全策略,如果再回到 packet based 方法的话就太低效了,因此这里的方法是在创建 connection 之前通过 SlimRouter 检查。如果是连接建立起来之后规则改变了,那么 SlimRouter 会扫描所有现有连接然后 remove 不能满足规则的 fd。

这种将 host 的 fd 直接返回给容器内的进程显然会引起很多安全方面的 concern,为此 slim 提供了一个安全模式,当开启安全模式时,SlimKernModule 会限制返回给 container 的 fd 的能力,SlimKernModule 有三个功能:

  1. track fd 在 container 中 propagate;
  2. 根据 SlimRouter 请求 revoke fd;
  3. 禁止这个 fd 的不安全的 sys call(getpeername、connect、setsocket),SlimSocket 会对非恶意的应用的模拟这些 sys call。

下图是 blocking io 为例演示连接建立的整个过程:

image.png-88kB

这部分在 slide 中很好的演示,上图也很清晰,我就不详细介绍了。

现在很多应用都是使用异步 API 了(select、epoll),而不是上面演示的同步 API,以 epoll 为例,epoll_create 会创建一个 meta fd,meta fd 实际表示了一个 fd set,应用使用 epoll_wait 来等待 fd set 的任一事件。在连接建立时,我们需要修改 epoll fd set 中的 fd。SlimSocket 会通过 epoll_ctl track epoll fd 和 epoll fd set 的对应。比如 epoll fd set 中的 fd acceptconnect 时,SlimSocket 会将原 fd 从 set 中移除,并增加 host namespace 的 fd 到 set。

这个方案有一个限制是对现有的 IT 工具不友好(host 上使用的 IT 工具),如果你想用 iptables 什么的搞一些之前的一些 packet-based 的策略的话,就不能用 Slim。

实现

大致上在上一节都提到过了,SlimSocket、SlimRouter、SlimKernModule 分别用了 1184 行 C、1196 行 C++ 和 1438 行 C,SlimSocket 使用 LD_PRELOAD 来动态链接到应用,SlimSocket 和 SlimRouter 之间用 Domain Socket 通信。非安全模式时,SlimRouter 用 sendmsg 直接传递 fd 到 SlimSocket,安全模式则经过 SlimKernModule 通过 fd duplication method。

SlimRouter 通过 JSON 文件获得 access control 的内容,提供一个 CLI 来 reload JSON。rate limit 和 QoS 通过 tc 实现。

SlimRouter 和 SlimKernModule 之间通过 procfs 中的 dummy file 通信。

SlimKernModule 需要替换 sys call table 中的函数指针,用 hash table 保存 taged fd 和 unsafe 的 sys call 列表( MatheMatrix:tagged fd 原文说类似于 OSDI 10′ https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Enck.pdf,是 SlimKernModule 实现里很重要的内容,但我没有具体看)

评估

直接看图了,测试环境是 40G 网络,使用 iperf3、NPtcp、mpstat 这些开源软件评估。RFS 为 weave 打开 rfs 的性能。

image.png-144.5kB

下图展示了 network policy 的效果。

image.png-166kB

此外还有一些使用应用的测试结果,有 Memcache、Nginx、PostgreSQL、Kafka

image.png-155.3kB

image.png-279.2kB

image.png-200.5kB

image.png-212.4kB

此外作者还测试了容器迁移,Slim 会轻微的影响迁移性能。

image.png-156.7kB

讨论

  • 连接建立时间。Slim 会比较明显的延长连接建立时间,优化思路吗,就是尽量少关 SlimSocket,都需要一些方法;
  • 容器热迁移。前面虽然测了容器迁移,但其实不是热迁移,Docker 现在有实验项目 criu,但还比较弱,Slim 从设计上将,增加了容器热迁移的难度;
  • UDP。UDP 本质上可以支持,但问题是做网络策略就比较难了,另外就是 UDP 在数据中心的典型使用场景是减少连接建立时间,而 Slim 正好相反;
  • Packet-based 网络策略。这个实在不好做,真的需要的话只能用 overlay 网络;
  • LD_PRELOAD。这个其实对应用有要求,因为一些 system 可能假设了一些应用(例如 Go 编写的一些)是静态链接的;
  • Error code。一些情况下,error code 与 overlay 网络的结果不同,不过这个是可以解决的;
  • SmartNic。SmartNic 对容器网络可能帮助并不大,比如 Catapult 可能需要 Linux 的改变,SR-IOV 有 macvlan 一样的问题,FlexNIC 可能还行,但还是实验阶段。

Leave a Reply

Your email address will not be published. Required fields are marked *