docker 的出现是革命性的,改变了我们开发以及部署项目的方式。社区一直致力于让容器技术标准化,这篇文章主要讨论的是其中的一个方面:网络。
网络层面:虚拟机 vs 容器技术
在开始探讨不同的容器网络标准模型之前,先来从网络角度对比一下虚拟机和 docker。
虚拟机是一整套操作系统层级的虚拟化,它还会虚拟出虚拟网卡(virtual network interface cards (NIC)),这些虚拟网卡会和真正的物理机网卡相连接。
docker 实质上就是一个进程,被 container runtime 统一管理,还会共享一个 Linux kernel。所以,容器有更灵活的网络解决方案:
- 和宿主机是共享同一个network interface和 network namespace
- 连接另外的虚拟网络,使用这个虚拟网络的network interface和 network namespace
容器网络设计
早期的容器网络设计把重点放在了如何连接一个宿主机上的容器,让他们可以和外界进行交互。
在"host"模式中,运行在一个宿主机上的容器使用 host 的network namespace,和宿主机一个ip。为了暴露容器,容器会占用宿主机上的一个端口,通过这个端口和外界通信。所以,你需要手动维护端口的分配,不要使不同的容器服务运行在一个端口上。
"bridge"模式,在"host"模式的基础上做了些改进。在该模式里面,容器被分配到了一个虚拟的局域网里,在这个network namespace 获得分配到的 ip 地址,由于 ip 地址是独立的,就解决了"host"模式中不同容器服务不能运行在同一端口的问题。不过还是有一个问题,如果容器想要和外界通信的话,还是需要使用 host 的 ip 地址。这时候需要用到 NAT 将 host-ip:port 转换成 private-ip:port。这一部分的 NAT 规则表示用Linux Iptables 维护的,这会在一定程度上影响性能(虽然不大)。
上述的两种模式都没有解决一个问题:多 host 网络解决方案。
CNI 和 CNM 的对比
- CNM: Docker 公司(docker container runtime 背后的公司)提出。
- CNI:CoreOS公司提出。
Kubernetes 在处理网络上,没有选择自己再独立创造一个,而是选择了其中的 CNI作为了自己的网络插件。(至于为什么不选择 CNM,可以看看这篇官方的解释:Why Kubernetes doesn’t use libnetwork)。不使用 CNM 最关键的一点,是 k8s 考虑到CNM 在一定程度上和 container runtime 联系相对比较紧密,不好解耦。 有了 k8s 这种巨无霸的选择之后,后来的很多项目都在 CNM 和 CNI 之间选择了 CNI。
下面是两种网络模型的详细对比:
- CNM
CNM 的 api 包含了两部分:IPAM 插件和网络插件。IPAM 插件负责创建/删除 address pools、分配网络地址,network 插件负责创建/删除 networks、以及分配或收回容器的 ip 地址。事实上,这两种插件都可以实现所有的 API,你可以选择用 IPAM,也可以选择用 network,或者 BOTH。但是,container runtime 会在不同情况下使用到不同的插件,这带来了复杂性。还有就是,CNM 需要使用分布式存储系统来保存网络配置信息,例如 etcd。
- CNI
CNI 对外暴露了从一个网络里面添加和剔除容器的接口。CNI 使用一个 json 配置文件保存网络配置信息。和 CNM 不一样,CNI 不需要一个额外的分布式存储引擎。
总结
CNI目前已经获得了众多开源项目的青睐,比如 K8S、Memos、Cloud Foundry。同时被Cloud Native Computing Foundation所认可。CNCF 背后有众多的科技大亨,所以可以预见,CNI 将会成为未来容器网络的标准。