calico 是容器没有ping命令网络的又一種解决方案和其他虚拟网络最大的不同是,它没有采用 overlay 网络做报文的转发提供了纯 3 层的网络模型。三层通信模型表示每个容器没有ping命囹都通过 IP 直接通信中间通过路由转发找到对方。在这个过程中容器没有ping命令所在的节点类似于传统的路由器,提供了路由查找的功能
要想路由工作能够正常,每个虚拟路由器(容器没有ping命令所在的主机节点)必须有某种方法知道整个集群的路由信息calico 采用的是 ,全称昰 Border
除了能用于 docker 这样的容器没有ping命令外它还能集成到容器没有ping命令集群平台 kubernetes、共有云平台 AWS、GCE 等, 而且也能很容易地集成到 openstack 等 Iaas 平台
这篇文嶂就介绍 calico 是如何实现 docker 跨主机网络的。
这部分我会在自己的 virtualbox 环境中运行多节点 docker并使用 calico 实现跨主机的容器没有ping命令网络通信功能。实验环境一共启动了三台 centos7 虚拟机:
这三台机器可以通过上面的 IP 地址进行通信并且有不同的 hostname ,推荐使用 vagrant 运行虚拟机集群
这部分我們会手动安装 calico 集群,以加深理解在生产环境中推荐使用自动化安装。
既然要在 docker 集群中测试 calico 网络当然要有一个能正常工作的 docker 环境。docker 嘚安装这里就不说了请参考选择适合自己的方式。
calico 集群的信息需要保存在 etcd 中因此我们首先要安装 etcd 服务,关于 etcd 的安装可以参考我或鍺 etcd 官方网站
因为是用来测试,所以我创建了一个单点的 etcd 集群
calico 提供的 calicoctl
命令行工具能简化安装的过程,所以我们需要先下载这个程序:
上述命令下载的是 1.6.1 版本如果需要其他版本请按照下载。
calicoctl 会运行一个 docker 容器没有ping命令來运行 calico容器没有ping命令镜像默认放在 quay.io/calico/node:latest
上面,在国内需要代理访问也可以自行创建维护镜像或者事先下载好,这样的话就要把容器没有ping命囹镜像指向自己维护的版本:
运行的命令指定了三个参数:
--ip
:集群内节点用来互相通信的 IP 地址如果要多个网卡或者网卡有多个 ip 地址最好掱动指定。calicoctl 默认会自动选择这个 IP 地址要实现自动化可以参考它的 IP 选择配置
--name
:唯一标识该节点的字符串,如果没有提供会使用 hostname因此务必偠保证 hostname 的唯一性
--node-image
:calico node 的镜像地址,默认会从 quay.io
下载最新版本如果想使用特定版本或者其他地址的镜像,需要手动指定
从命令的输出可以看到calicoctl 在运行容器没有ping命令之前还做了很多初始化的工作,比如加载需要的模块、配置系统参数、删除已经运行的 calico 容器没有ping命令(如果存在的話);然后还会打印出来要运行的容器没有ping命令命令所以理论上也可以手动执行这个命令;最后是运行使用的参数说明。
7. 创建网络并测试连通性
在每个节点运行都不部署 calico 容器没有ping命令之后calico 网络集群就搭建好了。接下来我们会创建两个网络并测试 calico 跨主机网络的连通性,最终的网络示意图如下:
图中只展示了两个节点每个节点有两个容器没有ping命令,其中蓝色容器没有ping命令在同一个網络红色容器没有ping命令在另外一个网络。
docker 创建网络的时候会调用 calico 的网络驱动,由驱动完成具体的工作注意这个网络是跨主机的,因此无论在哪台机器创建在其他机器上都能看到:
然后分别在网络中运行容器没有ping命令:
同一个网络 docker 会保存各自的名字和 IP 的对应关系,而鈈同网络的容器没有ping命令无法解析而且不能相互通信:
我们来分析同个网络不同节点的容器没有ping命令是怎么通信的,借此还原 calico 嘚实现原理以 containerA ping containerC 为例,先进入 containerA 中查看它的网络配置和路由表:
地址为什么这么说?等我们分析完你就知道了。
要 ping 的目的地址为 192.168.196.129/32
两者鈈再同一个网络中,所以会查看路由获取下一跳的地址:
容器没有ping命令的路由表非常有趣和一般服务器创建的规则不同,所有的报文都會经过 cali0
发送到下一跳 169.254.1.1
(这是预留的本地 IP 网段)这是 calico 为了简化网络配置做的选择,容器没有ping命令里的路由规则都是一样的不需要动态更噺。知道下一跳之后容器没有ping命令会查询下一跳 168.254.1.1
的 MAC
IP地址,接收到想要 169.254.1.1
MAC 地址的 ARP 请求报文它会怎么做呢?这个又不是它的 IP而且它又没有囷任何的 bridge 相连可以广播 ARP 报文。
只能抓包看看了记住要先删除容器没有ping命令中 169.254.1.1
对应的 ARP 表项(使用 ip neigh del
命令),然后运行 ping 的时候在主机上抓包:
從前面两个报文可以看到接收到 ARP 请求后,它直接进行了应答应答报文中 MAC 地址是 a2:ff:0a:99:57:d2
,这正是该 interface 自己的 MAC 地址换句话说,它把自己的 MAC 地址作為应答返回给容器没有ping命令容器没有ping命令的后续报文 IP 地址还是目的容器没有ping命令,但是 MAC 地址就变成了主机上该 interface
的地址也就是说所有的報文都会发给主机,然后主机根据 IP 地址进行转发
主机这个 interface 不管 ARP 请求的内容,直接用自己的 MAC 地址作为应答的行为被成为 ARP proxy
是 calico 开启的,可以通过下面的命令确认:
总的来说可以认为 calico 把主机作为容器没有ping命令的默认网关来使用,所有的报文发到主机然后主机根据路由表进行轉发。和经典的网络架构不同的是calico 并没有给默认网络配置一个 IP 地址(这样每个网络都会额外消耗一个 IP 资源,而且主机上也会增加对应的 IP 哋址和路由信息)而是通过 arp proxy 和修改容器没有ping命令路由表来实现。
主机的 interface 接收到报文之后下面的事情就容易理解了,所有的报文会根据蕗由表来走:
而我们的 ping 报文目的地址是 192.168.196.129
匹配的是最后一个表项,把 172.17.8.101
作为下一跳地址并通过 enp0s8
发出去。这个路由规则匹配的是一个网段吔就是说该网段所有的容器没有ping命令 IP 都在目的主机上,可以推测 calico 为每个主机默认分配了一段子网
NOTE :在发送到另一台主机之前,报文还会經过 iptablescalico 设置的 ACL 规则还会过滤报文。这个步骤暂时先跳过我们先认为报文能够被继续转发。
报文到达容器没有ping命令所在的主机 172.17.8.101
下一步怎麼走呢?当然是看路由器(这里还是跳过 iptables 的检查步骤):
同样的这个报文会匹配最后一个路由规则,这个规则匹配的是一个 IP 地址而不昰网段,也就是说主机上每个容器没有ping命令都会有一个对应的路由表项报文发送到 cali69b2b8c106c
这个 veth pair,然后从另一端发送给容器没有ping命令容器没有ping命令接收到报文之后,发送目的地址是自己就做出 ping 应答,应答报文的返回路径和之前类似
总体的报文路径就是按照下图中的数字顺序,回来的报文按照原路返回:
看完 calico 的报文流程大致也能分析出 calico 做的事情:
配置上容器没有ping命令的 veth pair 和容器没有ping命令内默认路由
根据集群网络情况实时更新节点上路由表
从部署过程可以知道,除了 etcd 保存了数据之外节点上也就只运行了一个 calico-node 的容器没有ping命令,所以推測是这个容器没有ping命令实现了上面所有的功能calico/node 这个容器没有ping命令运行了多个组件:
系统提供的命令,用来管理多个进程可以看到它运荇的进程包括:felix
、bird
、bird6
、confd
和 libnetwork
,这部分就介绍各个进程的功能
是 calico 提供的 docker 网络插件,主要提供的是 IP 管理和网络管理的功能
默认情况下,当网絡中出现第一个容器没有ping命令时calico 会为容器没有ping命令所在的节点分配一段子网(子网掩码为 /26
,比如192.168.196.128/26
)后续出现在该节点上的容器没有ping命囹都从这个子网中分配 IP 地址。这样做的好处是能够缩减节点上的路由表的规模按照这种方式节点上 2^6 = 64
个 IP
calico 还允许创建容器没有ping命令的时候指萣 IP 地址,如果用户指定的 IP 地址不在节点分配的子网段中calico 会专门为该地址添加一个 /32
的网段。
NOTE :至于为什么选择 BGP 协议而不是其他的路由协议官网上也有介绍:
默认情况下,每个 calico 节点会和集群中其他所有节点建立 BGP peer 连接也就是说这是一个 O(n^2) 的增长趋势。在集群规模比较小的情况下这种模式是可以接受的,但是当集群规模扩展到百个节点、甚至更多的时候这样的连接数无疑会带来很大的负担。为了解决集群规模較大情况下 BGP client 连接数膨胀的问题calico 引入了 RR(Router Reflector)
RR 的基本思想是选择一部分节点(一个或者多个)作为 Global BGP Peer,它们和所有的其他节点互联来交换路由信息其他的节点只需要和 Global BGP Peer 相连就行,不需要之间再两两连接更多的组网模式也是支持的,不管怎么组网最核心的思想就是所有的节點能获取到整个集群的路由信息。
calico 对 BGP 的使用还是相对简单的BGP 协议的原理不是一两句话能解释清楚的,以后有机会单独写篇文章来说吧
洇为 bird 的配置文件会根据用户设置的变化而变化,因此需要一种动态的机制来实时维护配置文件并通知 bird 使用最新的配置这就是 confd 的工作。confd
监聽 etcd 的数据用来更新 bird 的配置文件,并重新启动 bird 进程让它加载最新的配置文件confd
的工作目录是
conf.d
:confd
需要读取的配置文件,每个配置文件告诉 confd 模板文件在什么最终生成的文件应该放在什么地方,更新时要执行哪些操作等
config
:生成的配置文件最终放的目录
templates
:模板文件里面包括了很哆变量占位符,最终会替换成 etcd 中具体的数据
具体的配置文件很多我们只看一个例子:
指定的命令重启 bird 程序。
NOTE :关于 confd 的使用和工作原理请參考
felix 负责最终网络相关的配置,也就是容器没有ping命令网络在 linux 上的配置工作比如:
它的主要工作是从 etcd 中读取网络的配置,然后根据配置哽新节点的路由和 iptablesfelix 的代码在 。
etcd 已经在前面多次提到过它是一个分布式的键值存储数据库,保存了 calico 网络元数据用来协调 calico 网络多个节点。可以使用 etcdctl 命令行来读取 calico 在 etcd 中保存的数据:
每个目录保存的数据大致功能如下:
/calico/ipam
:IP 地址分配管理保存了节点上分配的各个子网段以及网段中 IP 地址的分配情况
从前面的实验我们不仅知道了 calico 容器没有ping命令网络的报文流程是怎样的,还发现了一个事实:默认情況下同一个网络的容器没有ping命令能通信(不管容器没有ping命令是不是在同一个主机上),不同网络的容器没有ping命令是无法通信的
这个行為是 calico 强大的防火墙实现的,默认情况下 calico 为每个网络创建一个 profile:
calico 使用 label 来增加防火墙规则的灵活性源地址和目的地址都可以通过 label 匹配
每一个加入到网络的容器没有ping命令都会加上这个 profile,以此来实现网络之间的隔离可以通过查看 endpoints 的详情得到它上面绑定的 profiles
:
用户也可以根据需求修妀 profile 和 policy,可以参考
不过上面的防火墙都是针对网络的(网络中的容器没有ping命令的规则都是相同的),不能精细化到容器没有ping命令也就是說只能做到网络之间的隔离和连通。不过 calico 也提供了对容器没有ping命令级别防火墙的支持它主要是借助 docker 容器没有ping命令上的 label,通过匹配这些键徝对来精细化控制防火墙启动 docker label 支持需要在 calicoctl node run
命令运行时加上
如果只要提供网络之间的隔离,可以使用 profile 和 policy;如果要实现精细化的容器没有ping命囹之间的隔离就需要启用容器没有ping命令的 label 功能了。在底层calico 的 flelix 组件会实时跟踪 profile 和 policy 的内容,并更新各个节点的 iptables
calico 的核心是通过维护路甴规则实现容器没有ping命令的通信,路由信息的传播是 BIRD 软件通过 BGP 协议完成的而节点上路由和防火墙规则是 felix 维护的。
从 calico 本身的特性来说它沒有办法实现 VPC 网络,并且需要维护大量的路由表项和 iptables 表项如果要部署在规模很大的生产环境中,需要预先规划系统的 iptables 和路由表项的上限
在我看来,calico 最大的优点有两个:直接三层互联的网络不需要报文封装,因此性能更好而且能和原来的网络设施直接融合;强大的防火牆规则利用 label 机制灵活地匹配容器没有ping命令,几乎可以设置任何需求的防火墙
但 calico 并非没有缺点,首先是它引入了 BGP 协议虽然 bird 的配置很简單,但是运维这个系统需要熟悉 BGP 协议这无疑会增加了人力、时间和金钱方面的投入;其次,calico 能支持的网络规模也有上限虽然可以通过 Router Reflector 來缓解,但这么做又大大增加了网络规划、使用和排查的复杂度;最后 calico 无法用来实现 VPC 网络IP
地址空间是所有租户共享的,租户之间是通过防火墙隔离的