11 月 19 - 20 日 Apache Pulsar 社区年度盛会来啦,立即报名! 了解详情
写点什么

如何跨过 Docker 集群网络 Weave 遇到的“坑”?

  • 2019-11-13
  • 本文字数:2464 字

    阅读完需:约 8 分钟

如何跨过Docker集群网络Weave遇到的“坑”?

Weave 作为 Docker(一个开源的应用容器引擎)跨主机集群网络解决方案的一种,可以用于连接部署在多台主机上的 Docker 容器,使用网络的应用程序不必去配置端口映射、链接等信息。另外,Weave 的通信支持加密,用户可以从一个不受信任的网络连接到主机。


Weave 在控制层和 Calico 类似,在数据层通过 UDP 封装实现 L2 overlay。Weave 在 1.2 版本之前都是通过 usersapce 实现,在 Weave-1.2 版本之后,Weave 结合了内核 Open vSwitch 模块,实现了 Open vSwitch datapath(ODP)功能,结合 kernel 的 vxlan 特性,在网络性能上有较大提升。


由于 ODP 功能与内核相关模块结合较为紧密,因此在实际使用中可能会遇到一些与内核相关的“坑”。本文描述的这两个问题都跟内核有关系。


坑一:使用 Weave FastDb 造成网络中断

1 问题描述

在 Weave 的 1.2 版本之后,考虑到原先 sleeve 模式网络性能较差,故增加 FastDb 模式,该模式也成为 Weave 启动时的默认模式。在 FastDb 模式中使用了 kernel 中的 Open vSwitch 模块,做报文封装时使用 vxlan 协议。在使用 qemu-kvm 创建的云主机上,如果安装 CentOS7.0,内核版本为 kernel-3.10.123,那么在启动 Weave 并使用 FastDb 模式时,会造成 virtio_net 虚拟网卡无法发送数据,进而导致整个虚拟机的网络中断。


2 问题分析

导致网络断开的原因是由于触发了内核的一个 bug,该内核 bug 的 commit 链接地址:


http://t.cn/Ro53BsH。


触发该 bug 主要是因为 Weave 在初始化时会发送一个 60000 字节的 UDP 数据包进行 PMTU 探测,并且 Weave 发送使用的套接字为 raw socket,导致 virtio_net 使用的内存被污染,具体表现就是无法通知到宿主机上 vhost 获取数据,在接口上看到发送报文的计数始终不会增加。


该问题不是只有 Weave 才能触发,用普通应用程序建立 socket 时使用 raw socket,并且发送的数据大于接口的 MTU 值,接口的 UFO 功能是打开的,这些情况下都极有可能触发该问题,造成网络中断。






(图 1:FastDb 模式的数据流原理)


3 解决方法

1、升级内核,保证内核版本大于等于 3.13;

2、关闭虚拟机网卡的 ufo 特性;

3、CentOS7.1 的 kernel-3.10.229 内核已经修复了该问题。


(图 2:guest 通知 vhost 读取数据流程)


坑二:Weave 无法使用 FastDb 模式

1 问题描述

在内核版本 CentOS Linux (3.10.0-327.10.1.el7.x86_64) 7 (Core)上 ,Weave 版本大于 1.2,如果云主机的 MTU 值为 1450 或者小于 1474,Weave 启动时无法正常选择 Fast Data Path 模式。在 Weave 启动后一直选择 sleeve 模式,本应该默认模式为 FastDb,该问题也和内核的版本相关。


2 问题分析

Weave 的 Fast Data Path 路径使用到 ODP 技术,也就是内核中的 OVS 模块,在 Container 中直接发送数据包到 ovs 模块。在启动 Weave 时,会自动选择使用 sleeve 模式还是 FastDb 模式,这里通过发送心跳包来决定。出现该问题时,在云主机通过 Docker logs Weave 日志可以看到出错信息:“FastDb timed out waiting for vxlan heartbeat”。


heartbeat 数据包是一个 UDP 包,目的端口号为 6784,在某些云主机上接口的 MTU 值为 1454,但在发送 UDP 的 heartbeat 数据包时,发送的是 1474 字节,这样就会对报文在 IP 层进行分片,而在主机上发现心跳报文发送不出去,当 MTU 的值修改为 1500 后,就可以发送出去。


在 MTU 为 1454 的情况下,会出现下面的 ICMP 错误报文:


(图 3: 出现的错误 ICMP 报文)


上面出现错误的 ICMP 报文是内核中的 ip_fragment 函数调用 ICMP_send 函数发送的:


if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) ||(IPCB(skb)->frag_max_size &&IPCB(skb)->frag_max_size > mtu))) {IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);ICMP_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,htonl(mtu));kfree_skb(skb);return -EMSGSIZE;}
复制代码


通过上述代码可以看出,如果出现错误 ICMP 报文,下面的判断条件 iph->frag_off & htons(IP_DF)) && !skb->ignore_df 需要成立。通过对抓取的报文分析可知 iph->frag_off & htons(IP_DF))的值为真,那么 skb->ignore_df 值需要为 0,而此处的关键在于 skb->ignore_df 的值是何时赋值为 0 的。


通过分析 Weave 发送心跳包的流程可知,在 vxlan_tnl_send 函数中,对 skb->ignore_df 赋值为 1,最后调用 tunnel 的发送函数 iptunnel_xmit 时,调用了 skb_scrub_packet 函数,在该函数中又重新对 skb->ignore_df 赋值为 0(kernel 版本为:3.10.0-327.el7),造成后续发送报文时,ICMP 目的不可达,并且错误码为 ICMP_FRAG_NEEDED 的报文。


void skb_scrub_packet(struct sk_buff *skb, bool xnet){skb->tstamp.tv64 = 0;skb->pkt_type = PACKET_HOST;skb->skb_iif = 0;skb->ignore_df = 0;skb_dst_drop(skb);secpath_reset(skb);nf_reset(skb);nf_reset_trace(skb);if (!xnet)return;skb_orphan(skb);skb->mark = 0;}
复制代码


上面代码是 CentOS7 的 3.10.0-327.el7,而在一些旧内核版本 3.10.0-123.el7 上,iptunnel_xmit 调用的是 secpath_reset(skb)函数,该函数并没有对 skb->local_df(低版本内核使用 local_df)进行重新初始化,也就是 skb->local_df 值仍旧为 1,因此在该版本上不会出现上述问题。


static inline voidsecpath_reset(struct sk_buff *skb){#ifdef CONFIG_XFRMsecpath_put(skb->sp);skb->sp = NULL;#endif}
复制代码


(图 4:内核版本不同造成设置不同)


虽然新的内核版本中存在该问题,不过内核本身没有问题,还是 Weave 用户态管理 datapath 程序与内核适配上出现问题(它并不是使用 ovs-switchd),在 OVS 中对 tunnel 类型可以设置为 df_default=false 进行分片。


3 解决方法

保证接口的 MTU 值为默认为 1500。


总 结

Weave 的 ODP 功能使用了内核特性,在使用 Weave 的 FastDb 功能时遇到上述两个问题都与内核密切相关。通过对内核层分析,可以定位到问题的根本原因,所以后续遇到类似问题时,可以多从内核角度进行考虑。


本文转载自公众号 UCloud 技术(ID:ucloud_tech)。


原文链接:


https://mp.weixin.qq.com/s/h8g3aXu0oD5FfXEnv72Kwg


2019-11-13 17:32548

评论

发布
暂无评论
发现更多内容

ansible 循环

耳东@Erdong

ansible 8月日更 ansible 循环

ShardingSphere源码解析 初步准备

Java 源码 ShardingSphere

vue入门:vuex概括与使用

小鲍侃java

8月日更

Yarn资源调度框架

布兰特

YARN

zookeeper集群怎么搞?

卢卡多多

zookeeper 8月日更

在线BMI身体质量指数计算器

入门小站

工具

模块一作业2(毕设构架设计)

Laintime

如此爱国,必然自毁长城

箭上有毒

8月日更

模块五作业

seawolflin

架构训练营

Android开发:新建后缀为txt的文件并且使用的步骤

三掌柜

8月日更 8月

手撸二叉树之合并二叉树

HelloWorld杰少

8月日更 数据结构算法

为了完成小姐姐安排的打分系统,又熬了一个小时的夜补充视图与模板

梦想橡皮擦

8月日更

【六顶思考帽】蓝色思考帽

LeifChen

8月日更 创新思维 蓝色思考帽

四种常见的 POST 提交数据方式

一个大红包

8月日更

Vue进阶(五十三):vue-cli 脚手架 webpack.prod.conf.js 配置文件详解

No Silver Bullet

Vue 8月日更

架构训练营第一模块作业(1)微信业务构架图学习

Laintime

架构实战营

IDEA2020.1构建Spring5.2.x源码

4ye

Java spring 源码 后端 8月日更

Android.mk

Changing Lin

8月日更

Java技术开发专题系列之【Guava Collections】实战使用相关Guava不一般的集合框架

洛神灬殇

Java Guava 8月日更 Guava Collections

Go- 字符串

HelloBug

索引 字符串 Go 语言 拼接 长度

@ConditionOnClass的使用

Rubble

8月日更

CSS的设计模式(二)BEM与SMACSS

Augus

CSS 8月日更

模块一课后作业

穿裤子的云

架构实战营

使用Redisson优雅关闭订单

码农参上

redission 8月日更

高尚小区

escray

生活记录 8月日更

Linux之uniq命令

入门小站

Linux

面试篇:虚拟机栈5连问,一听心里就乐了

阿Q说代码

面试 JVM 8月日更 虚拟机栈

Android EditText输入框实现下拉且保存最近5个历史记录

Andy阿辉

android Android 小菜鸟 编程思想 8月日更

Golang协程之了解管道的缓存能力

Regan Yue

协程 Go 语言 8月日更

【LeetCode】压缩字符串Java题解

Albert

算法 LeetCode 8月日更

几种常用设计模式的简单示例

编程三昧

JavaScript 大前端 设计模式 8月日更

如何跨过Docker集群网络Weave遇到的“坑”?_文化 & 方法_UCloud技术_InfoQ精选文章