k8s与网络--Flannel源码分析

简介: 之前在k8s与网络--Flannel解读一文中,我们主要讲了Flannel整体的工作原理。今天主要针对Flannel v0.10.0版本进行源码分析。首先需要理解三个比较重要的概念: 网络(Network):整个集群中分配给 flannel 要管理的网络地址范围 子网(Subnet):flanne.

之前在k8s与网络--Flannel解读一文中,我们主要讲了Flannel整体的工作原理。今天主要针对Flannel v0.10.0版本进行源码分析。首先需要理解三个比较重要的概念:

  • 网络(Network):整个集群中分配给 flannel 要管理 的网络地址范围
  • 子网(Subnet):flannel 所在的每台主机都会管理 network 中一个子网,子网的掩码和范围是可配置的
  • 后端(Backend):使用什么样的后端网络模型,比如默认的 udp,还是 vxlan 等

源码分析

整体的代码组织如下:

77829ddea4dead88085b7b4d5951d9f116797698

除了可执行文件的入口 main.go之外,有backend,network,pkg和subnet这么几个代码相关的文件夹。

  • network主要是iptables相关。主要是供main函数根据设置进行MasqRules和ForwardRules规则的设定。
  • pkg主要是抽象封装的ip功能库。
  • backed 主要是后端实现,目前支持 udp、vxlan、host-gw 等。
c871105981e248dafc9e4ed80cc8128cbd39c7fd
  • subnet 子网管理。主要支持etcdv2和k8s两种实现。
fb8044650ca22ccaacf54334cecba39c7313dfbe

启动参数

name 默认值 说明
etcd-endpoints http://127.0.0.1:4001,http://127.0.0.1:2379 etcd终端节点列表
etcd-prefix /coreos.com/network etcd 前缀
etcd-keyfile SSL key文件
etcd-certfile SSL certification 文件
etcd-cafile SSL Certificate Authority 文件
etcd-username 通过BasicAuth访问etcd 的用户名
etcd-password 通过BasicAuth访问etcd 的密码
iface 完整的网卡名或ip地址
iface-regex 正则表达式表示的网卡名或ip地址
subnet-file /run/flannel/subnet.env 存放运行时需要的一些变量 (subnet, MTU, ... )的文件名
public-ip 主机IP
subnet-lease-renew-margin 60分钟 在租约到期之前多长时间进行更新
ip-masq false 是否为覆盖网络外部的流量设置IP伪装规则
kube-subnet-mgr false 是否使用k8s作为subnet的实现方式
kube-api-url "" Kubernetes API server URL ,如果集群内部署,则不需要设置,做好rbac授权即可
kubeconfig-file "" kubeconfig file 位置,如果集群内部署,则不需要设置,做好rbac授权即可
healthz-ip 0.0.0.0 要监听的healthz服务器的IP地址
healthz-port 0 要监听的healthz服务器的端口,0 表示停用

分析

从main函数开始分析,主要步骤如下:

1. 校验subnet-lease-renew-margin

if opts.subnetLeaseRenewMargin >= 24*60 || opts.subnetLeaseRenewMargin <= 0 {
 log.Error("Invalid subnet-lease-renew-margin option, out of acceptable range")
 os.Exit(1)
 }

需要小于等于24h,大于0。

2. 计算去使用哪一个网络接口

假如主机有多个网卡,flannel会使用哪一个?
这就和咱们前面提到的iface和iface-regex两个参数有关。这两个参数每一个可以指定多个。flannel将按照下面的优先顺序来选取:
1) 如果”–iface”和”—-iface-regex”都未指定时,则直接选取默认路由所使用的输出网卡

2) 如果”–iface”参数不为空,则依次遍历其中的各个实例,直到找到和该网卡名或IP匹配的实例为止

3) 如果”–iface-regex”参数不为空,操作方式和2)相同,唯一不同的是使用正则表达式去匹配

最后,对于集群间交互的Public IP,我们同样可以通过启动参数”–public-ip”进行指定。否则,将使用上文中获取的网卡的IP作为Public IP。

外部接口的定义如下:

type ExternalInterface struct {
 Iface *net.Interface
 IfaceAddr net.IP
 ExtAddr net.IP
}

3.创建SubnetManager

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 := ReadSubnetFromSubnetFile(opts.subnetFile)

 return etcdv2.NewLocalManager(cfg, prevSubnet)
}

子网管理器负责子网的创建、更新、添加、删除、监听等,主要和 etcd 打交道,定义:

type Manager interface {
 GetNetworkConfig(ctx context.Context) (*Config, error)
 AcquireLease(ctx context.Context, attrs *LeaseAttrs) (*Lease, error)
 RenewLease(ctx context.Context, lease *Lease) error
 WatchLease(ctx context.Context, sn ip.IP4Net, cursor interface{}) (LeaseWatchResult, error)
 WatchLeases(ctx context.Context, cursor interface{}) (LeaseWatchResult, error)

 Name() string
}
  • RenewLease 续约。在lease到期之前,子网管理器调用该方法进行续约。
  • GetNetworkConfig 获取本机的subnet配置,进行一些初始化的工作。

4. 获取网络配置

config, err := getConfig(ctx, sm)
 if err == errCanceled {
 wg.Wait()
 os.Exit(0)
 }

这个配置主要是管理网络的配置,需要在flannel启动之前写到etcd中。例如:

{
 "Network": "10.0.0.0/8",
 "SubnetLen": 20,
 "SubnetMin": "10.10.0.0",
 "SubnetMax": "10.99.0.0",
 "Backend": {
 "Type": "udp",
 "Port": 7890
 }
}

/coreos.com/network/config 保存着上面网络配置数据。
详细解读一下:

  • SubnetLen表示每个主机分配的subnet大小,我们可以在初始化时对其指定,否则使用默认配置。在默认配置的情况下,如果集群的网络地址空间大于/24,则SubnetLen配置为24,否则它比集群网络地址空间小1,例如集群的大小为/25,则SubnetLen的大小为/26
  • SubnetMin是集群网络地址空间中最小的可分配的subnet,可以手动指定,否则默认配置为集群网络地址空间中第一个可分配的subnet。
  • SubnetMax表示最大可分配的subnet
  • BackendType为使用的backend的类型,如未指定,则默认为“udp”
  • Backend中会包含backend的附加信息,例如backend为vxlan时,其中会存储vtep设备的mac地址

5. 创建backend管理器,然后使用它来创建backend并使用它注册网络,然后执行run方法

 bm := backend.NewManager(ctx, sm, extIface)
 be, err := bm.GetBackend(config.BackendType)
 if err != nil {
 log.Errorf("Error fetching backend: %s", err)
 cancel()
 wg.Wait()
 os.Exit(1)
 }

 bn, err := be.RegisterNetwork(ctx, config)
 if err != nil {
 log.Errorf("Error registering network: %s", err)
 cancel()
 wg.Wait()
 os.Exit(1)
 }

...

log.Info("Running backend.")
 wg.Add(1)
 go func() {
 bn.Run(ctx)
 wg.Done()
 }()

backend管理器

type manager struct {
 ctx context.Context
 sm subnet.Manager
 extIface *ExternalInterface
 mux sync.Mutex
 active map[string]Backend
 wg sync.WaitGroup
}

主要是提供了GetBackend(backendType string) (Backend, error)方法,根据配置文件的设置backend标志,生产对应的backend。
此处注意

 go func() {
 <-bm.ctx.Done()

 // TODO(eyakubovich): this obviosly introduces a race. // GetBackend() could get called while we are here. // Currently though, all backends' Run exit only // on shutdown

 bm.mux.Lock()
 delete(bm.active, betype)
 bm.mux.Unlock()

 bm.wg.Done()
 }()

在生产backend以后,会启动一个协程,在flanneld退出运行之前,将会执行激活的backend map中删除操作。

最后run方法:

func (n *RouteNetwork) Run(ctx context.Context) {
 wg := sync.WaitGroup{}

 log.Info("Watching for new subnet leases")
 evts := make(chan []subnet.Event)
 wg.Add(1)
 go func() {
 subnet.WatchLeases(ctx, n.SM, n.SubnetLease, evts)
 wg.Done()
 }()

 n.routes = make([]netlink.Route, 0, 10)
 wg.Add(1)
 go func() {
 n.routeCheck(ctx)
 wg.Done()
 }()

 defer wg.Wait()

 for {
 select {
 case evtBatch := <-evts:
 n.handleSubnetEvents(evtBatch)

 case <-ctx.Done():
 return
 }
 }
}

run方法中主要是执行:

  • subnet 负责和 etcd 交互,把 etcd 中的信息转换为 flannel 的子网数据结构,并对 etcd 进行子网和网络的监听;
  • backend 接受 subnet 的监听事件,并做出对应的处理。

事件主要是subnet.EventAdded和subnet.EventRemoved两个。
添加子网事件发生时的处理步骤:检查参数是否正常,根据参数构建路由表项,把路由表项添加到主机,把路由表项添加到自己的数据结构中。

删除子网事件发生时的处理步骤:检查参数是否正常,根据参数构建路由表项,把路由表项从主机删除,把路由表项从管理的数据结构中删除

6. 其他

除了上面的核心的逻辑,还有一些iptables规则和SubnetFile相关的操作。

// Set up ipMasq if needed if opts.ipMasq {
 go network.SetupAndEnsureIPTables(network.MasqRules(config.Network, bn.Lease()))
 }

 // Always enables forwarding rules. This is needed for Docker versions >1.13 (https://docs.docker.com/engine/userguide/networking/default_network/container-communication/#container-communication-between-hosts) // In Docker 1.12 and earlier, the default FORWARD chain policy was ACCEPT. // In Docker 1.13 and later, Docker sets the default policy of the FORWARD chain to DROP. go network.SetupAndEnsureIPTables(network.ForwardRules(config.Network.String()))

可以看出主要是调用了network文件里的SetupAndEnsureIPTables方法。
PS
在Docker 1.13及更高版本中,Docker设置了FORWARD的默认策略是drop,所以需要flannel做一些工作。

本文转自SegmentFault-k8s与网络--Flannel源码分析

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
人工智能 弹性计算 运维
ACK Edge与IDC:高效容器网络通信新突破
本文介绍如何基于ACK Edge以及高效的容器网络插件管理IDC进行容器化。
|
存储 人工智能 编解码
Deepseek 3FS解读与源码分析(2):网络通信模块分析
2025年2月28日,DeepSeek 正式开源其颠覆性文件系统Fire-Flyer 3FS(以下简称3FS),重新定义了分布式存储的性能边界。本文基于DeepSeek发表的技术报告与开源代码,深度解析 3FS 网络通信模块的核心设计及其对AI基础设施的革新意义。
Deepseek 3FS解读与源码分析(2):网络通信模块分析
|
Kubernetes 数据安全/隐私保护 容器
K8s中Flannel网络插件安装提示forbidden无权限的解决方法
总的来说,解决“forbidden无权限”的问题,需要从权限和配置两个方面来考虑。只有当用户或者服务账户有足够的权限,且Flannel的配置文件设置正确,才能成功地安装Flannel。希望这个解答能够帮助你解决问题。
547 13
|
Kubernetes Shell Windows
【Azure K8S | AKS】在AKS的节点中抓取目标POD的网络包方法分享
在AKS中遇到复杂网络问题时,可通过以下步骤进入特定POD抓取网络包进行分析:1. 使用`kubectl get pods`确认Pod所在Node;2. 通过`kubectl node-shell`登录Node;3. 使用`crictl ps`找到Pod的Container ID;4. 获取PID并使用`nsenter`进入Pod的网络空间;5. 在`/var/tmp`目录下使用`tcpdump`抓包。完成后按Ctrl+C停止抓包。
595 12
|
Kubernetes 网络协议 应用服务中间件
Kubernetes Ingress:灵活的集群外部网络访问的利器
《Kubernetes Ingress:集群外部访问的利器-打造灵活的集群网络》介绍了如何通过Ingress实现Kubernetes集群的外部访问。前提条件是已拥有Kubernetes集群并安装了kubectl工具。文章详细讲解了Ingress的基本组成(Ingress Controller和资源对象),选择合适的版本,以及具体的安装步骤,如下载配置文件、部署Nginx Ingress Controller等。此外,还提供了常见问题的解决方案,例如镜像下载失败的应对措施。最后,通过部署示例应用展示了Ingress的实际使用方法。
1184 2
|
Kubernetes 网络协议 网络安全
k8s中网络连接问题
【10月更文挑战第3天】
1349 7
|
Kubernetes 应用服务中间件 nginx
搭建Kubernetes v1.31.1服务器集群,采用Calico网络技术
在阿里云服务器上部署k8s集群,一、3台k8s服务器,1个Master节点,2个工作节点,采用Calico网络技术。二、部署nginx服务到k8s集群,并验证nginx服务运行状态。
5024 3
|
存储 Kubernetes 网络协议
Kubernetes网络分析之Flannel
Flannel是cereos开源的CNI网络插件,flannel支持多种网络模式,在实际的生产环境中,最常用的还是vxlan模式,本文将介绍其工作原理,并通过源码解析实现过程。
|
存储 Kubernetes 网络协议
Kubernetes网络分析之Flannel
Flannel是CoreOS开源的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地址。
3594 0
|
10月前
|
人工智能 算法 调度
阿里云ACK托管集群Pro版共享GPU调度操作指南
本文介绍在阿里云ACK托管集群Pro版中,如何通过共享GPU调度实现显存与算力的精细化分配,涵盖前提条件、使用限制、节点池配置及任务部署全流程,提升GPU资源利用率,适用于AI训练与推理场景。
758 1

推荐镜像

更多