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