k8s與網絡--Flannel源碼分析

前言

以前在k8s與網絡--Flannel解讀一文中,咱們主要講了Flannel總體的工做原理。今天主要針對Flannel v0.10.0版本進行源碼分析。首先須要理解三個比較重要的概念:ios

  • 網絡(Network):整個集羣中分配給 flannel 要管理的網絡地址範圍
  • 子網(Subnet):flannel 所在的每臺主機都會管理 network 中一個子網,子網的掩碼和範圍是可配置的
  • 後端(Backend):使用什麼樣的後端網絡模型,好比默認的 udp,仍是 vxlan 等

源碼分析

總體的代碼組織以下:正則表達式

圖片描述

除了可執行文件的入口 main.go以外,有backend,network,pkg和subnet這麼幾個代碼相關的文件夾。docker

  • 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函數開始分析,主要步驟以下:json

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。segmentfault

2. 計算去使用哪個網絡接口

假如主機有多個網卡,flannel會使用哪個?
這就和我們前面提到的iface和iface-regex兩個參數有關。這兩個參數每個能夠指定多個。flannel將按照下面的優先順序來選取:
1) 若是」–iface」和」—-iface-regex」都未指定時,則直接選取默認路由所使用的輸出網卡後端

2) 若是」–iface」參數不爲空,則依次遍歷其中的各個實例,直到找到和該網卡名或IP匹配的實例爲止api

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作一些工做。

相關文章
相關標籤/搜索