1. 程式人生 > >Rancher技術特性全解及容器網路解決方案

Rancher技術特性全解及容器網路解決方案

作者簡介:

劉力
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 到 [email protected] 有了變化,原先網絡卡多 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 來進行控制,目前這個方式還在測試中。