Linux内核bug引起Mesos、Kubernetes、Docker的TCP/IP数据包失效

简介: 本文讲的是Linux内核bug引起Mesos、Kubernetes、Docker的TCP/IP数据包失效,【编者的话】最近发现Linux内核bug,会造成使用veth设备进行路由的容器
本文讲的是Linux内核bug引起Mesos、Kubernetes、Docker的TCP/IP数据包失效 【编者的话】最近发现Linux内核bug,会造成使用veth设备进行路由的容器(例如Docker on IPv6、Kubernetes、Google Container Engine和Mesos)不检查TCP校验码(checksum),这会造成应用在某些场合下,例如坏的网络设备,接收错误数据。这个bug可以在三年前任何一个测试过的内核版本中发现。补丁已经被整合进核心代码,正在回迁入3.14之前的多个发行版中(例如SuSE,Canonical)。如果在自己环境中使用容器,强烈建议打上此补丁,或者等发布后,部署已经打上补丁的核心版本。

注:Docker默认NAT网络并不受影响,而实际上,Google Container Engine也通过自己的虚拟网络防止硬件错误。
编者:Jake Bower指出这个bug跟一段时间前发现的另外一个bug很相似。有趣!

起因

​十一月的某个周末,一组Twitter负责服务的工程师收到值班通知,每个受影响的应用都报出“impossible”错误,看起来像奇怪的字符出现在字符串中,或者丢失了必须的字段。这些错误之间的联系并不很明确指向Twitter分布式架构。问题加剧表现为:任何分布式系统,数据,一旦出问题,将会引起更长的字段出问题(他们都存在缓存中,写入日志磁盘中,等等...)。经过一整天应用层的排错,团队可以将问题定位到某几个机柜内的设备。团队继续深入调查,发现在第一次影响开始前,扇入的TCP纠错码错误大幅度升高;这个调查结果将应用从问题中摘了出来,因为应用只可能引起网络拥塞而不会引起底层包问题。

编者:用“团队”这个词可能费解,是不是很多人来解决这个问题。公司内部很多工程师参与排错,很难列出每个人名字,但是主要贡献者包括:Brian Martin、David Robinson、Ken Kawamoto、Mahak Patidar、Manuel Cabalquinto、Sandy Strong、Zach Kiehl、Will Campbell、Ramin Khatibi、Yao Yue、Berk Demir、David Barr、Gopal Rajpurohit、Joseph Smith、Rohith Menon、Alex Lambert and Ian Downes、Cong Wang。

一旦机柜被移走,应用失效问题就解决了。当然,很多因素可以造成网络层失效,例如各种奇怪的硬件故障,线缆问题,电源问题,等....;TCP/IP纠错码就是为保护这些错误而设计的,而且实际上,从这些设备的统计证据表明错误都可以检测到---那么为什么这些应用也开始失效呢?

隔离特定交换机后,尝试减少这些错误(大部分复杂的工作是由SRE Brain Martin完成的)。通过发送大量数据到这些机柜可以很容易复现失效数据被接收。在某些交换机,大约~10%的包失效。然而,失效总是由于核心的TCP纠错码造成的(通过netstat -a返回的TcpInCsumError参数报告),而并不发送给应用。(在Linux中,IPv4 UDP包可以通过设置隐含参数SO_NO_CHECK,以禁止纠错码方式发送;用这种方式,我们可以发现数据失效)。

Evan Jones(@epcjones)有一个理论,说的是假如两个bit刚好各自翻转(例如0->1和1->0)失效数据有可能有有效的纠错码,对于16位序字节,TCP纠错码会刚好抵消各自的错误(TCP纠错码是逐位求和)。当失效数据一直在消息体固定的位置(对32位求模),事实是附着码(0->1)消除了这种可能性。因为纠错码在存储之前就无效了,一个也纠错码bit翻转外加一个数据bit翻转,也会抵消各自的错误。然而,我们发现出问题的bit并不在TCP纠错码内,因此这种解释不可能发生。

很快,团队意识到测试是在正常linux系统上进行的,许多Twitter服务是运行在Mesos上,使用Linux容器隔离不同应用。特别的,Twitter的配置创建了veth(virtual ethernet)设备,然后将应用的包转发到设备中。可以很确定,当把测试应用跑在Mesos容器内后,立即发现不管TCP纠错码是否有效(通过TcpInCsumErrors增多来确认),TCP链接都会有很多失效数据。有人建议激活veth以太设备上的“checksum offloading” 配置,通过这种方法解决了问题,失效数据被正确的丢弃了。

到这儿,有了一个临时解决办法,Twitter Mesos团队很快就将解决办法作为fix推给了Mesos项目组,将新配置部署到所有Twiter的生产容器中。

排错

当Evan和我讨论这个bug时,我们决定由于TCP/IP是在OS层出现问题,不可能是Mesos不正确配置造成的,一定应该是核心内部网络栈未被发现bug的问题。

为了继续查找bug,我们设计了最简单的测试流程:
  1. 单客户端打开一个socket,每秒发送一个简单且长的报文​
  2. 单服务端(使用处于侦听模式的nc)在socket端侦听,打印输出收到的消息。
  3. 网络工具,tc,可以用于发送前,任意修改包内容。
  4. 一旦客户端和服务端对接上,用网络工具失效所有发出包,发送10秒钟。

可以在一个台式机上运行客户端,服务器在另外一个台式机上。通过以太交换机连接两台设备,如果不用容器运行,结果和我们预想一致,并没有失效数据被接收到,也就是10秒内没有失效包被接收到。客户单停止修改包后,所有10条报文会立刻发出;这确认Linux端TCP栈,如果没有容器,工作是正常的,失效包会被丢弃并重新发送直到被正确接收为止。
video1.gif

The way it should work: corrupt data are not delivered; TCP retransmits data

Linux 和容器

现在让我们快速回顾一下Linux网络栈如何在容器环境下工作会很有帮助。容器技术使得用户空间(user-space)应用可以在机器内共存,因此带来了虚拟环境下的很多益处(减少或者消除应用之间的干扰,允许多应用运行在不同环境,或者不同库)而不需要虚拟化环境下的消耗。理想地,任何资源之间竞争都应该被隔离,场景包括磁盘请求队列,缓存和网络。

Linux下,veth设备用于隔离同一设备中运行的容器。Linux网络栈很复杂,但是一个veth设备本质上应该是用户角度看来的一个标准以太设备。

为了构建一个拥有虚拟以太设备的容器,必须(1)创建一个虚机,(2)创建一个veth,(3)将veth绑定与容器端,(4)给veth指定一个IP地址,(5)设置路由,用于Linux流量控制,这样包就可以扇入扇出容器了。

为什么是虚拟造成了问题

我们重建了如上测试场景,除了服务端运行于容器中。然后,当开始运行时,我们发现了很多不同:失效数据并未被丢弃,而是被转递给应用!通过一个简单测试(两个台式机,和非常简单的程序)就重现了错误。
video2.gif

失效数据被转递给应用,参见左侧窗口。

我们可以在云平台重建此测试环境。k8s的默认配置触发了此问题(也就是说,跟Google Container Engine中使用的一样),Docker的默认配置(NAT)是安全的,但是Docker的IPv6配置不是。​

修复问题

重新检查Linux核心网络代码,很明显bug是在veth核心模块中。在核心中,从硬件设备中接收的包有ip_summed字段,如果硬件检测纠错码,就会被设置为CHECKSUM_UNNECESSARY,如果包失效或者不能验证,者会被设置为CHECKSUM_NONE。

veth.c中的代码用CHECKSUM_UNNECESSARY代替了CHECKSUM_NONE,这造成了应该由软件验证或者拒绝的纠错码被默认忽略了。移除此代码后,包从一个栈转发到另外一个(如预期,tcpdump在两端都显示无效纠错码),然后被正确传递(或者丢弃)给应用层。我们不想测试每个不同的网络配置,但是可以尝试不少通用项,例如桥接容器,在容器和主机之间使用NAT,从硬件设备到容器见路由。我们在Twitter生产系统中部署了这些配置(通过在每个veth设备上禁止RX checksum offloading)。

还不确定为什么代码会这样设计,但是我们相信这是优化设计的一个尝试。很多时候,veth设备用于连接统一物理机器中的不同容器。

逻辑上,包在同一物理主机不同容器间传递(或者在虚机之间)不需要计算或者验证纠错码:唯一可能失效的是主机的RAM,因为包并未经过线缆传递。不幸的是,这项优化并不像预想的那样工作:本地产生的包,ip_summed字段会被预设为CHECKSUM_PARTIAL,而不是CHECKSUM_NONE。

这段代码可以回溯到驱动程序第一次提交(commit e314dbdc1c0dc6a548ecf [NET]: Virtual ethernet device driver)。 Commit 0b7967503dc97864f283a net/veth: Fix packet checksumming (in December 2010)修复了本地产生,然后发往硬件设备的包,默认不改变CHECKSUM_PARTIAL的问题。然而,此问题仍然对进入硬件设备的包生效。

核心修复补丁 如下:
diff — git a/drivers/net/veth.c b/drivers/net/veth.c
index 0ef4a5a..ba21d07 100644
— — a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -117,12 +117,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
kfree_skb(skb);
goto drop;
}
- /* don’t change ip_summed == CHECKSUM_PARTIAL, as that
- * will cause bad checksum on forwarded packets
- */
- if (skb->ip_summed == CHECKSUM_NONE &&
- rcv->features & NETIF_F_RXCSUM)
- skb->ip_summed = CHECKSUM_UNNECESSARY;

if (likely(dev_forward_skb(rcv, skb) == NET_RX_SUCCESS)) {
struct pcpu_vstats *stats = this_cpu_ptr(dev->vstats);

结论

我对Linux netdev和核心维护团队的工作很钦佩;代码确认非常迅速,不到几个星期补丁就被整合,不到一个月就被回溯到以前的(3.14+)稳定发行版本中(Canonical,SuSE)。在容器化环境占优势的今天,很惊讶这样的bug居然存在了很久而没被发现。很难想象因为这个bug引发多少应用崩溃和不可知的行为!如果确信由此引发问题请及时跟我联系。

原文链接:Linux kernel bug delivers corrupt TCP/IP data to Mesos, Kubernetes, Docker containers(翻译:杨峰)

原文发布时间为: 2016-02-21
本文作者:hokingyang
本文来自云栖社区合作伙伴DockerOne,了解相关信息可以关注DockerOne。
原文标题:Linux内核bug引起Mesos、Kubernetes、Docker的TCP/IP数据包失效
相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
4月前
|
Kubernetes Docker Python
Docker 与 Kubernetes 容器化部署核心技术及企业级应用实践全方案解析
本文详解Docker与Kubernetes容器化技术,涵盖概念原理、环境搭建、镜像构建、应用部署及监控扩展,助你掌握企业级容器化方案,提升应用开发与运维效率。
832 108
|
3月前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1713 10
|
3月前
|
Kubernetes Devops Docker
Kubernetes 和 Docker Swarm:现代 DevOps 的理想容器编排工具
本指南深入解析 Kubernetes 与 Docker Swarm 两大主流容器编排工具,涵盖安装、架构、网络、监控等核心维度,助您根据团队能力与业务需求精准选型,把握云原生时代的技术主动权。
308 1
|
6月前
|
存储 Kubernetes 监控
Docker与Kubernetes集成挑战及方案
面对这些挑战,并不存在一键解决方案。如同搭建灌溉系统需要考虑多种因素,集成Docker与Kubernetes也需要深思熟虑的规划、相当的技术知识和不断的调试。只有这样,才能建立起一个稳定、健康、高效的Docker-Kubernetes生态,让你的应用像花园中的植物一样繁荣生长。
311 63
|
8月前
|
存储 Kubernetes 调度
Kubernetes、Docker和Containerd的关系解析
总的来说,Docker、Containerd和Kubernetes之间的关系可以用一个形象的比喻来描述:Docker就像是一辆装满货物的卡车,Containerd就像是卡车的引擎,而Kubernetes就像是调度中心,负责指挥卡车何时何地送货。
393 12
|
9月前
|
Kubernetes Docker 容器
Kubernetes与Docker参数对照:理解Pod中的command、args与Dockerfile中的CMD、ENTRYPOINT。
需要明确的是,理解这些都需要对Docker和Kubernetes有一定深度的理解,才能把握二者的区别和联系。虽然它们都是容器技术的二个重要组成部分,但各有其特性和适用场景,理解它们的本质和工作方式,才能更好的使用这些工具,将各自的优点整合到生产环境中,实现软件的快速开发和部署。
355 25
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
647 78
|
12月前
|
存储 Kubernetes 开发者
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
Docker 是一种开源的应用容器引擎,允许开发者将应用程序及其依赖打包成可移植的镜像,并在任何支持 Docker 的平台上运行。其核心概念包括镜像、容器和仓库。镜像是只读的文件系统,容器是镜像的运行实例,仓库用于存储和分发镜像。Kubernetes(k8s)则是容器集群管理系统,提供自动化部署、扩展和维护等功能,支持服务发现、负载均衡、自动伸缩等特性。两者结合使用,可以实现高效的容器化应用管理和运维。Docker 主要用于单主机上的容器管理,而 Kubernetes 则专注于跨多主机的容器编排与调度。尽管 k8s 逐渐减少了对 Docker 作为容器运行时的支持,但 Doc
475 5
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档