以前在k8s與網絡--Flannel解讀一文中,咱們主要講了Flannel總體的工做原理。今天主要針對Flannel v0.10.0版本進行源碼分析。首先須要理解三個比較重要的概念:ios
總體的代碼組織以下:正則表達式
除了可執行文件的入口 main.go以外,有backend,network,pkg和subnet這麼幾個代碼相關的文件夾。docker
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
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
假如主機有多個網卡,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 }
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 }
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 保存着上面網絡配置數據。
詳細解讀一下:
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.EventAdded和subnet.EventRemoved兩個。
添加子網事件發生時的處理步驟:檢查參數是否正常,根據參數構建路由表項,把路由表項添加到主機,把路由表項添加到本身的數據結構中。
刪除子網事件發生時的處理步驟:檢查參數是否正常,根據參數構建路由表項,把路由表項從主機刪除,把路由表項從管理的數據結構中刪除
除了上面的核心的邏輯,還有一些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作一些工做。