美文网首页
k8s flannel cni plugin

k8s flannel cni plugin

作者: shoyu666 | 来源:发表于2022-03-17 15:41 被阅读0次

k8s cni
前面说到CRI负责 CNI的调用,以containerd为例子,sandbox_run.go 的 RunPodSandbox 函数会调用 setupPodNetwork 配置网络。
本文主要结合 flannel -cni 介绍 setupPodNetwork 背后的cni逻辑。

CNI

//pkg/cri/server/sandbox_run.go
func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (_ *runtime.RunPodSandboxResponse, retErr error) {

// Create initial internal sandbox object.
    sandbox := sandboxstore.NewSandbox(
        sandboxstore.Metadata{
            ID:             id,
            Name:           name,
            Config:         config,
            RuntimeHandler: r.GetRuntimeHandler(),
        },
        sandboxstore.Status{
            State: sandboxstore.StateUnknown,
        },
    )
}
sandbox.NetNS, err = netns.NewNetNS(netnsMountDir)
if err := c.setupPodNetwork(ctx, &sandbox); err != nil

先创建了一个sandbox(对应Pod),然后创建了一个 net namespace(linux namespaces隔离网络),调用 setupPodNetwork 设置网络

//pkg/cri/server/sandbox_run.go
func (c *criService) setupPodNetwork(ctx context.Context, sandbox *sandboxstore.Sandbox) error {
    var (
        id        = sandbox.ID
        config    = sandbox.Config
        path      = sandbox.NetNSPath
        netPlugin = c.getNetworkPlugin(sandbox.RuntimeHandler)
    )
    result, err := netPlugin.Setup(ctx, id, path, opts...)
}

获取CNI插件,然后调用插件设置。

//pkg/cri/server/sandbox_run.go
//获取插件
func (c *criService) getNetworkPlugin(runtimeClass string) cni.CNI {
    i, ok := c.netPlugin[runtimeClass]
}

所有插件是保存在一个 map里,map是如何初始化的?

//pkg/cri/server/service_linux.go
func (c *criService) initPlatform() (err error) {
i, err := cni.New(cni.WithMinNetworkCount(networkAttachCount),
            cni.WithPluginConfDir(dir),
            cni.WithPluginMaxConfNum(max),
            cni.WithPluginDir([]string{c.config.NetworkPluginBinDir}))
            c.netPlugin[name] = i
}

//containerd/go-cni/cni.go
func New(config ...Opt) (CNI, error) {
    cni := defaultCNIConfig()

func defaultCNIConfig() *libcni {
    return &libcni{
        config: config{
            pluginDirs:       []string{DefaultCNIDir},
            pluginConfDir:    DefaultNetDir,
            pluginMaxConfNum: DefaultMaxConfNum,
            prefix:           DefaultPrefix,
        },
        cniConfig: cnilibrary.NewCNIConfig(
            []string{
                DefaultCNIDir,
            },
            &invoke.DefaultExec{
                RawExec:       &invoke.RawExec{Stderr: os.Stderr},
                PluginDecoder: version.PluginDecoder{},
            },
        ),
        networkCount: 1,
    }
}

Setup的调用

////containerd/go-cni/cni.go
// Setup setups the network in the namespace and returns a Result
func (c *libcni) Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error) {
    if err := c.Status(); err != nil {
        return nil, err
    }
        //创建 ns对象
    ns, err := newNamespace(id, path, opts...)
    if err != nil {
        return nil, err
    }
        //attachNetworks
    result, err := c.attachNetworks(ctx, ns)
    if err != nil {
        return nil, err
    }
    return c.createResult(result)
}


func (c *libcni) attachNetworks(ctx context.Context, ns *Namespace) ([]*types100.Result, error) {
    var wg sync.WaitGroup
    var firstError error
    results := make([]*types100.Result, len(c.Networks()))
    rc := make(chan asynchAttachResult)
 
       //遍历 NetWork
    for i, network := range c.Networks() {
        wg.Add(1)
                //attach NetWork
        go asynchAttach(ctx, i, network, ns, &wg, rc)
    }
}

func asynchAttach(ctx context.Context, index int, n *Network, ns *Namespace, wg *sync.WaitGroup, rc chan asynchAttachResult) {
    defer wg.Done()
    r, err := n.Attach(ctx, ns)
    rc <- asynchAttachResult{index: index, res: r, err: err}
}

func (n *Network) Attach(ctx context.Context, ns *Namespace) (*types100.Result, error) {
    r, err := n.cni.AddNetworkList(ctx, n.config, ns.config(n.ifName))
    if err != nil {
        return nil, err
    }
    return types100.NewResultFromResult(r)
}

SetUp遍历 Networks ,add 每个Network

github.com/containernetworking/cni/libcni/api.go// AddNetworkList executes a sequence of plugins with the ADD command
func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
    var err error
    var result types.Result
    for _, net := range list.Plugins {
        result, err = c.addNetwork(ctx, list.Name, list.CNIVersion, net, result, rt)
        if err != nil {
            return nil, fmt.Errorf("plugin %s failed (add): %w", pluginDescription(net.Network), err)
        }
    }
    return result, nil
}


func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
    return invoke.ExecPluginWithResult(ctx, pluginPath, newConf.Bytes, c.args("ADD", rt), c.exec)
}

addNetwork 实际就是调用 DefaultCNIDir = "/opt/cni/bin" 目录下的CNI插件实际实现(bridge,flannel等)
参数 NetworkConfigList 到 NetworkConfig 的关系如下


image.png

上面还都是是containerd 中

flannel-cni

flannel 实现了 CNI 插件

//flannel.go
func main() {
    fullVer := fmt.Sprintf("CNI Plugin %s version %s (%s/%s) commit %s built on %s", Program, Version, runtime.GOOS, runtime.GOARCH, Commit, buildDate)
    skel.PluginMain(cmdAdd, cmdCheck, cmdDel, cni.All, fullVer)
}

可以看到CNI 的标准实现,cmdAdd,cmdCheck....,我们分析 cmdAdd

func cmdAdd(args *skel.CmdArgs) error {
        //加载 NetworkConfig  配置,设置默认的 SubnetFile(/run/flannel/subnet.env)
    n, err := loadFlannelNetConf(args.StdinData)
        //加载 /run/flannel/subnet.env
        fenv, err := loadFlannelSubnetEnv(n.SubnetFile)
       return doCmdAdd(args, n, fenv)
}
//run/flannel/subnet.env 文件内容示例
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24 
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

/run/flannel/subnet.env 文件是flannel 启动时输出,用于后面的配置转换。

//flannel_linux.go
func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error {
    return delegateAdd(args.ContainerID, n.DataDir, n.Delegate)
}

//flannel.go
func delegateAdd(cid, dataDir string, netconf map[string]interface{}) error {
    if err = saveScratchNetConf(cid, dataDir, netconfBytes); err != nil {
        return err
    }
    result, err := invoke.DelegateAdd(context.TODO(), netconf["type"].(string), netconfBytes, nil)
}

flannel cni插件会做配置转换,并调用转换后的插件。

//saveScratchNetConf + un/flannel/subnet.env 的内容
{
    "name": "mynet",
    "type": "flannel"
}

FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24 
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

转换为
{
    "name": "mynet",
    "type": "bridge",
    "mtu": 1472,
    "ipMasq": false,
    "isGateway": true,
    "ipam": {
        "type": "host-local",
        "subnet": "10.244.0.1/24"
    }
}

bridge 插件完成后,运行在host上的 flannel 接管了网络。

上面 flannel cni插件会做配置转换 很重要,flannel cni插件通过重新调用转换后的配置文件的插件来配置网络(比如bridge)。

相关文章

网友评论

      本文标题:k8s flannel cni plugin

      本文链接:https://www.haomeiwen.com/subject/sxfpdrtx.html