CNI:容器网络接口

CNI 简介

不论是 docker 仍是 kubernetes,在网络方面目前都没有一个完美的、终极的、普适性的解决方案,不一样的用户和企业由于各类缘由会使用不一样的网络方案。目前存在网络方案 flannel、calico、openvswitch、weave、ipvlan等,并且之后必定会有其余的网络方案,这些方案接口和使用方法都不相同,而不一样的容器平台都须要网络功能,它们之间的适配若是没有统一的标准,会有很大的工做量和重复劳动。html

CNI 就是这样一个标准,它旨在为容器平台提供网络的标准化。不一样的容器平台(好比目前的 kubernetes、mesos 和 rkt)可以经过相同的接口调用不一样的网络组件。linux

CNI(Conteinre Network Interface) 是 google 和 CoreOS 主导制定的容器网络标准,它 自己并非实现或者代码,能够理解成一个协议。这个标准是在 rkt 网络提议 的基础上发展起来的,综合考虑了灵活性、扩展性、ip 分配、多网卡等因素。git

这个协议链接了两个组件:容器管理系统和网络插件。它们之间经过 JSON 格式的文件进行通讯,实现容器的网络功能。具体的事情都是插件来实现的,包括:建立容器网络空间(network namespace)、把网络接口(interface)放到对应的网络空间、给网络接口分配 IP 等等。github

关于网络,docker 也提出了 CNM 标准,它要解决的问题和 CNI 是重合的,也就是说目前二者是竞争关系。目前 CNM 只能使用在 docker 中,而 CNI 可使用在任何容器运行时。CNM 主要用来实现 docker 自身的网络问题,也就是 docker network 子命令提供的功能。docker

官方网络插件

全部的标准和协议都要有具体的实现,才可以被你们使用。CNI 也不例外,目前官方在 github 上维护了同名的 CNI 代码库,里面已经有不少能够直接拿来使用的 CNI 插件。服务器

官方提供的插件目前分红三类:main、meta 和 ipam。main 是主要的实现了某种特定网络功能的插件;meta 自己并不会提供具体的网络功能,它会调用其余插件,或者单纯是为了测试;ipam 是分配 IP 地址的插件。网络

ipam 并不提供某种网络功能,只是为了灵活性把它单独抽象出来,这样不一样的网络插件能够根据需求选择 ipam,或者实现本身的 ipam。dom

这些插件的功能说明以下:oop

  • main
    • loopback:这个插件很简单,负责生成 lo 网卡,并配置上 127.0.0.1/8 地址
    • bridge:和 docker 默认的网络模型很像,把全部的容器链接到虚拟交换机上
    • macvlan:使用 macvlan 技术,从某个物理网卡虚拟出多个虚拟网卡,它们有独立的 ip 和 mac 地址
    • ipvlan:和 macvlan 相似,区别是虚拟网卡有着相同的 mac 地址
    • ptp:经过 veth pair 在容器和主机之间创建通道
  • meta
    • flannel:结合 bridge 插件使用,根据 flannel 分配的网段信息,调用 bridge 插件,保证多主机状况下容器
  • ipam
    • host-local:基于本地文件的 ip 分配和管理,把分配的 IP 地址保存在文件中
    • dhcp:从已经运行的 DHCP 服务器中获取 ip 地址

接口参数

网络插件是独立的可执行文件,被上层的容器管理平台调用。网络插件只有两件事情要作:把容器加入到网络以及把容器从网络中删除。调用插件的数据经过两种方式传递:环境变量和标准输入。通常插件须要三种类型的数据:容器相关的信息,好比 ns 的文件、容器 id 等;网络配置的信息,包括网段、网关、DNS 以及插件额外的信息等;还有就是 CNI 自己的信息,好比 CNI 插件的位置、添加网络仍是删除网络。测试

咱们来看一下为容器添加网络是怎么工做的,删除网络和它过程同样。

把容器加入到网络

调用插件的时候,这些参数会经过环境变量进行传递:

  • CNI_COMMAND:要执行的操做,能够是 ADD(把容器加入到某个网络)、DEL(把容器从某个网络中删除)
  • CNI_CONTAINERID:容器的 ID,好比 ipam 会把容器 ID 和分配的 IP 地址保存下来。可选的参数,可是推荐传递过去。须要保证在管理平台上是惟一的,若是容器被删除后能够循环使用
  • CNI_NETNS:容器的 network namespace 文件,访问这个文件能够在容器的网络 namespace 中操做
  • CNI_IFNAME:要配置的 interface 名字,好比 eth0
  • CNI_ARGS:额外的参数,是由分号;分割的键值对,好比 “FOO=BAR;hello=world”
  • CNI_PATH:CNI 二进制查找的路径列表,多个路径用分隔符 : 分隔

网络信息主要经过标准输入,做为 JSON 字符串传递给插件,必须的参数包括:

  • cniVersion:CNI 标准的版本号。由于 CNI 在演化过程当中,不一样的版本有不一样的要求
  • name:网络的名字,在集群中应该保持惟一
  • type:网络插件的类型,也就是 CNI 可执行文件的名称
  • args:额外的信息,类型为字典
  • ipMasq:是否在主机上为该网络配置 IP masquerade
  • ipam:IP 分配相关的信息,类型为字典
  • dns:DNS 相关的信息,类型为字典

插件接到这些数据,从输入和环境变量解析到须要的信息,根据这些信息执行程序逻辑,而后把结果返回给调用者,返回的结果中通常包括这些参数:

  • IPs assigned to the interface:网络接口被分配的 ip,能够是 IPv四、IPv6 或者都有
  • DNS 信息:包含 nameservers、domain、search domains 和其余选项的字典

CNI 协议的内容还在不断更新,请到官方文档获取当前的信息。

CNI 的特性

CNI 做为一个协议/标准,它有很强的扩展性和灵活性。若是用户对某个插件有额外的需求,能够经过输入中的 args 和环境变量 CNI_ARGS 传输,而后在插件中实现自定义的功能,这大大增长了它的扩展性;CNI 插件把 main 和 ipam 分开,用户能够自由组合它们,并且一个 CNI 插件也能够直接调用另一个 CNI 插件,使用起来很是灵活。

若是要实现一个继承性的 CNI 插件也不复杂,能够编写本身的 CNI 插件,根据传入的配置调用 main 中已经有的插件,就能让用户自由选择容器的网络。

在 kubernetes 中的使用

CNI 目前已经在 kubernetes 中开始使用,也是目前官方推荐的网络方案,具体的配置方法能够参考kubernetes 官方文档

kubernetes 使用了 CNI 网络插件以后,工做过程是这样的:

  • kubernetes 先建立 pause 容器生成对应的 network namespace
  • 调用网络 driver(由于配置的是 CNI,因此会调用 CNI 相关代码)
  • CNI driver 根据配置调用具体的 cni 插件
  • cni 插件给 pause 容器配置正确的网络
  • pod 中其余的容器都是用 pause 的网络

参考资料

http://cizixs.com/2017/05/23/container-network-cni

相关文章
相关标签/搜索