[TOC]前端
與 Docker 默認的網絡模型不一樣,Kubernetes 造成了一套本身的網絡模型,該網絡模型更加適應傳統的網絡模式,應用可以平滑的從非容器環境遷移到 Kubernetes 環境中。node
自從 Docker 容器出現,容器的網絡通訊一直是衆人關注的焦點,而容器的網絡方案又能夠分爲兩大部分:linux
利用 Net Namespace 能夠爲 Docker 容器建立隔離的網絡環境,容器具備徹底獨立的網絡棧,與宿主機隔離。也可使 Docker 容器共享主機或者其餘容器的網絡命名空間。git
咱們在使用docker run
建立 Docker 容器時,可使用--network=
選項指定容器的網絡模式,Docker 有如下 4 種網絡模式:github
--network=host
指定,不支持多主機;--network=bridge
指定,默認設置,不支持多主機;--network=container:NAME_or_ID
指定,即joiner 容器
,不支持多主機;--network=none
指定,不支持多主機。鏈接到 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 模式有利也有弊,主要包括如下缺點:網絡
Bridge 模式是 Docker 默認的網絡模式,也是開發者最經常使用的網絡模式。在這種模式下,Docker 爲容器建立獨立的網絡棧,保證容器內的進行使用獨立的網絡環境,實現容器之間,容器與宿主機之間的網絡棧隔離。同時,經過宿主機上的 Docker0 網橋,容器能夠與宿主機乃至外界進行網絡通訊。
從上圖能夠看出,容器是能夠與宿主機以及外界的其餘機器通訊的,同一宿主機上,容器之間都是橋接在 Docker0 這個網橋上,Docker0 做爲虛擬交換機使容器間互相通訊。可是,因爲宿主機的 IP 地址與容器 veth pair 的 IP 地址均不在同一個網段,故僅僅依靠 veth pair 和 NameSpace 的技術並不足以使宿主機之外的網絡主動發現容器的存在。Docker 採用了端口綁定的方式(經過 iptables 的 NAT),將宿主機上的端口流量轉發到容器內的端口上,這樣一來,外界就能夠與容器中的進程進行通訊。
Container 模式是一種特殊的網絡模式。該模式下的容器使用其餘容器的網絡命名空間,網絡隔離性會處於 Bridge 模式與 Host 模式之間。也就是說,當容器與其餘容器共享網絡命名空間時,這兩個容器間不存在網絡隔離,但他們與宿主機機器其餘容器又存在網絡隔離。
Container 模式的容器能夠經過 localhost 來與同一網絡命名空間下的其餘容器通訊,傳輸效率高。這種模式節約了必定數量的網絡資源,但並無改變容器與外界的通訊方式。在 Kubernetes 體系架構下引入 Pod 概念,Kubernetes 爲 Pod 建立一個基礎設施容器,同一 Pod 下的其餘容器都以 Container 模式共享這個基礎設施容器的網絡命名空間,相互之間以 localhost 訪問,構成一個統一的總體。
與前幾種不一樣,None 模式的 Docker 容器擁有本身的 Network Namespace,但並不爲 Docker 容器進行網絡配置。也就是說,該 Docker 容器沒有網卡、IP、路由等信息。須要用戶爲 Docker容器添加網卡、配置 IP 等。
常見的跨主機通訊方案主要有如下幾種:
主機 IP + 端口
的方式訪問容器中的服務。顯然,這種方式僅能支持網絡棧的 4 層及以上的應用,而且容器與宿主機緊耦合,很難靈活地處理問題,可擴展性不佳;容器網絡發展到如今,造成了兩大陣營:
CNM 和 CNI 是網絡規範或者網絡體系,並非網絡實現,所以不關心容器的網絡實現方式,CNM 和 CNI 關心的只是網絡管理。
但從網絡實現角度,又可分爲: **隧道方案:**隧道方案在 IaaS 層的網絡中應用也比較多,它的主要缺點是隨着節點規模的增加複雜度會提高,並且出了網絡問題後跟蹤起來比較麻煩,大規模集羣狀況下這是須要考慮的一個問題。
**路由方案:**通常是基於3層或者2層實現網絡隔離和跨主機容器互通的,出了問題也很容易排查。 **Calico:**基於 BGP 協議的路由方案,支持很細緻的 ACL 控制(Nerwork Policy),對混合雲親和度比較高。 **Macvlan:**從邏輯和 Kernel 層來看,是隔離性和性能最優的方案。基於二層隔離,因此須要二層路由器支持,大多數雲服務商不支持,因此混合雲上比較難以實現。
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 都是私有的。
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 數據包裝在另外一種網絡包裏面進行路由轉發和通訊。
咱們使用 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 強一些。
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 規則,控制進出容器的數據包,實現業務需求。
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
請查看個人博文 http://blog.51cto.com/wzlinux/2112061 。
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 網絡:
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 虛擬機,則不須要額外配置了。