iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Kubernetes中如何进行网络分析Flannel
  • 622
分享到

Kubernetes中如何进行网络分析Flannel

2023-06-04 16:06:53 622人浏览 独家记忆
摘要

这期内容当中小编将会给大家带来有关kubernetes中如何进行网络分析Flannel,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Flannel是cereos开源的CNI网络插件,下图flannel官网

这期内容当中小编将会给大家带来有关kubernetes中如何进行网络分析Flannel,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

Flannel是cereos开源的CNI网络插件,下图flannel官网提供的一个数据包经过封包、传输以及拆包的示意图,从这个图片中可以看出两台机器的Docker0分别处于不同的段:10.1.20.1/24 和 10.1.15.1/24 ,如果从WEB App Frontend1 pod(10.1.15.2)去连接另一台主机上的Backend Service2 pod(10.1.20.3),网络包从宿主机192.168.0.100发往192.168.0.200,内层容器的数据包被封装到宿主机的UDP里面,并且在外层包装了宿主机的IP和Mac地址。这就是一个经典的overlay网络,因为容器的IP是一个内部IP,无法从跨宿主机通信,所以容器的网络互通,需要承载到宿主机的网络之上。

Kubernetes中如何进行网络分析Flannel

flannel支持多种网络模式,常用的是vxlan、UDP、hostgw、ipip以及GCe和阿里云等,vxlan和UDP的区别是:vxlan是内核封包,而UDP是flanneld用户态程序封包,所以UDP的方式性能会稍差;hostgw模式是一种主机网关模式,容器到另外一个主机上容器的网关设置成所在主机的网卡地址,这个和calico非常相似,只不过calico是通过BGP声明,而hostgw是通过中心的etcd分发,所以hostgw是直连模式,不需要通过overlay封包和拆包,性能比较高,但hostgw模式最大的缺点是必须是在一个二层网络中,毕竟下一跳的路由需要在邻居表中,否则无法通行。

在实际的生产环境中,最常用的还是vxlan模式,我们先看工作原理,然后通过源码解析实现过程。

安装的过程非常简单,主要分为两步:

第一步安装flannel

yum install flannel 或者通过kubernetes的daemonset方式启动,配置flannel用的etcd地址

第二步配置集群网络

curl -L Http://etcdurl:2379/v2/keys/flannel/network/config -XPUT -d value="{\"Network\":\"172.16.0.0/16\",\"SubnetLen\":24,\"Backend\":{\"Type\":\"vxlan\",\"VNI\":1}}"

然后启动每个节点的flanned程序。

一、工作原理

1、容器的地址如何分配

Docker容器启动时通过docker0分配IP地址,flannel为每个机器分配一个IP段,配置在docker0上,容器启动后就在本段内选择一个未占用的IP,那么flannel如何修改docker0网段呢?

先看一下 flannel的启动文件 /usr/lib/systemd/system/flanneld.service

[Service]Type=notifyEnvironmentFile=/etc/sysconfig/flanneldExecStart=/usr/bin/flanneld-start $FLANNEL_OPTioNSExecStartPost=/opt/flannel/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker

文件里面指定了flannel环境变量和启动脚本和启动后执行脚本 ExecStartPost 设置的mk-docker-opts.sh,这个脚本的作用是生成/run/flannel/docker,文件内容如下:

DOCKER_OPT_BIP="--bip=10.251.81.1/24"DOCKER_OPT_IPMASQ="--ip-masq=false"DOCKER_OPT_MTU="--mtu=1450"DOCKER_NETWORK_OPTIONS=" --bip=10.251.81.1/24 --ip-masq=false --mtu=1450"

而这个文件又被docker启动文件/usr/lib/systemd/system/docker.service所关联,

[Service]Type=notifyNotifyAccess=allEnvironmentFile=-/run/flannel/dockerEnvironmentFile=-/etc/sysconfig/docker

这样便可以设置docker0的网桥了。

开发环境中,有三台机器,分别分配了如下网段:

host-139.245 10.254.44.1/24

host-139.246 10.254.60.1/24

host-139.247 10.254.50.1/24

2、容器如何通信

上面介绍了为每个容器分配IP,那么不同主机上的容器如何通信呢,我们用最常见的vxlan举例,这里有三个关键点,一个路由,一个arp,一个FDB。我们按照容器发包的过程,逐一分析上面三个元素的作用,首先容器出来的数据包会经过docker0,那么下面是直接从主机网络出去,还是通过vxlan封包转发呢?这是每个机器上面路由设定的。

 #ip route  show dev flannel.110.254.50.0/24 via 10.254.50.0 onlink10.254.60.0/24 via 10.254.60.0 onlink

可以看到每个主机上面都有到另外两台机器的路由,这个路由是onlink路由,onlink参数表明强制此网关是“在链路上”的(虽然并没有链路层路由),否则linux上面是没法添加不同网段的路由。这样数据包就能知道,如果是容器直接的访问则交给flannel.1设备处理。

flannel.1这个虚拟网络设备将会对数据封包,但下面一个问题又来了,这个网关的mac地址是多少呢?因为这个网关是通过onlink设置的,flannel会下发这个mac地址,查看一下arp表

# ip neig show dev flannel.110.254.50.0 lladdr ba:10:0e:7b:74:89 PERMANENT10.254.60.0 lladdr 92:f3:c8:b2:6e:f0 PERMANENT

可以看到这个网关对应的mac地址,这样内层的数据包就封装好了

还是最后一个问题,外出的数据包的目的IP是多少呢?换句话说,这个封装后的数据包应该发往那一台机器呢?难不成每个数据包都广播。vxlan默认实现第一次确实是通过广播的方式,但flannel再次采用一种hack方式直接下发了这个转发表FDB

# bridge fdb show dev flannel.192:f3:c8:b2:6e:f0 dst 10.100.139.246 self permanentba:10:0e:7b:74:89 dst 10.100.139.247 self permanent

这样对应mac地址转发目标IP便可以获取到了。

这里还有个地方需要注意,无论是arp表还是FDB表都是permanent,它表明写记录是手动维护的,传统的arp获取邻居的方式是通过广播获取,如果收到对端的arp相应则会标记对端为reachable,在超过reachable设定时间后,如果发现对端失效会标记为stale,之后会转入的delay以及probe进入探测的状态,如果探测失败会标记为Failed状态。之所以介绍arp的基础内容,是因为老版本的flannel并非使用本文上面的方式,而是采用一种临时的arp方案,此时下发的arp表示reachable状态,这就意味着,如果在flannel宕机超过reachable超时时间的话,那么这台机器上面的容器的网络将会中断,我们简单回顾试一下之前(0.7.x)版本的做法,容器为了为了能够获取到对端arp地址,内核会首先发送arp征询,如果尝试 

/proc/sys/net/ipv4/neigh/$NIC/ucast_solicit

此时后会向用户空间发送arp征询

/proc/sys/net/ipv4/neigh/$NIC/app_solicit

之前版本的flannel正是利用这个特性,设定

# cat   /proc/sys/net/ipv4/neigh/flannel.1/app_solicit3

从而flanneld便可以获取到内核发送到用户空间的L3MISS,并且配合etcd返回这个IP地址对应的mac地址,设置为reachable。从分析可以看出,如果flanneld程序如果退出后,容器之间的通信将会中断,这里需要注意。Flannel的启动流程如下图所示:

Kubernetes中如何进行网络分析Flannel

Flannel启动执行newSubnetManager,通过他创建后台数据存储,当前有支持两种后端,默认是etcd存储,如果flannel启动指定“kube-subnet-mgr”参数则使用kubernetes的接口存储数据。

具体代码如下:

func newSubnetManager() (subnet.Manager, error) {    if opts.kubeSubnetMgr {       return kube.NewSubnetManager(opts.kubeapiUrl, opts.kubeConfigFile)    }      cfg := &etcdv2.EtcdConfig{       Endpoints: strings.Split(opts.etcdEndpoints, ","),       Keyfile:   opts.etcdKeyfile,       Certfile:  opts.etcdCertfile,       CAFile:    opts.etcdCAFile,       Prefix:    opts.etcdPrefix,       Username:  opts.etcdUsername,       PassWord:  opts.etcdPassword,    }      // Attempt to renew the lease for the subnet specified in the subnetFile    prevSubnet := ReadCIDRFromSubnetFile(opts.subnetFile, "FLANNEL_SUBNET")      return etcdv2.NewLocalManager(cfg, prevSubnet) }

通过SubnetManager,结合上面介绍部署的时候配置的etcd的数据,可以获得网络配置信息,主要指backend和网段信息,如果是vxlan,通过NewManager创建对应的网络管理器,这里用到简单工程模式,首先每种网络模式管理器都会通过init初始化注册,

如vxlan

func init() {    backend.ReGISter("vxlan", New)

如果是udp

  func init() {    backend.Register("udp", New) }

其它也是类似,将构建方法都注册到一个map里面,从而根据etcd配置的网络模式,设定启用对应的网络管理器。

3、注册网络

RegisterNetwork,首先会创建flannel.vxlanID的网卡,默认vxlanID是1.然后就是向etcd注册租约并且获取相应的网段信息,这样有个细节,老版的flannel每次启动都是去获取新的网段,新版的flannel会遍历etcd里面已经注册的etcd信息,从而获取之前分配的网段,继续使用。

最后通过WriteSubnetFile写本地子网文件,

    # cat /run/flannel/subnet.env FLANNEL_NETWORK=10.254.0.0/16FLANNEL_SUBNET=10.254.44.1/24FLANNEL_MTU=1450FLANNEL_IPMASQ=true

通过这个文件设定docker的网络。细心的读者可能发现这里的MTU并不是以太网规定的1500,这是因为外层的vxlan封包还要占据50 Byte。

当然flannel启动后还需要持续的watch etcd里面的数据,这是当有新的flannel节点加入,或者变更的时候,其他flannel节点能够动态更新的那三张表。主要的处理方法都在handleSubnetEvents里面

    func (nw *network) handleSubnetEvents(batch []subnet.Event) { . . .         switch event.Type {//如果是有新的网段加入(新的主机加入)       case subnet.EventAdded:  . . .//更新路由表if err := netlink.RouteReplace(&directRoute); err != nil {    log.Errorf("Error adding route to %v via %v: %v", sn, attrs.PublicIP, err)    continue } //添加arp表log.V(2).Infof("adding subnet: %s PublicIP: %s VtepMAC: %s", sn, attrs.PublicIP, net.HardwareAddr(vxlanAttrs.VtepMAC))             if err := nw.dev.AddARP(neighbor{IP: sn.IP, MAC: net.HardwareAddr(vxlanAttrs.VtepMAC)}); err != nil {                log.Error("AddARP failed: ", err)                continue             } //添加FDB表             if err := nw.dev.AddFDB(neighbor{IP: attrs.PublicIP, MAC: net.HardwareAddr(vxlanAttrs.VtepMAC)}); err != nil {                log.Error("AddFDB failed: ", err)                                if err := nw.dev.DelARP(neighbor{IP: event.Lease.Subnet.IP, MAC: net.HardwareAddr(vxlanAttrs.VtepMAC)}); err != nil {                   log.Error("DelARP failed: ", err)                }                  continue             }//如果是删除实践      case subnet.EventRemoved://删除路由             if err := netlink.RouteDel(&directRoute); err != nil {                log.Errorf("Error deleting route to %v via %v: %v", sn, attrs.PublicIP, err)                       } else {             log.V(2).Infof("removing subnet: %s PublicIP: %s VtepMAC: %s", sn, attrs.PublicIP, net.HardwareAddr(vxlanAttrs.VtepMAC))             //删除arp            if err := nw.dev.DelARP(neighbor{IP: sn.IP, MAC: net.HardwareAddr(vxlanAttrs.VtepMAC)}); err != nil {                log.Error("DelARP failed: ", err)             } //删除FDB             if err := nw.dev.DelFDB(neighbor{IP: attrs.PublicIP, MAC: net.HardwareAddr(vxlanAttrs.VtepMAC)}); err != nil {                log.Error("DelFDB failed: ", err)             }               if err := netlink.RouteDel(&vxlanRoute); err != nil {                log.Errorf("failed to delete vxlanRoute (%s -> %s): %v", vxlanRoute.Dst, vxlanRoute.Gw, err)             }          }       default:          log.Error("internal error: unknown event type: ", int(event.Type))       }    } }

这样flannel里面任何主机的添加和删除都可以被其它节点所感知到,从而更新本地内核转发表。

上述就是小编为大家分享的Kubernetes中如何进行网络分析Flannel了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网精选频道。

--结束END--

本文标题: Kubernetes中如何进行网络分析Flannel

本文链接: https://www.lsjlt.com/news/238515.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • c#程序自启动怎么设置
    c# 程序的自启动方法有三种:注册表:在指定注册表项下创建新值,并将其设置为程序可执行文件路径。任务计划程序:创建一个新任务,并在触发器和动作部分分别指定登录时或特定时间触发,以及启动程...
    99+
    2024-05-14
    c#
  • c#怎么调用dll文件
    可在 c# 中轻松调用 dll 文件:引用 dll(使用 dllimport 特性)定义与 dll 函数签名匹配的函数原型调用 dll 函数(如同 c# 函数)附加技巧:使用 chars...
    99+
    2024-05-14
    c#
  • 如何构建 Golang RESTful API,并实现 CRUD 操作?
    通过创建 golang 项目并安装必要的包,我们可以构建一个功能齐全的 restful api。它使用 mysql 数据库进行 crud 操作:1. 创建和连接数据库;2. 定义数据结构...
    99+
    2024-05-14
    go crud mysql git golang
  • c#怎么添加类文件
    在c#中添加类文件的步骤:1. 创建新项目,2. 添加新类,3. 为类添加代码,4. 在另一个类中引用新类。using语句引用类文件所在的命名空间;new运算符创建类的新实例;点运算符访...
    99+
    2024-05-14
    c#
  • 使用 C++ 构建高性能服务器架构的最佳实践
    遵循 c++++ 中构建高性能服务器架构的最佳实践可以创建可扩展、可靠且可维护的系统:使用线程池以重用线程,提高性能。利用协程减少上下文切换和内存开销,提升性能。通过智能指针和引用计数优...
    99+
    2024-05-14
    c++ 高性能服务器架构 数据访问
  • c#怎么添加字段
    在 c# 中添加字段包括以下步骤:声明字段:在类或结构中使用 字段类型 字段名; 语法声明字段。访问修饰符:用于限制对字段的访问,如 private、public、protected 和...
    99+
    2024-05-14
    c#
  • c#中怎么添加引用
    c# 中添加引用的方法有四种:使用 nuget 包管理器添加软件包。添加项目引用以包含其他项目。手动编辑项目文件 (.csproj) 以添加引用。从编译器命令行使用 /reference...
    99+
    2024-05-14
    c#
  • c#怎么创建文本文件
    在 c# 中创建文本文件的方法包括:创建 filestream 对象以打开或创建文件。使用 streamwriter 写入文本至文件。关闭 streamwriter 对象释放资源。关闭 ...
    99+
    2024-05-14
    c#
  • c#怎么定义属性
    如何在 c# 中定义属性 属性是一种编程构造,它包含一个 get 访问器和一个 set 访问器,允许以一种类属性的方式访问字段。它们提供了一种安全且封装的方式来访问和修改类的内部数据。 ...
    99+
    2024-05-14
    c#
  • 基于 C++ 的服务器架构的安全性考虑因素
    在设计基于 c++++ 的服务器架构时,安全考虑至关重要:使用 std::string 或 std::vector 避免缓冲区溢出。使用正则表达式或库函数验证用户输入。采用输出转义防止跨...
    99+
    2024-05-14
    安全性 关键词: c++ 服务器架构 c++ lsp
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作