【kubernetes/k8s概念】CNI plugin bridge源码分析

2023-05-16

什么是bridge

        bridge是一个虚拟网络设备,可以配置IP、MAC地址等;其次,bridge是一个虚拟交换机,和物理交换机有类似的功能。

       普通的网络设备只有两端,从一端进来的数据会从另一端出去,如物理网卡从外面网络中收到的数据会转发给内核协议栈,而从协议栈过来的数据会转发到外面的物理网络中。

        Bridge(桥)是 Linux 上用来做 TCP/IP 二层协议交换的设备,与物理交换机功能相似。网桥是一种在链路层实现中继,对帧进行转发的技术,根据MAC分区块,可隔离碰撞,将网络的多个网段在数据链路层连接起来的网络设备。 Bridge 设备可以和 Linux 上其他网络设备实例连接,既 attach 一个从设备,类似于在现实世界中的交换机和一个用户终端之间连接一根网线。当有数据到达时,Bridge 会根据报文中的 MAC 信息进行广播、转发、丢弃处理

        bridge是用于连接两个不同网段的常见手段,不同网络段通过bridge连接后就如同在一个网段一样,工作原理很简单就是L2数据链路层进行数据包的转发。

原理

       bridge和route比较相似,都可以用来分发网络数据包,它们的本质不同在于:route在L3网络层,使用路由协议、bridge在L2数据链路层,通过学习和缓存在链路上传输的数据包中的源地址以及物理层的输入端口:

  • 收到新数据包时,记录源MAC地址和端口
  • 根据目的MAC查找本地缓存,如果能找到对应的MAC地址记录
  • 若发现记录不在本地网络,直接丢弃数据包
  • 若发现记录存在对应的端口,则将数据包直接从该端口转发出去
  • 如果本地缓存中不存在任何记录,则在本网段中进行广播。

 

1、结构体

(1)、NetConf结构体

  • name (string, required): the name of the network. 
  •  type (string, required): "bridge". 
  •  bridge (string, optional): name of the bridge to use/create. Defaults to "cni0". 
  •  isGateway (boolean, optional): assign an IP address to the bridge. Defaults to false. 
  •  isDefaultGateway (boolean, optional): Sets isGateway to true and makes the assigned IP the default route. Defaults to false. 
  •  forceAddress (boolean, optional): Indicates if a new IP address should be set if the previous value has been changed. Defaults to false. 
  •  ipMasq (boolean, optional): set up IP Masquerade on the host for traffic originating from this network and destined outside of it. Defaults to false. 
  •  mtu (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel. 
  •  hairpinMode (boolean, optional): set hairpin mode for interfaces on the bridge. Defaults to false. 
  •  ipam (dictionary, required): IPAM configuration to be used for this network. 
  •  promiscMode (boolean, optional): set promiscuous mode on the bridge. Defaults to false. 
type NetConf struct {
	types.NetConf
	BrName       string `json:"bridge"`
	IsGW         bool   `json:"isGateway"`
	IsDefaultGW  bool   `json:"isDefaultGateway"`
	ForceAddress bool   `json:"forceAddress"`
	IPMasq       bool   `json:"ipMasq"`
	MTU          int    `json:"mtu"`
	HairpinMode  bool   `json:"hairpinMode"`
	PromiscMode  bool   `json:"promiscMode"`
}

使用如下所时:

{
    "name": "mynet",
    "type": "bridge",
    "bridge": "mynet0",
    "isDefaultGateway": true,
    "forceAddress": false,
    "ipMasq": true,
    "hairpinMode": true,
    "ipam": {
        "type": "host-local",
        "subnet": "10.10.0.0/16"
    }
}
 

2、main函数

        main 函数主要是实现了两个主要方法cmdAdd,cmdDel,主要分析cmdAdd实现

func main() {
	// TODO: implement plugin version
	skel.PluginMain(cmdAdd, cmdGet, cmdDel, version.All, "TODO")
}

3、cmdAdd函数

       cmdAdd函数实现的内容表多,可以分开解析

   (3.1)、loadNetConf函数

       loadNetConf主要是得到NetConf结构体配置信息

func loadNetConf(bytes []byte) (*NetConf, string, error) {
	n := &NetConf{
		BrName: defaultBrName,
	}
	if err := json.Unmarshal(bytes, n); err != nil {
		return nil, "", fmt.Errorf("failed to load netconf: %v", err)
	}
	return n, n.CNIVersion, nil
}

 (3.2)、setupBridge函数

  •  setupBridge里面调用ensureBridge,前面吧啦吧啦设置了一大队系统调用参数,
  •  通过netlink.LinkAdd(br)创建网桥,相当于ip link add br-test type bridge
  •  然后通过 netlink.LinkSetUp(br)启动网桥,相当于ip link set dev br-test 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)
	}

	return br, &current.Interface{
		Name: br.Attrs().Name,
		Mac:  br.Attrs().HardwareAddr.String(),
	}, nil
}

 (3.3)、setupVeth函数

  •  调用netlink.LinkAdd(veth)创建veth,这个是一个管道,Linux的网卡对,在容器对应的namespace下创建好虚拟网络接口,相当于ip link add test-veth0 type veth peer name test-veth1
  •  调用netlink.LinkSetUp(contVeth)启动容器端网卡,相当于ip link set dev test-veth0 up
  • 调用netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd()))将host端加入namespace中,相当于ip link set $link netns $ns
  •  调用netlink.LinkSetMaster(hostVeth, br)绑到bridge,相当于ip link set dev test-veth0 master br-test
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
}

 (3.4)、ipam.ExecAdd函数

  •  调用ipam.ExecAdd获取IP地址
func ExecAdd(plugin string, netconf []byte) (types.Result, error) {
	return invoke.DelegateAdd(plugin, netconf, nil)
}

  (3.5)、calcGateways函数

  •   调用calcGateways根据IP地址计算对应的路由和网关
// Gather gateway information for each IP family
	gwsV4, gwsV6, err := calcGateways(result, n)
	if err != nil {
		return err
	}

  (3.6)、函数

  •   调用ipam.ConfigureIface将IP地址设置到对应的虚拟网络接口上,相当于ifconfig test-veth0 192.168.209.135/24 up
  •   调用enableIPForward(gws.family)开启ip转发,路径/proc/sys/net/ipv4/ip_forward写入值1
// Configure the container hardware address and IP address(es)
	if err := netns.Do(func(_ ns.NetNS) error {
		contVeth, err := net.InterfaceByName(args.IfName)
		if err != nil {
			return err
		}

		// Disable IPv6 DAD just in case hairpin mode is enabled on the
		// bridge. Hairpin mode causes echos of neighbor solicitation
		// packets, which causes DAD failures.
		for _, ipc := range result.IPs {
			if ipc.Version == "6" && (n.HairpinMode || n.PromiscMode) {
				if err := disableIPV6DAD(args.IfName); err != nil {
					return err
				}
				break
			}
		}

		// 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
	}

 4、ip.SetupIPMasq函数

  •   调用ip.SetupIPMasq建立iptables规则
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
			}
		}
	}
  • -A POSTROUTING -s 172.30.3.0/24 -m comment --comment "name: \"cbr0\" id: \"7567cf04805c47d94a3d9ad1db32f7cb424fd058c497b5f450278c1ee9c07e5b\"" -j CNI-15aa38615ee8a1e7bfb519c7
  • -A CNI-15aa38615ee8a1e7bfb519c7 -d 172.30.3.0/24 -m comment --comment "name: \"cbr0\" id: \"7567cf04805c47d94a3d9ad1db32f7cb424fd058c497b5f450278c1ee9c07e5b\"" -j ACCEPT
  • -A CNI-15aa38615ee8a1e7bfb519c7 ! -d 224.0.0.0/4 -m comment --comment "name: \"cbr0\" id: \"7567cf04805c47d94a3d9ad1db32f7cb424fd058c497b5f450278c1ee9c07e5b\"" -j MASQUERADE


  4.1 FormatChainName函數

  格式維 CNI-15aa38615ee8a1e7bfb519c7,容器id

// Generates a chain name to be used with iptables.
// Ensures that the generated chain name is exactly
// maxChainLength chars in length
func FormatChainName(name string, id string) string {
   chainBytes := sha512.Sum512([]byte(name + id))
   chain := fmt.Sprintf("%s%x", chainPrefix, chainBytes)
   return chain[:maxChainLength]
}

  4.2 SetupIPMasq函数

  终于找到罪魁祸首了,这里设置了iptables,如上iptables规则,刨出组播地址224.0.0.0/4

  添加了如上三条规则

// SetupIPMasq installs iptables rules to masquerade traffic
// coming from ipn and going outside of it
func SetupIPMasq(ipn *net.IPNet, chain string, comment string) error {
	isV6 := ipn.IP.To4() == nil

	var ipt *iptables.IPTables
	var err error
	var multicastNet string

	if isV6 {
		ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6)
		multicastNet = "ff00::/8"
	} else {
		ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
		multicastNet = "224.0.0.0/4"
	}
	if err != nil {
		return fmt.Errorf("failed to locate iptables: %v", err)
	}

	// Create chain if doesn't exist
	exists := false
	chains, err := ipt.ListChains("nat")
	if err != nil {
		return fmt.Errorf("failed to list chains: %v", err)
	}
	for _, ch := range chains {
		if ch == chain {
			exists = true
			break
		}
	}
	if !exists {
		if err = ipt.NewChain("nat", chain); err != nil {
			return err
		}
	}

	// Packets to this network should not be touched
	if err := ipt.AppendUnique("nat", chain, "-d", ipn.String(), "-j", "ACCEPT", "-m", "comment", "--comment", comment); err != nil {
		return err
	}

	// Don't masquerade multicast - pods should be able to talk to other pods
	// on the local network via multicast.
	if err := ipt.AppendUnique("nat", chain, "!", "-d", multicastNet, "-j", "MASQUERADE", "-m", "comment", "--comment", comment); err != nil {
		return err
	}

	return ipt.AppendUnique("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment)
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【kubernetes/k8s概念】CNI plugin bridge源码分析 的相关文章

  • Kubernetes Pod 已终止 - 退出代码 137

    我需要一些关于 k8s 1 14 和在其上运行 gitlab 管道所面临的问题的建议 许多作业都会抛出退出代码 137 错误 我发现这意味着容器突然终止 集群信息 库伯内特版本 1 14 使用的云 AWS EKS 节点 C5 4xLarge
  • Istio 允许所有出口流量

    如何允许 Istio 的所有出站流量 我尝试了以下方法 禁用出口网关并 使用 set global proxy includeIPRanges 0 0 0 0 0 0 0 0 0 0 绕过 sidecar 所有选项都不起作用 值得一提的是我
  • 在容器中运行多个相似的进程有意义吗?

    提供有关该问题的背景的简要背景 目前 我和我的团队正在将微服务迁移到 k8s 以减少维护多个部署工具和管道的工作量 我们计划迁移的微服务之一是 ETL Worker 它监听 SQS 上的消息并执行多阶段处理 它是使用 PHP Laravel
  • 如何为容器设置正确的 cpu 毫核?

    我想要优化配置 CPU 核心 而不会分配过多或不足 如何测量给定容器所需的 CPU 毫核 它还带来了一个问题 即代理将根据 CPU 消耗将多少流量发送到任何给定的 Pod 以便我们可以最佳地使用计算 目前我发送请求并进行监控 kubectl
  • Spark 执行器 STDOUT 到 Kubernetes STDOUT

    我在 Spark Worker 中运行的 Spark 应用程序将执行程序日志输出到特定文件路径 worker home directory app xxxxxxxx 0 stdout I used log4j properties将日志从
  • 如何将新的 Kubernetes Minion 添加到当前集群

    我有一个运行在 3 台服务器上的 Kubernetes 集群 一台主服务器和 2 台服务器 我想添加另一个小黄人 是否可以添加 Minion 而无需再次进行完整安装 到目前为止 在寻找执行此操作的指南时 我只能找到有关建立整个集群的优秀指南
  • 入口和 SSL 直通

    我最近一直在使用 nginxdemo nginx ingress 控制器 据我了解 该控制器无法执行 SSL 直通 我的意思是 将客户端证书一直传递到后端服务进行身份验证 因此我一直通过标头传递客户端主题 DN 最终 我更喜欢 SSL 直通
  • 匹配同一端口上不同路径的 Istio 虚拟服务路由

    我想知道如何在同一端口上匹配 gRPC 路由 以下是我希望通过 VirtualService 实现的目标的示例 apiVersion networking istio io v1alpha3 kind VirtualService meta
  • Google Kubernetes Engine 中的存储 ReadWriteMany

    有没有一种方法能够提供 ReadWriteMany 存储而无需实现存储集群 我能够使用 gcsfuse 提供存储 但速度非常慢 我需要接近 GlusterFS 速度的东西 我目前正在使用 GlusterFS 另一种选择 Google Clo
  • 如何为某些节点分配命名空间?

    有什么办法可以配置吗nodeSelector在命名空间级别 我只想在此命名空间的某些节点上运行工作负载 为了达到这个目的 你可以使用PodNodeSelector准入控制器 首先 您需要在您的kubernetes apiserver Edi
  • Kubernetes 通过基于时间的触发器扩展 Pod

    我有一台在 Kubernetes 上运行的服务器来处理每小时的处理作业 考虑使用服务来公开 pod 并使用 外部 cron 作业来访问负载均衡器 以便 kubernetes 可以根据需要自动缩放以处理更高的负载 然而在实现中 如果 cron
  • k8s书签解决什么问题?

    我正在尝试做什么 我正在尝试进行部署并监视 k8s 事件 直到部署准备好使用k8s节点API 手表 https github com kubernetes client javascript blob master examples typ
  • kubernetes,提示符在端口转发命令处冻结

    我想在在线指南之后设置从本地端口 节点 到 pod redis master 的端口转发库伯内特斯 https kubernetes io docs tasks access application cluster port forward
  • Helm 3 图表安装错误:验证数据时出错:未设置 apiVersion

    我有一个简单的 helm 图表 它将通过 docker 桌面将应用程序部署到我的 kubernetes 本地副本 如果我使用 kubectl 一次部署一个 yaml 文件 一切都会正常工作 但是 当我尝试创建 helm 图表以方便部署时 出
  • 您可以将 Docker 映像直接拉入 IBM Cloud Kubernetes 集群吗?

    TL DR 抱歉 如果这是基础知识 我正在学习 Kubernetes 我尝试在 IBM Cloud 中创建 Kubernetes 部署 但失败了 该部署在我的本地 minikube 上运行良好 但在 IBM Cloud 中失败 我是否需要使
  • Rabbit mq - 等待 Mnesia 表时出错

    我已经在 Kubernetes 集群上使用 Helm Chart 安装了 RabbitMQ rabbitmq pod不断重新启动 在检查 pod 日志时 我收到以下错误 2020 02 26 04 42 31 582 warning lt
  • 如何将我的 pod 日志存储在持久存储中?

    我已经使用以下命令为我的 Pod 生成了日志kubectl logs pod name 但我想将这些日志保存在一个卷 某种持久存储 中 因为如果 Pod 宕机 容器日志将被清除 有没有办法做到这一点 我必须写某种脚本吗 我已经阅读了很多答案
  • 我可以将应用程序数据存储在 kubernetes 配置资源中吗?

    我正在尝试为我的应用程序找到一个数据库 对象存储 该应用程序实际上是 ISTIO 网络路由 API 的包装器 基本上简化了我的网络的 ISTIO 配置 Kubernetes k8s 自定义资源定义 CRD 似乎符合我的要求 也喜欢 CRD
  • FluentD 日志不可读。已排除,下次检查

    面临 fluidd 日志无法读取 已排除 下次检查 我有一个在 kubernetes 设置中运行的 fluidD 守护进程集的简单配置 流利版本 fluidd 0 12 43 以下是我的配置
  • nslookup 无法解析 Kubernetes.default

    我在我的 minikube 设置上尝试了以下命令来验证 dns 是否正常工作 kubectl exec ti busybox nslookup kubernetes default 但这是我得到的输出 服务器 10 96 0 10 地址1

随机推荐