Rancher技术特性全解及容器网络解决方案

社区广播:运维派(Yunweipai.com)是国内最早成立的IT运维社区,欢迎大家投稿,让运维人不再孤寂的成长!

作者简介:

刘力
Rancher 资深架构师
主负责容器云平台产品架构及设计,10年以上的企业级 IT 解决方案和服务经验,在多个行业如运营商、政府、金融、大中型企业网络及应用交付解决方案上有着丰富经验。管理过国内对容器技术在实际生产环境中的大型项目。

1、前言

本文目的在于和大家一起了解一下容器网络以及如何在容器云管理平台上实现扁平网络。

内容包括:

  1. 什么是 CNI 接口;
  2. 容器网络基于 CNI 的实现—-IPsec 及 VXLAN;
  3. 容器网络基于 CNI 的实现—-直接路由访问容器;

2、容器时代

2.1 容器时代背景简介

自2013年 Docker 出现以来,全球已经有越来越多的企业用户在开发测试或生产环境中采用 Docker 容器技术。

然而,如下图所示,在企业内部将容器技术真正用于生产时,很多用户发现面临的最大的问题仍然是在 VM 虚拟化时代曾经面临并已经解决的问题。

例如,如何将容器中的数据进行持久化存储,如何选择合适的网络方案,跨云和跨数据中心的部署和管理如何解决?

业内有款开源的容器云管理平台—— Rancher,短短1年里,已经供全球 4000 多个用户部署及试用、超过 1800 万次的下载量(注:统计来自 DockerHub 官方镜像下载数量)。

使用场景覆盖金融、互联网、运营商、教育等各个领域。在本次交流中我们将基于该开源容器云平台进行探讨,同时针对容器面临的网络问题的解决方案进行分享。

2.2 容器的网络类型

2.2.1 原始容器网络

最早的容器网络模型,利用的是容器宿主机内部的网络,同时其 IPAM 管理也是基于容器宿主机的,所有容器宿主机上的容器都会连接到容器宿主机内的 Linux Bridge,叫 Docker0,它默认会分配 172.17 网段中的一个 IP,因为有 Docker0 这个网桥,所以在一个容器宿主机上的容器可以实现互联互通。

但是因为 IPAM 范围是基于单容器宿主机的,所以在其他容器宿主机上,也会出现完全相同的IP地址。这样一来这两个地址肯定没办法直接通信。

为了解决这个问题一般采用的就是 NAT 的方法。比如说我有一个应用,它有 Web 和 Mysql,分别在不同的容器宿主机上,Web 需要去访问 Mysql,我们会把这个 Mysql 的3306端口映射到容器宿主机上的3306这个端口,然后这个服务实际上是去访问容器宿主机 IP 10.10.10.3 的3306端口 。同时如果想要把服务暴露出去需要做端口映射。

下面就是主要的三种容器原始网络模式,这些模式在企业环境基本无法采用:

  • Bridge 模式
  • HOST 模式
  • Container 模式
2.2.2 容器网络的进化

随着用户对容器的网络要求的不断提高,容器界涌现了很多新的网络规范,其目的也都是帮助大家解决在使用容器时的网络问题,现在最主流就是下面这两个:

  • Container Networking Model (CNM)
  • Container Networking Interface (CNI)

需要注意的是 CNM 和 CNI 并不是网络实现而是网络规范和网络体系,CNM 和 CNI 关心的是网络管理的问题。

这两个模型全都是插件化的,用户可以以插件的形式去插入具体的网络实现。其中 CNM 由 Docker 公司自己提出,而 CNI 则是 Google  的 Kubernetes 主导。

总体上说 CNM 比较不灵活,也不难么开放,但是确是 Docker 的原生网络实现。而 CNI 则更具通用性,而且也十分的灵活。

目前主流的插件如:Calico、Weave、Mesos 基本上是对 CNI 和 CNM 两种规范都提供支持。

2.3 CNI和CNM简介

2.3.1 CNM接口:

CNM 是一个被 Docker 提出的规范。现在已经被 Cisco Contiv, Kuryr, Open Virtual Networking (OVN), Project Calico, VMware 和 Weave 这些公司和项目所采纳。

其中 Libnetwork 是 CNM 的原生实现。它为 Docker daemon和网络驱动程序之间提供了接口。网络控制器负责将驱动和一个网络进行对接。

每个驱动程序负责管理它所拥有的网络以及为该网络提供的各种服务,例如 IPAM 等等。由多个驱动支撑的多个网络可以同时并存。

网络驱动可以按提供方被划分为原生驱动(libnetwork内置的或Docker支持的)或者远程驱动 (第三方插件)。

原生驱动包括 none, bridge, overlay 以及 MACvlan。驱动也可以被按照适用范围被划分为本地(单主机)的和全局的 (多主机)。

Network Sandbox:容器内部的网络栈,包含interface、路由表以及DNS等配置,可以看做基于容器网络配置的一个隔离环境(其概念类似“network namespace”)

Endpoint:网络接口,一端在网络容器内,另一端在网络内。一个 Endpoints 可以加入一个网络。一个容器可以有多个 endpoints。

Network:一个 endpoints 的集合。该集合内的所有endpoints可以互联互通。(其概念类似:Linux bridge、VLAN)

最后,CNM 还支持标签(labels)。Lable 是以 key-value 对定义的元数据。用户可以通过定义 Label 这样的元数据来自定义 libnetwork 和驱动的行为。

2.3.2 CNI网络接口

CNI 是由 Google 提出的一个容器网络规范。已采纳改规范的包括 Apache Mesos, Cloud Foundry, Kubernetes, Kurma 和 rkt。另外 Contiv Networking, Project Calico 和 Weave 这些项目也为 CNI 提供插件。

CNI 的规范比较小巧。它规定了一个容器runtime和网络插件之间简单的契约。这个契约通过JSON的语法定义了CNI插件所需要提供的输入和输出。

一个容器可以被加入到被不同插件所驱动的多个网络之中。一个网络有自己对应的插件和唯一的名称。

CNI 插件需要提供两个命令:一个用来将网络接口加入到指定网络,另一个用来将其移除。这两个接口分别在容器被创建和销毁的时候被调用。

在使用 CNI 接口时容器 runtime 首先需要分配一个网络命名空间以及一个容器ID。然后连同一些 CNI 配置参数传给网络驱动。接着网络驱动会将该容器连接到网络并将分配的 IP 地址以 JSON 的格式返回给容器 runtime。

Mesos 是最新的加入 CNI 支持的项目。Cloud Foundry 的支持也正在开发中。当前的 Mesos 网络使用宿主机模式,也就是说容器共享了宿主机的 IP 地址。

Mesos 正在尝试为每个容器提供一个自己的 IP 地址。这样做的目的是使得IT人员可以自行选择适合自己的组网方式。

目前,CNI 的功能涵盖了 IPAM, L2 和 L3。端口映射(L4)则由容器 runtime 自己负责。CNI 也没有规定端口映射的规则。这样比较简化的设计对于 Mesos 来讲有些问题。端口映射是其中之一。

另外一个问题是:当 CNI 的配置被改变时,容器的行为在规范中是没有定义的。为此,Mesos在 CNI agent 重启的时候,会使用该容器与 CNI 关联的配置。

3、Rancher的Overlay网络实现

说完了网络规范,我们下面谈一下 Rancher 是怎么利用 CNI 规范完成容器环境下的 overlay 网络实现。众所周知,网络是容器云平台中很重要的一环,对于不同的规模、不同的安全要求,会有不同的选型。

Rancher 的默认网络目前完成了代码重构,完全支持 CNI 标准,同时也会支持其他第三方 CNI 插件,结合Rancher独有的 Environment Template 功能,用户可以在一个大集群中的每个隔离环境内,创建不同的网络模式,以满足各种业务场景需求,这种管理的灵活性是其他平台没有的。

至于 Rancher 为什么会选择 CNI 标准,最开始 Rancher 也是基于 CNM 进行了开发,但随着开发的深入,我们不得不转向了 CNI,个中原因在本次交流中我们就不做详细说(tu)明(cao)了,

大家如果有兴趣可以参阅:http://blog.kubernetes.io/2016/01/why-Kubernetes-doesnt-use-libnetwork.html

在之前的 Rancher 版本上,用户时常抱怨 Rancher 的网络只有 IPsec,没有其他选择。而容器社区的发展是十分迅猛的,各种容器网络插件风起云涌。

在 Rancher v1.2 之后 Rancher  全面支持了 CNI 标准,除在容器网络中实现 IPsec 之外又实现了呼声比较高的 VXLAN 网络,同时增加了 CNI 插件管理机制,让用户可以 hacking 接入其他第三方 CNI 插件。随后将和大家一起解读一下 Rancher 网络的实现。

3.1 Rancher-net CNI的IPsec实现

以最简单最快速方式部署 Rancher 并添加 Host,以默认的 IPsec 网络部署一个简单的应用后,进入应用容器内部看一看网络情况,对比一下之前的 Rancher 版本:

最直观的感受便是,网卡名从 eth0 到 eth0@if8 有了变化,原先网卡多 IP 的实现也去掉了,变成了单纯的 IPsec 网络 IP。

这其实就引来了我们要探讨的内容,虽然网络实现还是 IPsec,但是 rancher-net 组件实际上是已经基于 CNI 标准了。最直接的证明就是看一下,rancher-net 镜像的 Dockerfile:

熟悉 CNI 规范的伙伴都知道 /opt/cni/bin 目录是CNI的插件目录,bridge 和 loopback 也是 CNI 的默认插件,这里的 rancher-bridge 实际上和 CNI 原生的 bridge 没有太大差别,只是在幂等性健壮性上做了增强。

而在 IPAM 也就是 IP 地址管理上,Rancher 实现了一个自己的 rancher-cni-ipam,它的实现非常简单,就是通过访问 rancher-metadata 来获取系统给容器分配的 IP。

Rancher 实际上Fork了CNI的代码并做了这些修改,https://github.com/rancher/cni。 这样看来实际上,rancher-net 的 IPsec 和 Vxlan 网络其实就是基于 CNI 的 bridge 基础上实现的。

在解释 rancher-net 怎么和 CNI 融合之前,我们需要了解一下 CNI bridge 模式是怎么工作的。

举个例子,假设有两个容器 nginx 和 mysql,每个容器都有自己的 eth0,由于每个容器都是在各自的 namespace 里面。

所以互相之间是无法通信的,这就需要在外部构建一个 bridge 来做二层转发,容器内的 eth0 和外部连接在容器上的虚拟网卡构建成对的veth设备,这样容器之间就可以通信了。

其实无论是 docker 的 bridge 还是 cni 的 bridge,这部分工作原理是差不多的,如图所示:

那么我们都知道 CNI 网络在创建时需要有一个配置,这个配置用来定义CNI网络模式,读取哪个 CNI 插件。

在这个场景下也就是 cni bridge 的信息,这个信息 rancher 是通过 rancher-compose 传入 metadata 来控制的。

查看 ipsec 服务的 rancher-compose.yml 可以看到,type 使用 rancher-bridge,ipam 使用 rancher-cni-ipam,bridge 网桥则复用了 docker0,有了这个配置我们甚至可以随意定义 ipsec 网络的 CIDR,如下图所示:

ipsec 服务实际上有两个容器:一个是 ipsec 主容器,内部包含rancher-net服务和ipsec需要的charon服务;

另一个 sidekick 容器是 cni-driver,它来控制 cni bridge 的构建。

两端主机通过 IPsec 隧道网络通信时,数据包到达物理网卡时,需要通过 Host 内的 Iptables 规则转发到 ipsec 容器内,这个 Iptables 规则管理则是由 network-manager 组件来完成的,https://github.com/rancher/plugin-manager。其原理如下图所示(以 IPsec 为例):

整体上看 cni ipsec 的实现比之前的 ipsec 精进了不少,而且也做了大量的解耦工作,不单纯是走向社区的标准,之前大量的 Iptables 规则也有了很大的减少,性能上其实也有了很大提升。

3.2 Rancher-net vxlan的实现

那么 rancher-net 的另外一个 backend vxlan 又是如何实现的呢?我们需要创建一套VXLAN网络环境来一探究竟,默认的 Cattle 引擎网络是 IPsec,如果修改成 VXLAN 有很多种方式,可以参考我下面使用的方式。

首先,创建一个新的 Environment Template,把 Rancher IPsec 禁用,同时开启 Rancher VXLAN,如下图所示:

然后,我们创建一个新的 ENV,并使用刚才创建的模版 Cattle-VXLAN,创建完成后,添加 Host 即可使用。如下图所示:

以分析 IPsec 网络实现方式来分析 VXLAN,基本上会发现其原理大致相同。同样是基于 CNI bridge,使用 rancher 提供的 rancher-cni-bridge 、rancher-cni-ipam,网络配置信息以 metadata 方式注入。

区别就在于 rancher-net 容器内部,rancher-net 激活的是 vxlan driver,它会生成一个 vtep1042 设备,并开启 udp 4789 端口,这个设备基于 udp 4789 构建 vxlan overlay 的两端通信,对于本机的容器通过 eth0 走 bridge 通信,对于其他Host的容器,则是通过路由规则转发到 vtep1042 设备上,再通过 overlay 到对端主机,由对端主机的 bridge 转发到相应的容器上。整个过程如图所示:

4、Rancher的扁平网络实现

为什么需要扁平网络,因为容器目前主流的提供的都是 Overlay 网络,这个模式的好处是,灵活、容易部署、可以屏蔽网络对应用部署的阻碍。

但是对很多用户而言,这样也带来了额外的网络开销,网络管理不可以控制,以及无法与现有 SDN 网络进行对接。

在实现扁平网络后,容器可以直接分配业务 IP,这样访问容器上的应用就类似访问 VM 里的应用一样,可以直接通过路由直达,不需要进行 NAT 映射。但扁平网络带来的问题是,会消耗大量的业务段 IP 地址,同时网络广播也会增多。

4.1 扁平网络实现

在 Rancher 环境中实现扁平网络需要使用自定义的 bridge,同时这个 bridge 与 docker0 并没有直接关系。

我们可以给容器添加新的网桥 mybridge,并把容器通过 veth 对连接到网桥上 mybridge 上,如果要实现容器访问宿主机的 VM 上的服务可以将虚拟机配置的IP也配置到网桥上。

进行如上配置后,容器就可以实现 IP 之间路由直接访问。此图中的 vboxnet bridge 可以看做是用户环境的上联交互机。

在VM和物理机的混合场景,所采用的方法也很类型,这边就不再多做解释了。

Rancher CNI 网络环境实现扁平网络的工作流如下:

在实现容器扁平网络的基本配置后,就需要考虑和 Rancher 的集成,Rancher 的 Network-plugin 的启动依赖于 Metadata,而 Metadata 和 DNS server 均使用 docker0 的 bridge 网络(IP为169.254.169.250)。

即用户配置的扁平网络要能够访问到 docker0 网络,否则 Rancher 提供的服务发现与注册以及其它为业务层提供的服务将不可用。目前主要方法如下图所示:

  1. container-1 内部有到达 169.254.169.250 的一条主机路由,即要访问 169.254.169.250 需要先访问 10.43.0.2;
  2. 通过 veth-cni 与 veth-doc 的链接,CNI bridge 下的 container-1 可以将 ARP 请求发送到 docker0 的 10.43.0.2 地址上。由于 10.1.0.2 的 ARP response 报文是被 veth-cni 放行的,于是 container-1 能够收到来自 10.43.0.2 的 ARP response 报文。
  3. 然后 container-1 开始发送到 169.254.169.250 的 IP 请求,报文首先被送到 docker0 的 veth-doc 上,docker0 查询路由表,将报文转到 DNS/metadata 对应的容器。然后IP报文原路返回,被 docker0 路由到 veth1 上往 br0 发送,由于来自 169.254.169.250 的 IP 报文都是被放行的,因此 container-1 最终能够收到 IP。
  4. 由于属于该 network 的所有的宿主机的 docker0 上都需要绑定 IP 地址 10.43.0.2;因此,该IP地址必须被预留,即,在 catalog 中填写 CNI 的 netconf 配置时,不能将其放入IP地址池。
  5. 同时,为了保障该地址对应的 ARP 请求报文不被发送出主机,从而收到其他主机上对应接口的 ARP 响应报文,需要对所有请求 10.1.0.2 地址的 ARP REQUEST 报文做限制,不允许其通过 br0 发送到宿主机网卡。

具体转发规则对应的 ebtables 规则如下所示:

Drop All traffic from veth-cni except:
IP response from 169.254.169.250
ARP response from 10.43.0.2
ebtables -t broute -A BROUTING -i veth-cni -j DROP
ebtables -t broute -I BROUTING -i veth-cni -p ipv4 --ip-source 169.254.169.250 -j ACCEPT
ebtables -t broute -I BROUTING -i veth-cni -p arp --arp-opcode 2 --arp-ip-src 10.43.0.2 -j ACCEPT

Drop ARP request for 10.43.0.2 on eth1
ebtables -t nat -D POSTROUTING -p arp --arp-opcode 1 --arp-ip-dst 10.43.0.2  -o eth1 -j DROP

另外也可以在容器所在的主机上将 Docker0 的 bridge 和 CNI 的 bridge 做三层打通,并使用 iptables 来进行控制,目前这个方式还在测试中。

网友评论comments

发表评论

电子邮件地址不会被公开。 必填项已用*标注

  1. 不忘初心说道:

    感觉和现在的rancher环境又不一样了

Copyright © 2012-2017 YUNWEIPAI.COM - 运维派 - 粤ICP备14090526号-3
扫二维码
扫二维码
返回顶部