首先,在为某一个stack编写Rancher catalog的时候,假设在docker-compose里指定了ports A:B,那么,cattle在调度的时将首先过滤掉所有已经占用了port A的主机。这也就意味着,假设用户只有4台host作为Rancher agent,但是需要运行5个在外网中占用80端口的服务,这显然是不行的。
另一方面,客户端通过主机的IP地址来访问Stack提供的服务,一旦由于系统异常,或者微服务内部程序崩溃导致Service被重新调度到其他主机,势必导致访问Stack服务IP地址的更改。
为了让客户端不被影响,常见的解决方案是通过使用Rancher提供的external-DNS;如果业务在公网,有像AWS的Router-53之类的解决方案;若是自建数据中心也可以通过使用支持RFC-2136的硬件路由器等来实现DNS条目的及时刷新。
从当前Github代码看External-DNS已经支持下面的Provider了:
但是,通过刷新DNS的方案要么价格昂贵,要么需要自己维护一个Rancher外部的DNS Server,都存在一定缺陷。
那么,有没有一种方式,既能够解决端口冲突,又能够解决stack对外服务IP变更的问题呢?答案就是今天我们的主角:Floating IP!
上一次Alan为大家分享的「关于在Rancher中使用keepalived」中就有提到VIP的概念,只是keepalived需要在Active+Passive的主机之间周期性发送心跳报文,然后基于优先级来判断将VIP绑定到哪一个host,它解决了服务迁移后访问的目标IP地址变化的问题。
而Floating IP却是从另一个角度来解决问题。熟悉OpenStack的人对Floating IP应该都不会陌生,所谓Floating IP就是为某台虚拟机绑定的一个外网IP地址。绑定之后,无论该虚拟机在OpenStack内被迁移到哪一台hypervisor,外网都可以通过Floating IP来访问到该虚拟机。我们来看Rancher中是怎么引入Floating IP的。首先上一张总体模块框图:
从图上我们可以看到,整个Floating IP的实现,只需要两个模块,或者说两个微服务:Metadata-confd和Network-plugin。至于IPAM为什么不需要重新实现,我们后面会讲到。接下来我们讲一下各个模块功能和作用。
Metadata-confd:
这是一个使用了Rancher managed网络的service,通过managed网络,它按照一定的周期从Agent-Instance上polling metadata信息。采用分布式架构,每一个Metadata-confd只关注属于自己host上的容器。
但是,也并非所有的container都会触发后续行为,只有该host上带有“io.rancher.container.floating.ip” 标签的容器有被新增、删除或更新IP的时候,Metadata-confd才会通过docker client向docker daemon发送操作命令。
需要注意的是,这里的network均是指Floating IP所在的网络。
对于Metadata-confd的实现方式,除了通过通过周期性polling之外,也可以采用支持Rancher backend的confd来生成map文件(包含ip <—> floating ip映射),然后使用confd里面的reload-cmd参数,指定脚本去解析和做后续处理。当然,这种方法存在不少坑,大家可以下来自己研究。
FIP Network Plugin:
FIP Network Plugin是按照CNM的定义来实现的一个libnetwork的remote driver。该service使用host网络,遵照docker deamon发送过来的请求指令,实现网络相关的操作。Network plugin所做的事情,我们可以通过下图来理解:
原生的docker bridge driver创建了docker0网桥,然后将所有的container通过link pair挂到桥上。在network root namespace内,通过NAPT规则将对Host上特定port的访问DNAT到container的内部port。而在Floating IP网络中,FIP network plugin创建了一个新的bridge,然后将label含Floating IP的container连接到该bridge上。
由于使用了与docker0 共享的“default” driver的IPAM,FIP network bridge的IP地址段不会与docker0所在的网段重叠,从而保障container在连接了两个网段后,路由不会冲突。
按照CNM的定义,Docker Network Plugin主要需要实现以下方法:
而FIP Network Plugin除了要实现基本的功能外,还需要为Floating IP添加以下功能:
这些修改之后,container其实还无法通过FIP回包,为什么呢?有兴趣的可以研究一下挂载了两个network后container的路由表条目。至于还需要如何配置,大家可以下来自己实验。
现在我们来做一个实验,看看Floating IP的报文转发是如何工作的。
1.首先在Rancher catalog中选择“Wise2C Floating IP”,从详细页面看,Floating IP这个stack不需要添加任何配置参数:
启动过程中,可以看到:该stack在一个host ”vm-153-5”上启动了两个containers(在每个host上均会被调度):
2.服务启动成功后,我们到host里面可以看到已经初始化好的network(其网段是172.18.0.0/16):
3.然后我们再通过Rancher创建一个带有标签为“io.rancher.container.floating.ip=192.168.99.200”的container。这里的IP地址就是我们希望为该container绑定的Floating IP:
4.当container启动成功后,metadata-confd会检测到该label,然后向docker daemon发送请求将该container加入到wise2c network。我们可以进入container查看interface详情来了解到:
上图中接口eth1@if9就是接入到wise2c network的接口,其IP地址属于172.18.0.0/16网段。
5.再来看host上的IP地址和NAT规则
其中enp0s8就是我们host的default gw对应的出口网卡,可以看到floating ip:192.168.99.200已经被添加到上面了。
主机的NAT表:
上图就是我们的network-plugin为Floating IP创建的基于destination IP的DNAT规则,其中br-d62debee292b是FIP network plugin为wise2c network创建的bridge。
下面的DNAT规则是将所有不来自br-d62debee292b,且目标IP地址为192.168.99.200的报文DNAT到172.18.0.2容器。
6.接下来就是验证网络,在客户机(192.168.99.1)上ping Floating IP:
到wise2c network的bridge上抓包:
可以看到,DNAT规则已经生效。
我们再来整理一下使用场景。
初始化:
1.首先FIP catalog指定在所有运行了Rancher agent的主机上均启动Floating IP service;
2.在服务启动后,Metadata-confd向docker daemon请求创建FIP network,然后开始polling Rancher metadata信息;
一旦发现本机上存在带FIP标签的容器,Metadata-confd就向docker daemon请求将容器接入FIP network;
3.接下来network-plugin从docker daemon收到创建FIP network和将容器接入FIP network的请求后,执行操作。
在这个过程中,Floating IP被添加到host上默认网关对应的网卡,并更新NAT规则,保障外网访问Floating IP的流量被DNAT到容器。
容器迁移:
1.当容器从Host-A迁移到Host-B,Host-A上的docker-daemon会发起将container移出FIP network。network-plugin只需要按照leave endpoint from network的流程,逐一删除NAT规则,并移除host上的Floating IP address。
2.在Host-B上执行的操作除少了初始化时候的创建FIP network外,其他操作流程同“初始化”。
Q:
其他编排引擎怎么支持?
A:
除了通过CNM来实现外,还可以通过外部实现,只需要能够为主机配置IP地址和NAT规则就能够支持的了。只是如果通过CNM实现,对资源的释放可以由docker daemon来触发,不需要完全自主控制。
Q:
Metadata-confd poll metadata service的间隔是不是需要很短?否则是不是可能存在旧的host IP还没有移除,新的host已经在尝试添加IP的情况,导致IP冲突?
A:
如果采用poll的方式来实现肯定是有一定的时间间隔的缺陷的,如果无法接受,就可以采用支持Rancher backend的confd来实现。
Q:
今天讲的FIP在k8s环境下也可以用吗?
A:
其实理论上是可用的,但我们还没测试。因为方案只用了docker label, Rancher metadata service 和 docker libnetwork,这些在Rancher 的k8s 环境都是有的。
Q:
managed网络还是基于ipsec的隧道网络吗?性能上怎么样呢?
A:
是的,基于ipsec,在很多生产环境的实际使用中,没什么性能问题,性能就是和普通VPN 一样。在Docker本身提供的几种网络基础上提供了一种叫做Managed的网络特性,官方说法如下:The Rancher network uses IPsec tunnelling and encryption for security。
简单理解就是在容器之间构建了一条私有网络,只有容器与容器之间可以访问。
Rancher会为每个容器分配一个 10.42. . 的私有网络地址,这个地址只在容器之间是可达的,对外不可见,有点类似一种纯软件上的VPC方式。从路由上可以看出,对于10.42网段的路由是通过docker0接口,docker0的特殊的配置了IP也包含了10.42网段。
原文来源: Rancher Labs