理解CNI网络模型

CNI网络模型

随着容器技术在企业生产系统中的逐步落地,跨主机容器间的网络互通已经成为基本要求,更高的要求包括容器固定IP地址、一个容器多个IP地址、多个子网隔离、ACL控制策略、与SDN集成等。目前主流的容器网络模型主要有Docker公司提出的Container Network Model(CNM)模型和CoreOS公司提出的Container Network Interface(CNI)模型。

CNM模型

CNM模型主要通过Network Sandbox、Endpoint和Network这三个组件进行实现。

截屏2021-05-18 上午10.53.18

  • Network Sandbox:容器内部的网络栈,包括网络接口、路由 表、DNS等配置的管理。Sandbox可用Linux网络命名空间、FreeBSD Jail等机制进行实现。一个Sandbox可以包含多个Endpoint。
  • Endpoint:用于将容器内的Sandbox与外部网络相连的网络接 口。可以使用veth对、Open vSwitch的内部port等技术进行实现。一个 Endpoint仅能够加入一个Network。
  • Network:可以直接互连的Endpoint的集合。可以通过Linux网 桥、VLAN等技术进行实现。一个Network包含多个Endpoint。

CNI模型

CNI是由CoreOS公司提出的另一种容器网络规范,让各个容器管理平台(k8s,mesos等)都可以通过相同的接口调用各式各样的网络插件来为容器配置网络。

截屏2021-05-18 上午10.57.21

CNI定义的是容器运行环境与网络插件之间的简单接口规范,通过 一个JSON Schema定义CNI插件提供的输入和输出参数。一个容器可以通过绑定多个网络插件加入多个网络中。

CNI规范概述

CNI提供了一种应用容器的插件化网络解决方案,定义对容器网络进行操作和配置的规范,通过插件的形式对CNI接口进行实现。CNI仅关注在创建容器时分配网络资源,和在销毁容器时删除 网络资源,这使得CNI规范非常轻巧、易于实现,得到了广泛的支持。

CNI规范的一些要点:

  • CNI规范为一个容器定义一个Linux网络命名空间
  • CNI的网络定义存储为JSON格式
  • 网络定义通过STDIN输入流传输到插件,这意味着宿主机上不会存储网络配置文件
  • 其他的配置参数通过环境变量传递给插件
  • CNI插件为可执行文件
  • CNI插件负责连通容器网络,它要完成所有的工作才能使容器连入网络
  • CNI插件负责调用IPAM插件

CNI模型只涉及两个概念:容器和网络

  • 容器(Container):是拥有独立Linux网络命名空间的环境, 例如使用Docker或rkt创建的容器。关键之处是容器需要拥有自己的 Linux网络命名空间,这是加入网络的必要条件。
  • 网络(Network):表示可以互连的一组实体,这些实体拥有 各自独立、唯一的IP地址,可以是容器、物理机或者其他网络设备(比 如路由器)等。

对容器网络的设置和操作都通过插件(Plugin)进行具体实现, CNI插件包括两种类型:CNI Plugin和IPAM(IP Address Management) Plugin。CNI Plugin负责为容器配置网络资源,IPAM Plugin负责对容器 的IP地址进行分配和管理。IPAM Plugin作为CNI Plugin的一部分,与 CNI Plugin一起工作。

CNI Plugin插件详解

CNI Plugin包括3个基本接口的定义:添加(ADD)、删除 (DELETE)、检查(CHECK)和版本查询(VERSION)。这些接口的具体实现要求插件提供一个可执行的程序,在容器网络添加或删除时进行调用,以完成具体的操作。比如添加接口的参数有Version、ContanierID、Network namespace、Network configuration、Extra arguments、Name of the interface inside the container。

网络配置参数(Network configuration)则由一个JSON报文组成,以标准输入(stdin)的方 式传递给可执行程序。下面例子定义了一个名为dbnet的网络配置参数:

{
  "cniVersion": "0.4.0",	//cni版本号
  "name": "dbnet",	//网络名称,应在一个管理域内唯一
  "type": "bridge",	//CNI插件的可执行文件的名称
  "args": "",	//其他参数
  "ipam": {	//IP地址管理的相关配置
		"type": "host-local",	//IPAM可执行文件名
    "subnet": "10.1.0.0.0/16",
    "gateway": "10.1.0.1"
	},
  "dns": {	//dns服务相关配置
    "nameservers": ["10.1.0.1"],
    "domain": ""	//本地域名
    "search": ""	//按优先级排序的域名查询列表
    "options": ""	//传递给resolver的选项列表
  }
}

CNI插件应能够支持通过环境变量和标准输入传入参数。可执行文 件通过网络配置参数中的type字段标识的文件名在环境变量CNI_PATH 设定的路径下进行查找

IPAM Plugin插件

为了减轻CNI Plugin对IP地址管理的负担,在CNI规范中设置了一个 新的插件专门用于管理容器的IP地址(还包括网关、路由等信息),被称为IPAM Plugin。通常由CNI Plugin在运行时自动调用IPAM Plugin完 成容器IP地址的分配。

如果成功完成了容器IP地址的分配,则IPAM插件应该通过标准输 出(stdout)返回以下JSON报文:

{
  "cniVerison": "0.4.0",
  "ips": [
    {
      "version": "<4-or-6>",
      "address": "<ip-and-prefix-in-CIDR>"
      "gateway": "<ip-address-of-the-gateway>" //optional
    },
    //......
  ],
  "routes": [
    {
      "dst": "ip-and-prefix-in-cidr",
      "gw": "<ip-of-next-hop>"	//optional
    },
    //......
  ],
  "dns": {
    "nameservers": "<list-of-nameservers>"	//optional
    "domain": "<name-of-local-domain>"	//optional
    "search": "<list-of-search-domains>"	//optional
    "options": "<list-of-options>"	//optional
  }
}

多网络插件

在很多情况下,一个容器需要连接多个网络,CNI规范支持为一个 容器运行多个CNI Plugin来实现这个目标。多个网络插件将按照网络配置列表中的顺序执行,并将前一个网络配置的执行结果传递给后面的网络配置。

下面的例子定义了两个网络配置参数,分别作用于两个插件,第1 个为bridge,第2个为tuning。CNI将首先执行第1个bridge插件设置容器的网络,然后执行第2个tuning插件:

{
  "cniVersion": "0.4.0",
  "name": "dbnet",
  "plugin": [
    {
      "type": "bridge",
      "args": {
        "labels": {
          "appVersion": "1.0"
        }
      },
      "ipam": {
        "type": "host-local",
        "subnet": "10.1.0.0/16"
        "gateway": "10.1.0.1"
      },
      "dns": {
        "nameservers": ["10.1.0.1"]
      }
    },
    {
      "type": "tuning",
      "sysctl": {
        "net.core.somaxconn": "500"
      }
    }
  ]
}

在Kubernetes中使用网络插件

CNI插件是可执行文件,会被kubelet调用。Kubernetes目前支持两种网络插件的实现:

  • CNI插件:根据CNI规范实现其接口,以与插件提供者进行对接。
  • kubenet插件:使用bridge和host-local CNI插件实现一个基本的cbr0。

为了在k8s集群中使用网络插件,需要在kubelet服务的启动参数上设置下面两个参数

  • --network-plugin-dir:kubelet启动时扫描网络插件的目录
  • --network-plugin:网络插件名称,对于CNI插件为cni(无须关注--network-plugin-dir路径),kubenet插件为kubenet。

设置为cni时,还需要两个参数--cni-conf-dir(默认/etc/cni/net.d)和--cni-bin-dir(默认/opt/cni/bin),作用看名字就知道。

目前已有多个开源项目支持以CNI网络插件的形式部署到Kubernetes集群中,进行Pod的网络设置和网络策略的设置。

使用CNI插件的简单示例

Docker有自己的CNM标准,那我们可以将CNI与Docker一起使用吗?答案是肯定的,但这不是个完整的解决方案。CNI插件负责连接容器,因此有可能只是用Docker的容器运行时,而不调用Docker的网络端的工作。

CNI的工作是从容器管理系统处获取运行时信息,包括network namespace的路径,容器ID以及network interface name,再从容器网络的配置文件中加载网络配置信息,再将这些信息传递给对应的插件,由插件进行具体的网络配置工作,并将配置的结果再返回到容器管理系统中。

下载CNI二进制文件

# -O 参数来告诉 curl 保存为文件,使用 -L 参数来允许 curl 跟随重定向
curl -O -L https://github.com/containernetworking/cni/releases/download/v0.4.0/cni-amd64-v0.4.0.tgz

tar -xzvf cni-amd64-v0.4.0.tgz

截屏2021-05-27 上午10.30.26

让我们聚焦到网络插件bridge文件,Bridge是CNI官网插件之一,它的工作是将容器依附到网桥接口上。网络配置是通过STDIN流传输到插件中的,其他信息通过环境变量传递到插件。

首先定义一个网桥的网络配置文件

cat > mybridge.conf <<"EOF"
{
    "cniVersion": "0.2.0",
    "name": "mybridge",
    "type": "bridge",
    "bridge": "cni_bridge0",
    "isGateway": true,
    "ipMasq": true,
    "ipam": {
        "type": "host-local",
        "subnet": "10.15.20.0/24",
        "routes": [
            { "dst": "0.0.0.0/0" },
            { "dst": "1.1.1.1/32", "gw":"10.15.20.1"}
        ]
    }
}
EOF

除了可以设置系统级别的环境变量外,我们还可以把环境变量直接传递给命令。

sudo CNI_COMMAND=ADD CNI_CONTAINERID=xingns CNI_NETNS=/var/run/netns/xingns CNI_IFNAME=eth12 CNI_PATH=`pwd` ./bridge < mybridge.conf
  • CNI_COMMAND = ADD:告诉CNI要添加连接
  • CNI_CONTAINER = xingns:CNI要使用xingns网络命名空间
  • CNI_NETNS = /var/run/netns/xingns:网络命名空间路径
  • CNI_IFNAME = eth12:命名空间里使用的网络接口名
  • CNI_PATH = pwd:CNI插件的可执行文件路径

在运行命令前,我们还需要创建插件将要配置的网络命名空间,通常容器运行时会自动创建命名空间,但由于我们是自己手动实验,所以得自己创建。

ip netns add xingns

创建完成后,运行插件

截屏2021-05-27 上午11.00.07

执行完后返回两部分输出

  • 由于IPAM找不到本地存储的保留IP分配信息文件,因此返回错误。如果我们对其他网络命名空间再次运行此命令,则不会出现此错误了,因为该文件在我们首次运行插件时创建了。
  • 其次是返回一个JSON格式的IP配置,在本例中,网桥本身配置为10.15.20.1/24的IP,而网络命名空间接口将会分配到10.15.20.2/24,它还设置了默认网关和我们在网络配置JSON中定义的1.1.1.1/32路由。

截屏2021-05-27 上午11.05.10

我们现在又了一个名为cni_bridge0的网桥接口,接口IP也和我们预期的一致,注意底部有veth设备的一端。我们还启用了ipMasq,(是否为该网络配置出站地址转换)如果我们查看主机的iptables,将看到如下规则...

截屏2021-05-27 上午11.07.45

在看一下网络命名空间:

截屏2021-05-27 上午11.09.30

截屏2021-05-27 上午11.10.56

网络配置和预期的也一致,命名空间有一个名为”eth12”的网络接口,其IP地址为10.15.20.2/24,我们之前定义的路由也在那里。

参考:

《Kubernetes权威指南》

http://www.dasblinkenlichten.com/understanding-cni-container-networking-interface/

posted @ 2021-05-27 12:00  尹瑞星  阅读(997)  评论(0编辑  收藏  举报