资讯专栏INFORMATION COLUMN

k8s与网络--Flannel源码分析

Jeffrrey / 2763人阅读

摘要:今天主要针对版本进行源码分析。外部接口的定义如下创建子网管理器负责子网的创建更新添加删除监听等,主要和打交道定义续约。在到期之前,子网管理器调用该方法进行续约。

前言

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

网络(Network):整个集群中分配给 flannel 要管理的网络地址范围

子网(Subnet):flannel 所在的每台主机都会管理 network 中一个子网,子网的掩码和范围是可配置的

后端(Backend):使用什么样的后端网络模型,比如默认的 udp,还是 vxlan 等

源码分析

整体的代码组织如下:

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

network主要是iptables相关。主要是供main函数根据设置进行MasqRules和ForwardRules规则的设定。

pkg主要是抽象封装的ip功能库。

backed 主要是后端实现,目前支持 udp、vxlan、host-gw 等。

subnet 子网管理。主要支持etcdv2和k8s两种实现。

启动参数
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做一些工作。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/32725.html

相关文章

  • k8s网络--Flannel源码分析

    摘要:今天主要针对版本进行源码分析。外部接口的定义如下创建子网管理器负责子网的创建更新添加删除监听等,主要和打交道定义续约。在到期之前,子网管理器调用该方法进行续约。 前言 之前在k8s与网络--Flannel解读一文中,我们主要讲了Flannel整体的工作原理。今天主要针对Flannel v0.10.0版本进行源码分析。首先需要理解三个比较重要的概念: 网络(Network):整个集群中...

    wpw 评论0 收藏0
  • k8s网络--Flannel源码分析

    摘要:今天主要针对版本进行源码分析。外部接口的定义如下创建子网管理器负责子网的创建更新添加删除监听等,主要和打交道定义续约。在到期之前,子网管理器调用该方法进行续约。 前言 之前在k8s与网络--Flannel解读一文中,我们主要讲了Flannel整体的工作原理。今天主要针对Flannel v0.10.0版本进行源码分析。首先需要理解三个比较重要的概念: 网络(Network):整个集群中...

    hoohack 评论0 收藏0
  • 浅谈k8s cni 插件

    摘要:即配置网络和解除网络配置。类类型的插件,在执行命令时会分配一个给调用者。执行命令时会将调用者指定的放回池。向删除时,同样通过请求,解除该的租约。组件通常在组件执行完毕后执行 目前不论是个人还是企业,在使用k8s时,都会采用CNI作为集群网络方案实现的规范。 在早先的k8s版本中,kubelet代码里提供了networkPlugin,networkPlugin是一组接口,实现了pod的网...

    fireflow 评论0 收藏0
  • k8s网络--Flannel解读

    摘要:是一个专为定制的三层网络解决方案,主要用于解决容器的跨主机通信问题。收到的数据包被转发到进程。查询路由表,解封包,并将数据包发送到。然后在网络层的源和目的均是容器的,虚拟。默认也是使用容器网络方案,其官网也清晰的画出了的。 前言 我们知道docker官方并没有提供多主机的容器通信方案,单机网络的模式主要有host,container,brige,none。none这种模式,顾名思义就是...

    laznrbfe 评论0 收藏0
  • k8s网络--Flannel解读

    摘要:是一个专为定制的三层网络解决方案,主要用于解决容器的跨主机通信问题。收到的数据包被转发到进程。查询路由表,解封包,并将数据包发送到。然后在网络层的源和目的均是容器的,虚拟。默认也是使用容器网络方案,其官网也清晰的画出了的。 前言 我们知道docker官方并没有提供多主机的容器通信方案,单机网络的模式主要有host,container,brige,none。none这种模式,顾名思义就是...

    Pink 评论0 收藏0

发表评论

0条评论

Jeffrrey

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<