Kubernetes & Docker 容器網絡終極之戰(十四)

[TOC]前端

與 Docker 默認的網絡模型不一樣,Kubernetes 造成了一套本身的網絡模型,該網絡模型更加適應傳統的網絡模式,應用可以平滑的從非容器環境遷移到 Kubernetes 環境中。node

自從 Docker 容器出現,容器的網絡通訊一直是衆人關注的焦點,而容器的網絡方案又能夠分爲兩大部分:linux

  • 單主機的容器間通訊;
  • 跨主機的容器間通訊。

1、單主機 Docker 網絡通訊

利用 Net Namespace 能夠爲 Docker 容器建立隔離的網絡環境,容器具備徹底獨立的網絡棧,與宿主機隔離。也可使 Docker 容器共享主機或者其餘容器的網絡命名空間。git

咱們在使用docker run建立 Docker 容器時,可使用--network=選項指定容器的網絡模式,Docker 有如下 4 種網絡模式:github

  • host 模式,使用--network=host指定,不支持多主機;
  • bridge 模式,使用--network=bridge指定,默認設置,不支持多主機;
  • container 模式,使用--network=container:NAME_or_ID指定,即joiner 容器,不支持多主機;
  • none 模式,使用--network=none指定,不支持多主機。

1.一、host 模式

鏈接到 host 網絡的容器共享 Docker host 的網絡棧,容器的網絡配置與 host 徹底同樣。docker

咱們先查看一下主機的網絡。數據庫

[root@datanode03 ~]# ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:44:8d:48:70  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp1s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.203  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::2e0:70ff:fe92:4779  prefixlen 64  scopeid 0x20<link>
        ether 00:e0:70:92:47:79  txqueuelen 1000  (Ethernet)
        RX packets 46093  bytes 66816291 (63.7 MiB)
        RX errors 0  dropped 1  overruns 0  frame 0
        TX packets 24071  bytes 1814769 (1.7 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 170  bytes 107720 (105.1 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 170  bytes 107720 (105.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

而後建立 host 網絡的容器,再查看容器的網絡信息。json

[root@datanode03 ~]# docker run -it --network=host busybox
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
90e01955edcd: Pull complete 
Digest: sha256:2a03a6059f21e150ae84b0973863609494aad70f0a80eaeb64bddd8d92465812
Status: Downloaded newer image for busybox:latest
/ # ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:44:8D:48:70  
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

enp1s0    Link encap:Ethernet  HWaddr 00:E0:70:92:47:79  
          inet addr:192.168.1.203  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::2e0:70ff:fe92:4779/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:45850 errors:0 dropped:1 overruns:0 frame:0
          TX packets:23921 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:66794758 (63.7 MiB)  TX bytes:1783655 (1.7 MiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:170 errors:0 dropped:0 overruns:0 frame:0
          TX packets:170 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:107720 (105.1 KiB)  TX bytes:107720 (105.1 KiB)

在容器中能夠看到 host 的全部網卡,而且連 hostname 也是 host 的,能夠直接使用宿主機 IP 地址與外界通訊,無需額外進行 NAT 轉換。因爲容器通訊時,再也不須要經過 Linux Bridge 等方式轉發或者數據包的封裝,性能上有很大的優點。後端

固然,Host 模式有利也有弊,主要包括如下缺點:網絡

  • 容器沒有隔離、獨立的網絡棧:容器因與宿主機共用網絡棧而爭搶網絡資源,而且容器崩潰也可能致使主機崩潰,這再生產環境中是不容許發生的。
  • 端口資源:Docker host 上已經使用的端口就不能再用了。

1.2 Bridge 模式

Bridge 模式是 Docker 默認的網絡模式,也是開發者最經常使用的網絡模式。在這種模式下,Docker 爲容器建立獨立的網絡棧,保證容器內的進行使用獨立的網絡環境,實現容器之間,容器與宿主機之間的網絡棧隔離。同時,經過宿主機上的 Docker0 網橋,容器能夠與宿主機乃至外界進行網絡通訊。

從上圖能夠看出,容器是能夠與宿主機以及外界的其餘機器通訊的,同一宿主機上,容器之間都是橋接在 Docker0 這個網橋上,Docker0 做爲虛擬交換機使容器間互相通訊。可是,因爲宿主機的 IP 地址與容器 veth pair 的 IP 地址均不在同一個網段,故僅僅依靠 veth pair 和 NameSpace 的技術並不足以使宿主機之外的網絡主動發現容器的存在。Docker 採用了端口綁定的方式(經過 iptables 的 NAT),將宿主機上的端口流量轉發到容器內的端口上,這樣一來,外界就能夠與容器中的進程進行通訊。

1.3 Container 模式

Container 模式是一種特殊的網絡模式。該模式下的容器使用其餘容器的網絡命名空間,網絡隔離性會處於 Bridge 模式與 Host 模式之間。也就是說,當容器與其餘容器共享網絡命名空間時,這兩個容器間不存在網絡隔離,但他們與宿主機機器其餘容器又存在網絡隔離。

Container 模式的容器能夠經過 localhost 來與同一網絡命名空間下的其餘容器通訊,傳輸效率高。這種模式節約了必定數量的網絡資源,但並無改變容器與外界的通訊方式。在 Kubernetes 體系架構下引入 Pod 概念,Kubernetes 爲 Pod 建立一個基礎設施容器,同一 Pod 下的其餘容器都以 Container 模式共享這個基礎設施容器的網絡命名空間,相互之間以 localhost 訪問,構成一個統一的總體。

1.四、None 模式

與前幾種不一樣,None 模式的 Docker 容器擁有本身的 Network Namespace,但並不爲 Docker 容器進行網絡配置。也就是說,該 Docker 容器沒有網卡、IP、路由等信息。須要用戶爲 Docker容器添加網卡、配置 IP 等。

2、跨主機 Docker 網絡通訊分類

2.1 通訊方案

常見的跨主機通訊方案主要有如下幾種:

  • Host 模式:容器直接使用宿主機的網絡,這樣天生就能夠支持跨主機主機通訊。這種方式雖然能夠解決跨主機通訊問題,但應用場景頗有限,容易出現端口衝突,也沒法作到隔離網絡環境,一個容器崩潰極可能引發整個宿主機的崩潰;
  • 端口模式:經過綁定容器端口到宿主機端口,跨主機通訊時使用主機 IP + 端口的方式訪問容器中的服務。顯然,這種方式僅能支持網絡棧的 4 層及以上的應用,而且容器與宿主機緊耦合,很難靈活地處理問題,可擴展性不佳;
  • 定義容器網絡:使用 Open vSwitch 或 Flannel 等第三方 SDN 工具,爲容器構建能夠跨主機通訊網絡環境。這一類方案通常要求各個主機上的 Docker0 網橋的 cidr 不一樣,以免出現 IP 衝突的問題,限制容器在宿主機上可獲取的 IP 範圍。而且在容器須要對集羣外提供服務時,須要比較複雜的配置,對部署實施人員的網絡技能要求比較高。

2.二、容器網絡規範

容器網絡發展到如今,造成了兩大陣營:

  • Docker 的 CNM;
  • Google、CoreOS、Kubernetes 主導的 CNI。

CNM 和 CNI 是網絡規範或者網絡體系,並非網絡實現,所以不關心容器的網絡實現方式,CNM 和 CNI 關心的只是網絡管理。

  • CNM(Container Network Model):CNM 的優點在於原生,容器網絡和 Docker 容器生命週期結合緊密;缺點是被 Docker 「綁架」。支持 CNM 網絡規範的容器網絡實現包括:Docker Swarm overlay、Macvlan & IP networkdrivers、Calico、Contiv、Weave等。
  • CNI(Container Network Interface):CNI 的優點是兼容其餘容器技術(rkt)以及上層的編排系統(Kubernetes&Mesos)、並且社區活躍勢頭迅猛;缺點是非 Docker 原生。支持 CNI 的網絡規範的容器網絡實現包括:Kubernetes、Weave、Macvlan、Calico、Flannel、Contiv、Mesos CNI,由於它們都實現了 CNI 規範,用戶不管選擇哪一種方案,獲得的網絡模型都同樣,即每一個 Pod 都有獨立的 IP,能夠直接通訊。區別在於不一樣方案的底層實現不一樣,有的採用基於 VxLAN 的 Overlay 實現,有的則是 Underlay,性能上有區別。再有就是是否支持 Network Policy。

2.三、網絡通訊實現方案

但從網絡實現角度,又可分爲: **隧道方案:**隧道方案在 IaaS 層的網絡中應用也比較多,它的主要缺點是隨着節點規模的增加複雜度會提高,並且出了網絡問題後跟蹤起來比較麻煩,大規模集羣狀況下這是須要考慮的一個問題。

  • Weave:UDP 廣播,本機創建新的 BR,經過 PCAP 互通。
  • Open vSwitch(OVS):基於 VxLAN 和 GRE 協議,可是性能方面損失比較嚴重。
  • Flannel:UDP 廣播,VxLAN。
  • Racher:IPSec。

**路由方案:**通常是基於3層或者2層實現網絡隔離和跨主機容器互通的,出了問題也很容易排查。 **Calico:**基於 BGP 協議的路由方案,支持很細緻的 ACL 控制(Nerwork Policy),對混合雲親和度比較高。 **Macvlan:**從邏輯和 Kernel 層來看,是隔離性和性能最優的方案。基於二層隔離,因此須要二層路由器支持,大多數雲服務商不支持,因此混合雲上比較難以實現。

2.四、Kubernetes 網絡模型

Kubernetes 採用的是基於扁平地址空間的網絡模型,集羣中的每一個 Pod 都有本身的 IP 地址,Pod 之間不須要配置 NAT 就能直接通訊。另外,同一個 Pod 中的容器共享 Pod 的 IP,可以經過 localhost 通訊。

這種網絡模型對應用開發者和管理員至關友好,應用能夠很是方便地從傳統網絡遷移到 Kubernetes。每一個 Pod 可被看做是一個個獨立的系統,而 Pod 中的容器則可被看作同一系統中的不一樣進程。

Pod 內容器之間的通訊 當 Pod 被調度到某個節點,Pod 中的全部容器都在這個節點上運行,這些容器共享相同的本地文件系統、IPC 和網絡命名空間。

不一樣 Pod 之間不存在端口衝突的問題,由於每一個 Pod 都有本身的 IP 地址。當某個容器使用 localhost 時,意味着使用的是容器所屬 Pod 的地址空間。

好比 Pod A 有兩個容器 container-A1 和 container-A2,container-A1 在端口 1234 上監聽,當 container-A2 鏈接到 localhost:1234,實際上就是在訪問 container-A1。這不會與同一個節點上的 Pod B 衝突,即便 Pod B 中的容器 container-B1 也在監聽 1234 端口。

Pod 之間的通訊 Pod 的 IP 是集羣可見的,即集羣中的任何其餘 Pod 和節點均可以經過 IP 直接與 Pod 通訊,這種通訊不須要藉助任何的網絡地址轉換、隧道或代理技術。Pod 內部和外部使用的是同一個 IP,這也意味着標準的命名服務和發現機制,好比 DNS 能夠直接使用。

Pod 與 Service 的通訊 Pod 間能夠直接經過 IP 地址通訊,但前提是 Pod 得知道對方的 IP。在 Kubernetes 集羣中, Pod 可能會頻繁的銷燬和建立,也就是說 Pod 的 IP 不是固定的。爲了解決這個問題,Service 提供了訪問 Pod 的抽象層。不管後端的 Pod 如何變化,Service 都做爲穩定的前端對外提供服務。同時,Service 還提供了高可用和負載均衡功能,Service 負責將請求轉發給正確的 Pod。

外部訪問 不管是 Pod 的 IP 仍是 Service 的 Cluster IP,它們只能在 Kubernetes 集羣中可見,對集羣以外的世界,這些 IP 都是私有的。

3、跨主機 Docker 網絡

3.1 Flannel 網絡方案

Kubernetes 安裝方式。

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/bc79dd1505b0c8681ece4de4c0d86c5cd2643275/Documentation/kube-flannel.yml

flannel 是 CoreOS 開發的容器網絡解決方案。flannel 爲每一個 host 分配一個 subnet,容器今後 subnet 中分配 IP,這些 IP 能夠在 host 間路由,容器間無需 NAT 和 port mapping 就能夠跨主機通訊。

每一個 subnet 都是從一個更大的 IP 池中劃分的,flannel 會在每一個主機上運行一個叫 flanneld 的 agent,其職責就是從池子中分配 subnet。爲了在各個主機間共享信息,flannel 用 etcd(與 consul 相似的 key-value 分佈式數據庫)存放網絡配置、已分配的 subnet、host 的 IP 等信息。

數據包如何在主機間轉發是由 backend 實現的。flannel 提供了多種 backend,有 UDP、vxlan、host-gw、aws-vpc、gce 和 alloc 路由,最經常使用的有 vxlan 和 host-gw。

Flannel 實質上是一種疊加網絡(Overlay Network),也就是將 TCP 數據包裝在另外一種網絡包裏面進行路由轉發和通訊。

  1. 容器直接使用目標容器的 IP 訪問,默認經過容器內部的 eth0 發送出去;
  2. 報文經過 veth pair 被髮送到 vethXXX;
  3. vethXXX 是直接鏈接到 cni0,報文經過虛擬 bridge cni0 發送出去;
  4. 查找路由表,外部容器 IP 的報文都會轉發到 flannel.1 的虛擬網卡,這是一個 P2P 的虛擬網卡,而後報文就被轉發到監聽在另外一端的 flanneld;
  5. flanneld 經過 etcd 維護了各個節點之間的路由表,把原來的報文 UDP 封裝一層,經過配置的 iface 發送出去;
  6. 報文經過主機之間的網絡棧找到目標主機;
  7. 報文繼續往上送,到達傳輸層,交給監聽的 flanneld 程序處理;
  8. 數據被解包,而後發送給 flannel.1 虛擬網卡;
  9. 查找路由表,發現對應容器的報文要交給 cni0;
  10. cni0 鏈接到本身的容器,把報文發送過去。

咱們使用 kubectl apply 安裝的 flannel 默認的 backend 爲 vxlan,host-gw 是 flannel 的另外一個 backend,咱們將前面的 vxlan backend 切換成 host-gw。

與 vxlan 不一樣,host-gw 不會封裝數據包,而是在主機的路由表中建立到其餘主機 subnet 的路由條目,從而實現容器跨主機通訊。要使用 host-gw 首先修改 flannel 的配置 flannel-config.json

kubectl edit cm kube-flannel-cfg -o yaml -n kube-system

找到以下字段進行修改。

net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "host-gw"
      }
    }

host-gw 把每一個主機都配置成網關,主機知道其餘主機的 subnet 和轉發地址,因爲 vxlan 須要對數據包進行額外的打包和拆包,性能會比 vxlan 強一些。

3.二、Calico 網絡方案

Kubernetes 安裝方式。

kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/rbac-kdd.yaml
kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml

Calico 把每一個操做系統的協議棧當作一個路由器,而後認爲全部的容器是連在這個路由器上的網絡終端,在路由器之間運行標準的路由協議——BGP,而後讓他們本身去學習這個網絡拓撲該如何轉發,因此Calico 是一個純三層的虛擬網絡方案,Calico 爲每一個容器分配一個 IP,每一個 host 都是 router,把不一樣 host 的容器鏈接起來。與 VxLAN 不一樣的是,Calico 不對數據包作額外封裝,不須要 NAT 和端口映射,擴展性和性能都很好。

與其餘容器網絡方案相比,Calico 還有一大優點:network policy。用戶能夠動態定義 ACL 規則,控制進出容器的數據包,實現業務需求。

  1. **Felix:**運行在每一臺 Host 的 agent 進程,主要負責網絡接口管理和監聽、路由、ARP 管理、ACL 管理和同步、狀態上報等;
  2. **Orchestrator Plugin:**編排插件,並非獨立運行的某些進程,而是設計與 k8s、OpenStack 等平臺集成的插件,如 Neutron’s ML2 plugin 用於用戶使用 Neutron API 來管理 Calico,本質是要解決模型和 API 間的兼容性問題;
  3. **Etcd:**Calico 模型的存儲引擎;
  4. **BGP Client(BIRD):**Calico 爲每一臺 Host 部署一個 BGP Client,使用 BIRD 實現,BIRD 是一個單獨的持續發展的項目,實現了衆多動態路由協議好比 BGP、OSPF、RIP 等。在 Calico 的角色是監聽 Host 上由 Felix 注入的路由信息,而後經過 BGP 協議廣播告訴剩餘 Host 節點,從而實現網絡互通;
  5. **BGP Route Reflector(BIRD):**在大型網絡規模中,若是僅僅使用 BGP client 造成 mesh 全網互聯的方案就會致使規模限制,由於全部節點之間倆倆互聯,須要 N^2 個鏈接,爲了解決這個規模問題,能夠採用 BGP 的 Router Reflector 的方法,使全部 BGP Client 僅與特定 RR 節點互聯並作路由同步,從而大大減小鏈接數。

3.三、Canal 網絡方案

Network Policy Network Policy 是 Kubernetes 的一種資源。Network Policy 經過 Label 選擇 Pod,並指定其餘 Pod 或外界如何與這些 Pod 通訊。

默認狀況下,全部 Pod 是非隔離的,即任何來源的網絡流量都可以訪問 Pod,沒有任何限制。當爲 Pod 定義了 Network Policy,只有 Policy 容許的流量才能訪問 Pod。

不過,不是全部的 Kubernetes 網絡方案都支持 Network Policy。好比 Flannel 就不支持,Calico 是支持的。咱們接下來將用 Canal 來演示 Network Policy。Canal 這個開源項目頗有意思,它用 Flannel 實現 Kubernetes 集羣網絡,同時又用 Calico 實現 Network Policy。

kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/canal/rbac.yaml
kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/canal/canal.yaml

3.四、Docker overlay 網絡方案

請查看個人博文 http://blog.51cto.com/wzlinux/2112061

3.五、Docker macvlan 網絡方案

docker network create -d macvlan --subnet=172.16.86.0/24 --gateway=172.16.86.1 -o parent=enp0s9 mac_net1

macvlan 自己是 linxu kernel 模塊,其功能是容許在同一個物理網卡上配置多個 MAC 地址,即多個 interface,每一個 interface 能夠配置本身的 IP。macvlan 本質上是一種網卡虛擬化技術,Docker 用 macvlan 實現容器網絡就不奇怪了。

macvlan 的最大優勢是性能極好,相比其餘實現,macvlan 不須要建立 Linux bridge,而是直接經過以太 interface 鏈接到物理網絡。

macvlan 會獨佔主機的網卡,也就是說一個網卡只能建立一個 macvlan 網絡:

但主機的網卡數量是有限的,如何支持更多的 macvlan 網絡呢?

docker network create -d macvlan -o parent=enp0s9 mac_net2

好在 macvlan 不只能夠鏈接到 interface(如 enp0s9),也能夠鏈接到 sub-interface(如 enp0s9.xxx)。

VLAN 是現代網絡經常使用的網絡虛擬化技術,它能夠將物理的二層網絡劃分紅多達 4094 個邏輯網絡,這些邏輯網絡在二層上是隔離的,每一個邏輯網絡(即 VLAN)由 VLAN ID 區分,VLAN ID 的取值爲 1-4094。

Linux 的網卡也能支持 VLAN(apt-get install vlan),同一個 interface 能夠收發多個 VLAN 的數據包,不過前提是要建立 VLAN 的 sub-interface。

好比但願 enp0s9 同時支持 VLAN10 和 VLAN20,則需建立 sub-interface enp0s9.10 和 enp0s9.20。

在交換機上,若是某個 port 只能收發單個 VLAN 的數據,該 port 爲 Access 模式,若是支持多 VLAN,則爲 Trunk 模式,因此接下來實驗的前提是:

enp0s9 要接在交換機的 trunk 口上。不過咱們用的是 VirtualBox 虛擬機,則不須要額外配置了。

相關文章
相關標籤/搜索