<button id="ysiwy"><noscript id="ysiwy"></noscript></button>
    <input id="ysiwy"></input>
  • <input id="ysiwy"></input>
  • <del id="ysiwy"></del>
    <s id="ysiwy"><kbd id="ysiwy"></kbd></s>
    <del id="ysiwy"></del>
      • 智匯華云|Kubernetes網絡詳解(二)CNI原理與實現

        編輯:華云公眾號2020-02-27 10:04:11 關鍵字:智匯華,cni,網絡,plugin,pod,md,error,操作,參數,opt

        原標題:智匯華云 | Kubernetes網絡詳解(二) CNI原理與實現

        智匯華云|Kubernetes網絡詳解(二)CNI原理與實現

        繼上期“智匯華云”邀請到華云數據容器組資深OpenStack開發(fā)工程師郭棟為您帶來“ Kubernetes網絡詳解(一) Flannel基本原理”之后, 本期“智匯華云”為大家?guī)鞬ubernetes網絡詳解的系列內容——Kubernetes網絡詳解(二) CNI原理與實現。

        1、CNI概述

        本文中 Kubernetes版本是v1.16.3。

        CNI的全稱是Container Network Interface,屬于CNCF的一個子項目,它是一個完整的規(guī)范,詳見 https://github.com/containernetworking/cni/blob/master/SPEC.md

        本文 會在一個實際的環(huán)境中講解其基本原理,并給出一些直觀的例子。

        Kubernetes自身在創(chuàng)建和刪除pod的時候不涉及網絡相關的操作,它會把這些交給CNI插件來完成。具體來講就是當Kubernetes分別在創(chuàng)建一個pod的時候和刪除一個pod的時候,對應的cni插件要做哪些操作。

        2、CNI的入口

        Kubernetes中創(chuàng)建pod的實際操作是 由node節(jié)點上的kubelet進行來完成的,它有兩個重要的cni相關的命令行參數

        - `cni-conf-dir`

        這個參數指定了 cni配置文件的目錄默認是 `/etc/cni/net.d`

        - `cni-bin-dir`

        這個參數指定了 cni插件的可執(zhí)行文件所在的目錄 `/opt/cni/bin`

        CNI插件會以二進制可執(zhí)行文件的形式提供,存放于 `cni-bin-dir`這個目錄下,kubelet會根據cni配置文件來決定具體調用哪些插件。

        ```

        [root@node1 ~]# ls -l /opt/cni/bin/

        total 36132

        ```

        在本文中我們會詳細分析 幾個基礎的cni plugin

        3、flannel cni plugin

        在上一篇文章中,我們 以flannel為例,講解了其基本原理,現在接著分析cni的部分。

        flannel的DaemonSet的啟動前會 將一個ConfigMap中的內容copy成一個cni配置文件

        ```

        [root@node1 ~]# cat /etc/cni/net.d/10-flannel.conflist

        {

        "cniVersion": "0.2.0",

        "name": "cbr0",

        "plugins": [

        {

        "type": "flannel",

        "delegate": {

        "hairpinMode": true,

        "isDefaultGateway": true

        }

        },

        {

        "type": "portmap",

        "capabilities": {

        "portMappings": true

        }

        }

        ]

        }

        ```

        配置文件中的plugins參數指定了需要調用的plugin列表和對應的配置,本文中我們只關心基礎的plugin,因此這里會暫時忽略portmap這個plugin。

        plugin中的type參數指定具體的plugin二進制文件名稱,因此kubelet會調用 `/opt/cni/bin/flannel`,代碼位于 `https://github.com/containernetworking/plugins/tree/master/plugins/meta/flannel`

        ```

        func main {

        skel.PluginMain(cmdAdd, cmdGet, cmdDel, version.All, "TODO")

        }

        ```

        這個main就是flannel cni plugin的入口, 當創(chuàng)建和刪除pod時會分別調用其中的 `cmdAdd``cmdDel`函數來執(zhí)行對應的操作

        ```

        func cmdAdd(args *skel.CmdArgs) error {

        n, err := loadFlannelNetConf(args.StdinData)

        if err != nil {

        return err

        }

        fenv, err := loadFlannelSubnetEnv(n.SubnetFile)

        if err != nil {

        return err

        }

        if n.Delegate == nil {

        n.Delegate = make(map[string]interface{})

        } else {

        if hasKey(n.Delegate, "type") && !isString(n.Delegate["type"]) {

        return fmt.Errorf("'delegate' dictionary, if present, must have (string) 'type' field")

        }

        if hasKey(n.Delegate, "name") {

        return fmt.Errorf("'delegate' dictionary must not have 'name' field, it'll be set by flannel")

        }

        if hasKey(n.Delegate, "ipam") {

        return fmt.Errorf("'delegate' dictionary must not have 'ipam' field, it'll be set by flannel")

        }

        }

        return doCmdAdd(args, n, fenv)

        }

        ```

        `cmdAdd`中主要有 三個步驟

        1. 從stdin讀取配置,生成一個 `type NetConf struct`對象

        2. 從第1步返回的 `NetConf.SubnetFile`指定的路徑(默認是 `/run/flannel/subnet.env`)載入一個配置文件, 這個文件由Flanneld DaemonSet生成,在上一篇文章中有說明

        3. 調用 `doCmdAdd`函數執(zhí)行真正的操作

        3.1、讀取NetConf配置

        ```

        type NetConf struct {

        types.NetConf

        SubnetFile string `json:"subnetFile"`

        DataDir string `json:"dataDir"`

        Delegate map[string]interface{} `json:"delegate"`

        }

        # types.NetConf結構體

        // NetConf describes a network.

        type NetConf struct {

        CNIVersion string `json:"cniVersion,omitempty"`

        Name string `json:"name,omitempty"`

        Type string `json:"type,omitempty"`

        Capabilities map[string]bool `json:"capabilities,omitempty"`

        IPAM IPAM `json:"ipam,omitempty"`

        DNS DNS `json:"dns"`

        RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`

        PrevResult Result `json:"-"`

        }

        ```

        現在回頭看 cni的配置文件(忽略了portmap plugin)

        ```

        {

        "cniVersion": "0.2.0",

        "name": "cbr0",

        "plugins": [

        {

        "type": "flannel",

        "delegate": {

        "hairpinMode": true,

        "isDefaultGateway": true

        }

        }

        ]

        }

        ```

        對應于flannel plugin中的 `NetConf`結構體 ,可以看出配置文件中的

        ```

        "delegate": {

        "hairpinMode": true,

        "isDefaultGateway": true

        }

        ```

        對應于 `NetConf`中的 `Delegate map[string]interface{}`除了這個和name, type, version之外而其它值都為空或者默認

        3.2、讀取flanneld生成的配置文件

        在node1上 `/run/flannel/subnet.env`這個文件的內容如下

        ```

        [root@node1 ~]# cat /run/flannel/subnet.env

        FLANNEL_NETWORK=10.244.0.0/16

        FLANNEL_SUBNET=10.244.1.1/24

        FLANNEL_MTU=1450

        FLANNEL_IPMASQ=true

        [root@node1 ~]#

        ```

        3.3、執(zhí)行doCmdAdd

        這是 flannel cni plugin的核心

        ```

        func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error {

        n.Delegate["name"] = n.Name

        if !hasKey(n.Delegate, "type") {

        n.Delegate["type"] = "bridge"

        }

        if !hasKey(n.Delegate, "ipMasq") {

        // if flannel is not doing ipmasq, we should

        ipmasq := !*fenv.ipmasq

        n.Delegate["ipMasq"] = ipmasq

        }

        if !hasKey(n.Delegate, "mtu") {

        mtu := fenv.mtu

        n.Delegate["mtu"] = mtu

        }

        if n.Delegate["type"].(string) == "bridge" {

        if !hasKey(n.Delegate, "isGateway") {

        n.Delegate["isGateway"] = true

        }

        }

        if n.CNIVersion != "" {

        n.Delegate["cniVersion"] = n.CNIVersion

        }

        n.Delegate["ipam"] = map[string]interface{}{

        "type": "host-local",

        "subnet": fenv.sn.String,

        "routes": []types.Route{

        {

        Dst: *fenv.nw,

        },

        },

        }

        return delegateAdd(args.ContainerID, n.DataDir, n.Delegate)

        }

        ```

        主要完成的工作如下

        - 將下一級調用的的 cni plugin設置為 `bridge`

        - 設置ipMasq和mtu, 注意這里cni plugin最終的ipMasq和flanneld自身的是相反的,也就是說如果flanneld設置了,cni plugin就不再設置了

        - 如果是 `bridge`,且沒有設置isGateway的話 ,將其默認設置為true

        - 設置ipam cni plugin為host-local,并且將subnet參數設置為上文 `/run/flannel/subnet.env`中的 `FLANNEL_SUBNET`,并設置路由

        從這里可以 看出flannel cni plugin的核心邏輯就根據當前配置生成bridge和host-local這兩個cni plugin的配置參數,隨后通過調用它們來實現主要的功能。

        host-local cni plugin的功能是在當前節(jié)點從一個subnet中給pod分配ip地址,詳細邏輯可閱讀其代碼來理解。 這個cni plugin會被bridge cni plugin調用。

        下面簡述一下 bridge的實現

        4、bridge cni plugin

        智匯華云|Kubernetes網絡詳解(二)CNI原理與實現

        bridge插件負責 從pod到veth到cni0的整個流程

        核心功能同樣 在cmdAdd和cmdDel這兩個函數中,下面看cmdAdd中的邏輯。

        總體上分為二層和三層兩部分

        4.1、二層處理

        第一步會先 處理網橋設備

        ```

        br, brInterface, err := setupBridge(n)

        if err != nil {

        return err

        }

        ```

        `setupBridge`負責創(chuàng)建cni0這個bridge,并配置它的mtu、混雜模式等等,最后將這個網橋設備up起來。詳細的代碼如下

        ```

        func setupBridge(n *NetConf) (*netlink.Bridge, *current.Interface, error) {

        // create bridge if necessary

        br, err := ensureBridge(n.BrName, n.MTU, n.PromiscMode)

        if err != nil {

        return nil, nil, fmt.Errorf("failed to create bridge %q: %v", n.BrName, err)

        }

        ...

        }

        func ensureBridge(brName string, mtu int, promiscMode bool) (*netlink.Bridge, error) {

        br := &netlink.Bridge{

        LinkAttrs: netlink.LinkAttrs{

        Name: brName,

        MTU: mtu,

        TxQLen: -1,

        },

        }

        err := netlink.LinkAdd(br)

        ...

        if promiscMode {

        ...

        if err := netlink.LinkSetUp(br); err != nil {

        ...

        }

        ```

        第二步會 處理veth pair接口

        ```

        netns, err := ns.GetNS(args.Netns)

        if err != nil {

        return fmt.Errorf("failed to open netns %q: %v", args.Netns, err)

        }

        defer netns.Close

        hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode)

        if err != nil {

        return err

        }

        ```

        `setupVeth`函數會在pod所在的network namespace中創(chuàng)建一對veth接口,并將其中的一端移到host中,然后設置它的mac地址,并將其掛載到cni0這個網橋上,如果需要的話還會設置這個接口的hairpin模式。代碼如下

        ```

        func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool) (*current.Interface, *current.Interface, error) {

        contIface := &current.Interface{}

        hostIface := &current.Interface{}

        err := netns.Do(func(hostNS ns.NetNS) error {

        // create the veth pair in the container and move host end into host netns

        hostVeth, containerVeth, err := ip.SetupVeth(ifName, mtu, hostNS)

        if err != nil {

        return err

        }

        contIface.Name = containerVeth.Name

        contIface.Mac = containerVeth.HardwareAddr.String

        contIface.Sandbox = netns.Path

        hostIface.Name = hostVeth.Name

        return nil

        })

        if err != nil {

        return nil, nil, err

        }

        // need to lookup hostVeth again as its index has changed during ns move

        hostVeth, err := netlink.LinkByName(hostIface.Name)

        if err != nil {

        return nil, nil, fmt.Errorf("failed to lookup %q: %v", hostIface.Name, err)

        }

        hostIface.Mac = hostVeth.Attrs.HardwareAddr.String

        // connect host veth end to the bridge

        if err := netlink.LinkSetMaster(hostVeth, br); err != nil {

        return nil, nil, fmt.Errorf("failed to connect %q to bridge %v: %v", hostVeth.Attrs.Name, br.Attrs.Name, err)

        }

        // set hairpin mode

        if err = netlink.LinkSetHairpin(hostVeth, hairpinMode); err != nil {

        return nil, nil, fmt.Errorf("failed to setup hairpin mode for %v: %v", hostVeth.Attrs.Name, err)

        }

        return hostIface, contIface, nil

        }

        ```

        4.2、三層處理

        ```

        isLayer3 := n.IPAM.Type != ""

        ...

        if isLayer3 {

        ..

        }

        ```

        三層網絡是否需要處理取決于ipam cni plugin是否已經配置,在我們的環(huán)境中, 這個字段已經由flannel cni plugin配置成host-local了,因此這里需要處理三層的邏輯。處理的詳細邏輯如下

        ```

        // run the IPAM plugin and get back the config to apply

        r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)

        if err != nil {

        return err

        }

        // release IP in case of failure

        defer func {

        if !success {

        os.Setenv("CNI_COMMAND", "DEL")

        ipam.ExecDel(n.IPAM.Type, args.StdinData)

        os.Setenv("CNI_COMMAND", "ADD")

        }

        }

        // Convert whatever the IPAM result was into the current Result type

        ipamResult, err := current.NewResultFromResult(r)

        if err != nil {

        return err

        }

        result.IPs = ipamResult.IPs

        result.Routes = ipamResult.Routes

        if len(result.IPs) == 0 {

        return errors.New("IPAM plugin returned missing IP config")

        }

        ```

        這里bridge plugin 會調用host-local這個ipmi plugin來為當前處理的pod分配一個ip地址

        ```

        // Gather gateway information for each IP family

        gwsV4, gwsV6, err := calcGateways(result, n)

        if err != nil {

        return err

        }

        ```

        接著會 根據這個地址計算出bridge設備cni0的ip地址

        ```

        // Configure the container hardware address and IP address(es)

        if err := netns.Do(func(_ ns.NetNS) error {

        contVeth, err := net.InterfaceByName(args.IfName)

        ...

        // Add the IP to the interface

        if err := ipam.ConfigureIface(args.IfName, result); err != nil {

        return err

        }

        // Send a gratuitous arp

        for _, ipc := range result.IPs {

        if ipc.Version == "4" {

        _ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth)

        }

        }

        return nil

        }); err != nil {

        return err

        }

        ```

        這段代碼的含義是 在pod所在的network namespace中配置之前添加進來的那個veth接口,包括將設備設置為up狀態(tài),配置ip地址和路由信息,最后發(fā)送gratuitous arp廣播。

        ```

        if n.IsGW {

        var firstV4Addr net.IP

        // Set the IP address(es) on the bridge and enable forwarding

        for _, gws := range []*gwInfo{gwsV4, gwsV6} {

        for _, gw := range gws.gws {

        if gw.IP.To4 != nil && firstV4Addr == nil {

        firstV4Addr = gw.IP

        }

        err = ensureBridgeAddr(br, gws.family, &gw, n.ForceAddress)

        if err != nil {

        return fmt.Errorf("failed to set bridge addr: %v", err)

        }

        }

        if gws.gws != nil {

        if err = enableIPForward(gws.family); err != nil {

        return fmt.Errorf("failed to enable forwarding: %v", err)

        }

        }

        }

        }

        ```

        接下來會給cni0這個bridge配置ip地址,并執(zhí)行類似于 `echo 1 > /proc/sys/net/ipv4/ip_forward`的操作來啟用數據包轉發(fā)功能

        ```

        if n.IPMasq {

        chain := utils.FormatChainName(n.Name, args.ContainerID)

        comment := utils.FormatComment(n.Name, args.ContainerID)

        for _, ipc := range result.IPs {

        if err = ip.SetupIPMasq(ip.Network(&ipc.Address), chain, comment); err != nil {

        return err

        }

        }

        }

        ```

        最后如果cni plugin設置ipmasq則需要進行相關ipmasq相關的設置, 在當前環(huán)境中ipmasq是由Flanneld Daemon完成的,因此這里的cni plugin不會進行設置。

        至此, 詳細分析了bridge cni plugin的cmAdd主要流程,其核心功能總結如下

        - 新建并配置網橋設備cni0

        - 在pod所在的namespace中創(chuàng)建一對veth接口,并 將其中的一端移到host中并將其掛載到網橋上

        - 調用ipmi cni plugin給pod中的 一端veth接口分配ip地址并將其配置到接口上

        - 給cni0配置ip地址并開啟數據包轉發(fā)功能

        參考文獻

        -https://github.com/containernetworking/cni/blob/master/SPEC.md

        -https://github.com/containernetworking/cni

        -https://github.com/containernetworking/plugins

        責任編輯:

        相關文章
        6月中國廠商出海收入30強榜單公布:騰訊排名下滑 掌趣科技、易幻網絡跌出榜單

        6月中國廠商出海收入30強榜單公布:騰訊排名下滑 掌趣科技、易幻網絡跌出榜單

        中國網科技7月30日訊(記者 李婷)市場研究機構App Annie近日發(fā)布2020年6月中國廠商出海收入30強榜單,FunPlus(趣加)取代[詳情]

        凱迪拉克怎么了?

        凱迪拉克怎么了?

        題圖:GM authority隨著 2020 年新冠疫情逐步趨穩(wěn),很多事已經沒法再讓疫情背鍋了。先是跌入谷底,再是觸底回升,上半年國內汽[詳情]

        運載火箭可用固體燃料 美再為韓國研制彈道導彈“松綁”

        運載火箭可用固體燃料 美再為韓國研制彈道導彈“松綁”

        據韓聯社首爾7月28日報道,韓國7月28日宣布,根據與美國達成的新導彈指南,該國已能研發(fā)使用固體推進劑的火箭。他在新聞發(fā)布[詳情]

        意大利餐廳服務員確診 追蹤發(fā)現某些顧客留假信息

        意大利餐廳服務員確診 追蹤發(fā)現某些顧客留假信息

        歐聯網7月30日電,據歐聯通訊社報道,意大利坎帕尼亞大區(qū)衛(wèi)生部門28日通報,當日該地區(qū)新增確診病例29例,那不勒斯省維科·埃[詳情]

        元晟溱:柯潔很有才能 他與李世石風格相似卻不同

        元晟溱:柯潔很有才能 他與李世石風格相似卻不同

        韓國棋手元晟溱九段  據韓國烏鷺網報道,韓國棋手元晟溱在10多歲的時候就已經達到了世界超一流棋手的水平。在20歲中期[詳情]

        contact us

        Copyright     2018-2020   All rights reserved.
        欧美日韩国产高清一区二区三区,国产欧美综合一区二区,欧美黑人巨大3dvideo,亚洲视频在线一区二区三区
        <button id="ysiwy"><noscript id="ysiwy"></noscript></button>
          <input id="ysiwy"></input>
        • <input id="ysiwy"></input>
        • <del id="ysiwy"></del>
          <s id="ysiwy"><kbd id="ysiwy"></kbd></s>
          <del id="ysiwy"></del>
            • 主站蜘蛛池模板: 久久亚洲综合色| 国产精品久久久久影院| 国产乱理伦片a级在线观看| 亚洲乱码中文字幕小综合| 97精品国产一区二区三区| 稚嫩进出嗯啊湿透公交车漫画 | 小草视频免费观看| 国产人妖乱国产精品人妖| 久久久久久影院久久久久免费精品国产小说 | 中文字幕在线观看一区| 色综合久久天天综合| 欧美系列第一页| 天堂а√在线中文在线| 免费高清av一区二区三区| 中文精品无码中文字幕无码专区 | 最近中文字幕mv手机免费高清| 国产高清一区二区三区视频| 亚洲综合五月天欧美| 窝窝午夜看片成人精品| 日本哺乳期xxxx| 免费观看黄网站| 男女一边摸一边做爽的免费视频 | 美女毛片一区二区三区四区| 成年大片免费视频| 交性大片欧美网| a视频在线观看免费| 男生女生一起差差很痛| 国产精品亚洲小说专区| 久久久久久亚洲精品中文字幕| 王小明恶魔手机催眠1-6| 在线观看欧洲成人免费视频| 免费精品久久天干天干| 一级做a爰片久久毛片唾| 无码日韩人妻av一区二区三区| 国产aⅴ一区二区三区| 中文字幕乱码中文乱码51精品| 熟妇人妻va精品中文字幕| 国产视频精品久久| 久久久青草青青亚洲国产免观| 激情五月婷婷久久| 国产区精品视频|