刨根问底 Kubernetes -- CNI (三)Multus

news2024/11/17 5:58:28

文章目录

  • Multus 概述
  • Multus 使用
    • Multus DaemonSet 的作用
    • Multus 的使用
    • Multus 的 处理
      • 1. 从 input 加载 netConf, 将 cni 配置加载到 netConf.Delegates
      • 2. 加载委托插件(delegate)并将其添加至 multus 配置
        • 2.1. 尝试解析 Pod 注解中 multus 配置
        • 2.2. 获取指定的 Network-attachment-definition
      • 3. 加载 delegate 后

Multus 概述

Kubernetes 的 POD 默认不支持多网卡设置,我们通过 Multus CNI 来为 Pod 建立多网络接口。

Multus 使用

Multus 安装很简单

$ git clone https://github.com/intel/multus-cni.git && cd multus-cni
$ cat ./images/multus-daemonset.yml | kubectl apply -f -

Multus DaemonSet 的作用

  • 启动一个 Multus 守护程序集,这会在每个节点上运行一个 pod,它在每个节点上放置一个 Multus 二进制文件/opt/cni/bin

  • 读取按字典顺序(按字母顺序)的第一个配置文件/etc/cni/net.d,并在每个节点上为 Multus 创建一个新的配置文件/etc/cni/net.d/00-multus.conf,此配置是自动生成的,并且基于默认网络配置(假定为按字母顺序排列的第一个配置)

  • 在每个节点上创建一个/etc/cni/net.d/multus.d目录,其中包含 Multus 访问 Kubernetes API 的身份验证信息。

  • 安装 network-attachment-definition CRD,这个 CRD 存储着其他 CNI 的信息,在 Multus 看到使用此资源时,会调用设置的 CNI 去配置网络。

其中第二点,安装 Multus 会将原有的 CNI 作为代理的一部分放到自己的 CNI 配置中,当 work 时,也会使用之前的 CNI 创建第一张网卡。
如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qlPG0OVy-1670220998992)(/img/multus.png)]

配置变化, 可以看到 原有的 CNI 配置完全存在于 Multus 的 delegates 里,通过配置变化,containerd 调用 CNI 时实际调用的是 Multus 的 CNI。

/etc/cni/net.d/01-kube-ovn.conflist 
{
    "name":"kube-ovn",
    "cniVersion":"0.3.1",
    "plugins":[
        {
            "type":"kube-ovn",
            "server_socket":"/run/openvswitch/kube-ovn-daemon.sock"
        },
        {
            "type":"portmap",
            "capabilities":{
                "portMappings":true
            }
        }
    ]
}

cat /etc/cni/net.d/00-multus.conf | jq
{
  "capabilities": {
    "portMappings": true
  },
  "cniVersion": "0.3.1",
  "delegates": [
    {
      "cniVersion": "0.3.1",
      "name": "kube-ovn",
      "plugins": [
        {
          "server_socket": "/run/openvswitch/kube-ovn-daemon.sock",
          "type": "kube-ovn"
        },
        {
          "capabilities": {
            "portMappings": true
          },
          "type": "portmap"
        }
      ]
    }
  ],
  "logLevel": "debug",
  "logToStderr": true,
  "kubeconfig": "/etc/cni/net.d/multus.d/multus.kubeconfig",
  "name": "multus-cni-network",
  "type": "multus"
}

Multus 的使用

希望两个网卡都使用 kube-ovn 为 pod 创建
Network-Attachment-Definition 创建

apiVersion: v1
kind: Namespace
metadata:
  name: mec-nets
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: attachnet1
  namespace: mec-nets
spec:
  config: '{
      "cniVersion": "0.3.0",
      "type": "kube-ovn",
      "server_socket": "/run/openvswitch/kube-ovn-daemon.sock",
      "provider": "attachnet1.mec-nets.ovn"
    }

创建 Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod9
  annotations:
    ovn.kubernetes.io/logical_switch: subnet-ipv4-ns-demo-100
    k8s.v1.cni.cncf.io/networks: mec-nets/attachnet1   # 选择 NetworkAttachmentDefinition
    attachnet1.mec-nets.ovn.kubernetes.io/logical_switch: subnet-ipv4-ns-demo-100
spec:
  containers:
  - name: pod9
    command: ["/bin/ash", "-c", "trap : TERM INT; sleep 36000 & wait"]
    image: rancher/curl
  nodeSelector:
    kubernetes.io/hostname: eci2

Multus 的 处理

符合 CNI 的标准规范,在 containerd 调用 plugin (multus 二进制) 后,multus 进行处理

pkg/multus/miltus.go L557

func CmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (cnitypes.Result, error) {
    n, err := types.LoadNetConf(args.StdinData)
}

1. 从 input 加载 netConf, 将 cni 配置加载到 netConf.Delegates

n, err := types.LoadNetConf(args.StdinData)

2. 加载委托插件(delegate)并将其添加至 multus 配置

	_, kc, err := k8s.TryLoadPodDelegates(pod, n, kubeClient, resourceMap)
	if err != nil {
		return nil, cmdErr(k8sArgs, "error loading k8s delegates k8s args: %v", err)
	}

2.1. 尝试解析 Pod 注解中 multus 配置

v1.multus-cni.io/default-network
是否携带 v1.multus-cni.io/default-network 键值对
pkg/k8sclient/k8sclient.go L323

func TryLoadPodDelegates(pod *v1.Pod, conf *types.NetConf, clientInfo *ClientInfo, resourceMap map[string]*types.ResourceInfo) (int, *ClientInfo, error) {
	var err error
    ......
    delegate, err := tryLoadK8sPodDefaultNetwork(clientInfo, pod, conf)
	if err != nil {
		return 0, nil, logging.Errorf("TryLoadPodDelegates: error in loading K8s cluster default network from pod annotation: %v", err)
	}
	if delegate != nil {
		logging.Debugf("TryLoadPodDelegates: Overwrite the cluster default network with %v from pod annotations", delegate)

		conf.Delegates[0] = delegate
	}

k8s.v1.cni.cncf.io/networks
是否携带 k8s.v1.cni.cncf.io/networks 键值对, 可以看到 该配置可以多个。

	networks, err := GetPodNetwork(pod)

parsePodNetworkAnnotation 拆解 k8s.v1.cni.cncf.io/networks 对应的值用于初始化 types.NetworkSelectionElement 并追加至切片:

func GetPodNetwork(pod *v1.Pod) ([]*types.NetworkSelectionElement, error) {
	logging.Debugf("GetPodNetwork: %v", pod)

	netAnnot := pod.Annotations[networkAttachmentAnnot]
	defaultNamespace := pod.ObjectMeta.Namespace

	if len(netAnnot) == 0 {
		return nil, &NoK8sNetworkError{"no kubernetes network found"}
	}

	networks, err := parsePodNetworkAnnotation(netAnnot, defaultNamespace)
	if err != nil {
		return nil, err
	}
	return networks, nil
}

像上面示例 pod9 中 mec-nets/attachnet1, network 的 Name 是 attachnet1, Namespace 是 mec-nets, InterfaceRequest 可以命名网卡名称, 如果要 eth10 则命名为 mec-nets/attachnet1@eth10

2.2. 获取指定的 Network-attachment-definition

	if networks != nil {
		delegates, err := GetNetworkDelegates(clientInfo, pod, networks, conf, resourceMap)

		if err != nil {
			if _, ok := err.(*NoK8sNetworkError); ok {
				return 0, clientInfo, nil
			}
			return 0, nil, logging.Errorf("TryLoadPodDelegates: error in getting k8s network for pod: %v", err)
		}

		if err = conf.AddDelegates(delegates); err != nil {
			return 0, nil, err
		}

		return len(delegates), clientInfo, err
	}

getKubernetesDelegate
pkg/k8sclient/k8sclient.go L254

func getKubernetesDelegate(client *ClientInfo, net *types.NetworkSelectionElement, confdir string, pod *v1.Pod, resourceMap map[string]*types.ResourceInfo) (*types.DelegateNetConf, map[string]*types.ResourceInfo, error) {

	logging.Debugf("getKubernetesDelegate: %v, %v, %s, %v, %v", client, net, confdir, pod, resourceMap)
	customResource, err := client.NetClient.NetworkAttachmentDefinitions(net.Namespace).Get(context.TODO(), net.Name, metav1.GetOptions{})
	if err != nil {
		errMsg := fmt.Sprintf("cannot find a network-attachment-definition (%s) in namespace (%s): %v", net.Name, net.Namespace, err)
		if client != nil {
			client.Eventf(pod, v1.EventTypeWarning, "NoNetworkFound", errMsg)
		}
		return nil, resourceMap, logging.Errorf("getKubernetesDelegate: " + errMsg)
	}
......
	configBytes, err := netutils.GetCNIConfig(customResource, confdir)
	if err != nil {
		return nil, resourceMap, err
	}

	delegate, err := types.LoadDelegateNetConf(configBytes, net, deviceID, resourceName)
	if err != nil {
		return nil, resourceMap, err
	}

	return delegate, resourceMap, nil
}

解析后得到 NetworkAttachmentDefinition 的 spec 字段中的配置字符串:

{ 
    "cniVersion": "0.3.0", 
    "type": "kube-ovn", 
    "server_socket": "/run/openvswitch/kube-ovn-daemon.sock",
    "provider": "attachnet1.mec-nets.ovn" 
}

使用 该 NAD 的 CNI delegate 使用 kube-ovn CNI。

3. 加载 delegate 后

在补充完配置结构体后,遍历它的 Delegates 字段:
/pkg/multus/multus.go L612

	var result, tmpResult cnitypes.Result
	var netStatus []nettypes.NetworkStatus
	for idx, delegate := range n.Delegates {
		ifName := getIfname(delegate, args.IfName, idx)
  • 3.1 获取网卡名称
    如果 attachnet1/mec-nets@eth10 这种已配置的,会根据索引 + net 确定网卡名
func getIfname(delegate *types.DelegateNetConf, argif string, idx int) string {
	logging.Debugf("getIfname: %v, %s, %d", delegate, argif, idx)
	if delegate.IfnameRequest != "" {
		return delegate.IfnameRequest
	}
	if delegate.MasterPlugin {
		// master plugin always uses the CNI-provided interface name
		return argif
	}

	// Otherwise construct a unique interface name from the delegate's
	// position in the delegate list
	return fmt.Sprintf("net%d", idx)
}
  • 3.2 使用 DelegateAdd 为 pod 配置网络
	if delegate.ConfListPlugin {
		result, err = conflistAdd(rt, delegate.Bytes, multusNetconf, exec)
		if err != nil {
			return nil, err
		}
	} else {
		result, err = confAdd(rt, delegate.Bytes, multusNetconf, exec)
		if err != nil {
			return nil, err
		}
	}
func conflistAdd(rt *libcni.RuntimeConf, rawnetconflist []byte, multusNetconf *types.NetConf, exec invoke.Exec) (cnitypes.Result, error) {
	logging.Debugf("conflistAdd: %v, %s", rt, string(rawnetconflist))
	// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
	binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
	binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
	cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)

	confList, err := libcni.ConfListFromBytes(rawnetconflist)
	if err != nil {
		return nil, logging.Errorf("conflistAdd: error converting the raw bytes into a conflist: %v", err)
	}

	result, err := cniNet.AddNetworkList(context.Background(), confList, rt)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func conflistAdd(rt *libcni.RuntimeConf, rawnetconflist []byte, multusNetconf *types.NetConf, exec invoke.Exec) (cnitypes.Result, error) {
	logging.Debugf("conflistAdd: %v, %s", rt, string(rawnetconflist))
	// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
	binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
	binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
	cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)

	confList, err := libcni.ConfListFromBytes(rawnetconflist)
	if err != nil {
		return nil, logging.Errorf("conflistAdd: error converting the raw bytes into a conflist: %v", err)
	}

	result, err := cniNet.AddNetworkList(context.Background(), confList, rt)
	if err != nil {
		return nil, err
	}

	return result, nil
}
  • 3.3 执行多次 CNI 网络插件的 ADD 操作。
    如 pod9 的日志
    debug
conflistAdd: &{24a1efa35675f1382fa12a5849c0597bf4bcb7e4c1d7764dc2b5f3701b57b11c /var/run/netns/cni-3b58095c-52a9-c2c4-d9ba-ad30f44fee13 eth0 [[IgnoreUnknown true] [K8S_POD_NAMESPACE default] [K8S_POD_NAME pod9] [K8S_POD_INFRA_CONTAINER_ID 24a1efa35675f1382fa12a5849c0597bf4bcb7e4c1d7764dc2b5f3701b57b11c] [K8S_POD_UID 3da6f436-bb79-461e-8595-6779f972ca76] [K8S_POD_NAMESPACE default] [K8S_POD_NAME pod9] [K8S_POD_INFRA_CONTAINER_ID 24a1efa35675f1382fa12a5849c0597bf4bcb7e4c1d7764dc2b5f3701b57b11c] [K8S_POD_UID 3da6f436-bb79-461e-8595-6779f972ca76] [IgnoreUnknown 1]] map[] }, {"cniVersion":"0.3.1","name":"kube-ovn","plugins":[{"server_socket":"/run/openvswitch/kube-ovn-daemon.sock","type":"kube-ovn"},{"capabilities":{"portMappings":true},"type":"portmap"}]}


confAdd: &{24a1efa35675f1382fa12a5849c0597bf4bcb7e4c1d7764dc2b5f3701b57b11c /var/run/netns/cni-3b58095c-52a9-c2c4-d9ba-ad30f44fee13 net1 [[IgnoreUnknown true] [K8S_POD_NAMESPACE default] [K8S_POD_NAME pod9] [K8S_POD_INFRA_CONTAINER_ID 24a1efa35675f1382fa12a5849c0597bf4bcb7e4c1d7764dc2b5f3701b57b11c] [K8S_POD_UID 3da6f436-bb79-461e-8595-6779f972ca76] [K8S_POD_NAMESPACE default] [K8S_POD_NAME pod9] [K8S_POD_INFRA_CONTAINER_ID 24a1efa35675f1382fa12a5849c0597bf4bcb7e4c1d7764dc2b5f3701b57b11c] [K8S_POD_UID 3da6f436-bb79-461e-8595-6779f972ca76] [IgnoreUnknown 1]] map[] }, {"cniVersion":"0.3.0","name":"attachnet1","provider":"attachnet1.mec-nets.ovn","server_socket":"/run/openvswitch/kube-ovn-daemon.sock","type":"kube-ovn"}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/63376.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实践案例丨CenterNet-Hourglass论文复现

摘要:本案例是CenterNet-Hourglass论文复现的体验案例,此模型是对Objects as Points 中提出的CenterNet进行结果复现。本文分享自华为云社区《CenterNet-Hourglass (物体检测/Pytorch)》,作者:HWCloudAI。 目标检测常采用Anchor的…

【正点原子FPGA连载】第二十七章 MDIO接口读写测试实验 摘自【正点原子】DFZU2EG/4EV MPSoC 之FPGA开发指南V1.0

1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第二十七章 MDIO…

字典类型和字典函数、字典方法

字典类型 (无序&#xff0c;不能重复) 通过任意键信息查找一组数据中值信息的过程叫映射&#xff0c; Python语言中通过字典实现映射。 Python语言中的字典可以通过大括号({})建立&#xff0c;建立模式如下&#xff1a; {<键1>:<值1>,<键2>:<值2>,...,…

[附源码]Python计算机毕业设计SSM健身房管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

p15~p22基本链表容器和高级链表容器迭代器

STL一、自制链表容器/基本链表容器1.1 首/尾部增删节点1.2 获取首/尾部的元素1.3 清空链表7 / 判空链表 / 链表大小81.4 缺省构造0/拷贝构造10/析构函数91.5 输出流操作符重载二、迭代器原理2.1 迭代器概念2.2 迭代器的分类三、迭代器实现3.1 正向非常迭代类3.2 正向非常迭代器…

html旅游网站设计与实现——绿色古典旅游景区 HTML+CSS+JavaScript

&#x1f468;‍&#x1f393;学生HTML静态网页基础水平制作&#x1f469;‍&#x1f393;&#xff0c;页面排版干净简洁。使用HTMLCSS页面布局设计,web大学生网页设计作业源码&#xff0c;这是一个不错的旅游网页制作&#xff0c;画面精明&#xff0c;排版整洁&#xff0c;内容…

解析仓库管理系统对于企业的重要性

仓储管理的职责是有效的保存和管理仓库内的物资&#xff0c;这些物资是指仓库内所有的有形物品以及无形的资产。以前很多企业都是依靠人工方式对库房的管理&#xff0c;难免会造成一些难以解决的问题&#xff1a; 仓库种类太多&#xff0c;查看困难&#xff1b;仓库信息记录不…

Java应用程序安全框架

《从零打造项目》系列文章 工具 比MyBatis Generator更强大的代码生成器 ORM框架选型 SpringBoot项目基础设施搭建SpringBoot集成Mybatis项目实操SpringBoot集成MybatisPlus项目实操SpringBoot集成Spring Data JPA项目实操 数据库变更管理 数据库变更管理&#xff1a;Liquibase…

Word控件Spire.Doc 【图像形状】教程(11): 如何在 C# 中为 Word 中的图像设置 Transeperant 颜色

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

A-Level经济题解析及练习Policy options for Common Resources

今日知识点&#xff1a;Policy options for Common Resources 例题 There is a medieval town where sheep graze on common land. As the population grows, the number of sheep grows. However, the amount of land is fixed, the grass begins to disappear from overgra…

SwiftUI 中为什么应该经常用子视图替换父视图中的大段内容?

概览 在 SwiftUI 官方教程中&#xff0c;Apple 时常提出“化整为零”的界面布局思想。简单来说&#xff0c;Apple 推荐 SwiftUI 视图的构建方式是&#xff1a;用若干自定义小视图来构成上层的功能视图。 这是为什么呢&#xff1f; 在本篇博文中&#xff0c;我们将用一个通俗…

[Java反序列化]—CommonsCollections6

先贴个图 0x01: CC 6 应该是CC1 和 URLDNS 的综合&#xff0c;有一定联系&#xff0c;审一下吧 JDK版本需低于 8u71 AnnotationInvocationHandler类的readObject()方法在8u71以后逻辑就发生了改变&#xff0c;不能再利用了&#xff0c;所以就需要找一个绕过高版本的利用链…

Cadence Virtuoso Layout 版图绘制的使用技巧及其相关快捷键

1.版图前准备操作 画好原理图&#xff0c;打好pin脚&#xff08;pin最好以全大写的形式书写&#xff0c;以防后续操作中可能出现Bug&#xff09; 查看所使用工艺库的design rule文件&#xff0c;确定栅格单位设置大小 在准备绘制的原理图界面启动layout XL/GXL 在layout界面…

JS 正则表达式常用方法

1. JS 正则表达式 2. 使用字符串方法 3. 使用 RegExp 方法 1. JS 正则表达式 JS 正则表达式语法: # JS 的正则表达式不需要使用引号包裹&#xff0c;PHP 需要使用引号包裹。修饰符是可选的&#xff0c;可写可不写/正则表达式主体/修饰符JS 中使用正则表达式的方法比较多&am…

【强化学习论文合集】九.2018AAAI人工智能大会论文(AAAI2018)

强化学习(Reinforcement Learning, RL),又称再励学习、评价学习或增强学习,是机器学习的范式和方法论之一,用于描述和解决智能体(agent)在与环境的交互过程中通过学习策略以达成回报最大化或实现特定目标的问题。 本专栏整理了近几年国际顶级会议中,涉及强化学习(Rein…

Python中的Apriori关联算法-市场购物篮分析

数据科学Apriori算法是一种数据挖掘技术&#xff0c;用于挖掘频繁项集和相关的关联规则。本模块重点介绍什么是关联规则挖掘和Apriori算法&#xff0c;以及Apriori算法的用法。 去年&#xff0c;我们为一家公司进行了短暂的咨询工作&#xff0c;该公司正在构建一个主要基于Apr…

使用DIV+CSS技术设计的非遗文化网页与实现制作(web前端网页制作课作业)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

m基于自适应遗传优化的IEEE-6建设费用和网络损耗费用最小化电网规划算法matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 电力工业是当今世界各国经济的重要组成部分&#xff0c;随着世界经济的不断发展&#xff0c;电网的建设和中长期规划和经济发展之间的矛盾变得越来越突出&#xff0c;对电力系统的需求也变得越来…

微服务框架 SpringCloud微服务架构 16 SpringAMQP 16.7 DirectExchange

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 SpringCloud微服务架构 文章目录微服务框架SpringCloud微服务架构16 SpringAMQP16.7 DirectExchange16.7.1 发布订阅 - DirectExchange16.7.…

基于遗传优化算法的小车障碍物避障路线规划matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 一种通过模拟自然进化过程搜索最优解的方法&#xff0c;对于一个最优化问题&#xff0c;该算法通过一定数量的候选解种群迭代地执行选择、交叉、变异、评价等操作使得种群向更好的解进化。 遗传算…