云原生環(huán)境網(wǎng)絡(luò)方案1 --- 容器網(wǎng)絡(luò)模型與K8S網(wǎng)絡(luò)模型
K8S系統(tǒng)將網(wǎng)絡(luò)方面的功能托管給了第三方插件來(lái)完成庶诡,本文將簡(jiǎn)略描述其基本通訊原理以及常見的幾種網(wǎng)絡(luò)方案惦银。
Docker網(wǎng)絡(luò)通訊模型
Docker容器的網(wǎng)絡(luò)設(shè)置有4種形式,去除None和共享Namespace之外末誓,其實(shí)只有兩種網(wǎng)絡(luò)即:
主機(jī)網(wǎng)絡(luò)
Bridge網(wǎng)絡(luò)
其中Bridge是Docker容器的默認(rèn)網(wǎng)絡(luò)扯俱。
在任何安裝了Docker的宿主機(jī)環(huán)境上,我們都能發(fā)現(xiàn)系統(tǒng)中新增了一個(gè)名為Docker0的網(wǎng)橋設(shè)備喇澡。
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.024204b14ad5 no veth9d63d04 vethbf226b3
這個(gè)名為Docker0的網(wǎng)橋是默認(rèn)用來(lái)給Docker容器通信用的迅栅。在容器創(chuàng)建過(guò)程中,默認(rèn)會(huì)產(chǎn)生一對(duì)虛擬網(wǎng)口veth读存,一頭連在Docker0上,另外一頭連接在容器內(nèi)的eth0上呕屎。如下圖:
我們查看一下這個(gè)eth0的ip地址
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
157545493a1b nginx:latest "/docker-en.…" 26 minutes ago Up 26 minutes 0.0.0.0:80->80/tcp nginx_demo
$ sudo docker container inspect 157545493a1b
...
"Gateway": "172.18.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:12:00:03",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "91397797f09b069b9472a638861e21e9c0e861c14d2374cec9184997b52a2ba1",
"EndpointID": "8e8230218df826a1005625a09398785833497d79af2c69b0b82db37eaa0798a2",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:03",
"DriverOpts": null
}
}
可以看到這個(gè)容器的eth0的IP地址被設(shè)置為了172.18.0.2
,而網(wǎng)關(guān)被設(shè)置為了172.18.0.1
。那這個(gè)網(wǎng)關(guān)的地址是誰(shuí)的呢居凶?
$ ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.0.1 netmask 255.255.0.0 broadcast 172.18.255.255
inet6 fe80::42:4ff:feb1:4ad5 prefixlen 64 scopeid 0x20<link>
ether 02:42:04:b1:4a:d5 txqueuelen 0 (Ethernet)
RX packets 494147 bytes 23247538 (23.2 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 886037 bytes 1319084427 (1.3 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
在宿主機(jī)中使用ifconfig
命令抹估,我們可以看到一個(gè)名為docker0的設(shè)備其網(wǎng)絡(luò)地址為172.18.0.1
替饿,正是容器設(shè)置里的網(wǎng)關(guān)地址视卢。在Linux環(huán)境中惋砂,可以為網(wǎng)橋配置網(wǎng)絡(luò)地址西饵,其實(shí)質(zhì)上是一個(gè)名為Docker0的虛擬網(wǎng)卡設(shè)備插在Docker0網(wǎng)橋上积蜻,做為一個(gè)可參與路由的節(jié)點(diǎn):
$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.17.1.1 0.0.0.0 UG 0 0 0 ens160
172.17.1.0 0.0.0.0 255.255.255.0 U 0 0 0 ens160
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
如果此時(shí)竿拆,我再啟動(dòng)另外一個(gè)容器丙笋,這個(gè)容器的eth0的地址為172.18.0.3
怠肋,那這兩個(gè)容器中笙各,相互是可以ping通的杈抢。那么他們之間的網(wǎng)絡(luò)拓?fù)淙缦聢D:
此時(shí),數(shù)據(jù)包還是只能在宿主機(jī)內(nèi)部傳輸何陆,我們啟動(dòng)的這個(gè)容器是一個(gè)nginx web服務(wù)器晃洒,從上面的容器信息輸出可以看到0.0.0.0:80->80/tcp
吃引,其實(shí)現(xiàn)上是通過(guò)NAT進(jìn)行的庐氮,從剛才的輸出可以看到音婶,宿主機(jī)的80端口被映射到了容器的80端口上碴卧。查看一下宿主機(jī)的NAT表界弧,我們可以看到這個(gè)映射:
$ sudo iptables -L -n -t nat
...
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.18.0.0/16 0.0.0.0/0
MASQUERADE tcp -- 172.18.0.2 172.18.0.2 tcp dpt:80
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.18.0.2:80
可以看到,Docker網(wǎng)絡(luò)模型的設(shè)計(jì),采用了私網(wǎng)地址+Linux 網(wǎng)橋的方法與外部外諾做解耦吱窝,流量進(jìn)入宿主機(jī)時(shí)兴使,使用DNAT方式映射端口欠母,容器網(wǎng)絡(luò)內(nèi)部出去則需要做SNAT借用宿主機(jī)的網(wǎng)絡(luò)地址。由于使用了大量的NAT映射,大規(guī)模網(wǎng)絡(luò)出現(xiàn)問(wèn)題的情況下,定位是非常困難的啡莉。
另外一種使用主機(jī)網(wǎng)絡(luò)的方案,和直接跑一個(gè)程序在宿主機(jī)上是完全一致的,本文不做贅述囤躁,Docker的視角實(shí)際上只著眼于宿主機(jī)內(nèi)部的網(wǎng)絡(luò)通訊言蛇,跨宿主機(jī)通訊的場(chǎng)景實(shí)際上并沒有考慮的。這樣的考量應(yīng)當(dāng)是對(duì)多宿主機(jī)組成的容器云進(jìn)行調(diào)度和編排的方案需要考慮的像鸡,比如K8S着绷。
K8S跨宿主機(jī)網(wǎng)絡(luò)通訊模型
上一節(jié)提到的是同一個(gè)宿主機(jī)內(nèi)部容器間的通信,本節(jié)描述跨宿主機(jī)通訊的幾種方式。
目前缕棵,跨宿主機(jī)通訊在網(wǎng)絡(luò)層面一般有兩種即:
- 基于隧道的宿主機(jī)間通訊
- 基于路由的宿主機(jī)間通訊
這兩種方式又被稱為overlayer和underlayer,其區(qū)別在于容器網(wǎng)絡(luò)和宿主機(jī)網(wǎng)絡(luò)是否在同一層面触趴。
基于隧道的宿主機(jī)通訊
其中基于隧道的宿主機(jī)間通訊可能有多種類型,目前最常見的是基于VxLAN的隧道贰谣,這也是虛擬化網(wǎng)絡(luò)的常規(guī)方案秘豹。以flannel的VxLAN方案為例。
在每臺(tái)宿主機(jī)上,會(huì)有一個(gè)VxLAN的VTEP虛擬設(shè)備flannel.1,其負(fù)責(zé)與其他VTEP設(shè)備打通祝拯,構(gòu)成2層VxLAN虛擬交換網(wǎng)絡(luò)。以上圖為例,當(dāng)宿主機(jī)2加入到網(wǎng)絡(luò)中時(shí)籽前,宿主機(jī)1上會(huì)有如下路由信息:
$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
...
172.18.1.0 172.18.1.0 255.255.255.0 UG 0 0 0 flannel.1
其中172.18.1.0是宿主機(jī)2的VTEP設(shè)備的網(wǎng)絡(luò)地址阻荒。宿主機(jī)1上所有發(fā)往該網(wǎng)段的網(wǎng)絡(luò)包會(huì)發(fā)往VTEP設(shè)備flannel.1,然后走VxLAN隧道發(fā)往宿主機(jī)2众羡。
flannel的隧道方案除了VxLAN以外還有一種基于存UDP的早期方案侨赡,由于性能問(wèn)題,目前已被廢棄粱侣。
基于路由的宿主機(jī)通訊
上述基于隧道的宿主機(jī)間通訊羊壹,由于多了兩次拆解包的過(guò)程,性能會(huì)有所下降齐婴。據(jù)有關(guān)數(shù)據(jù)表明油猫,網(wǎng)絡(luò)傳輸效率下降約20%-30%。除了這種通訊方式之外柠偶,還有一種基于路由的跨宿主機(jī)通信方式情妖。例如除了上節(jié)提到的基于VxLAN隧道的方案外,flannel還有一種基于host-gateway的3層路由方案嚣州。其原理比較簡(jiǎn)單鲫售,即將各個(gè)宿主機(jī)網(wǎng)絡(luò)進(jìn)行子網(wǎng)劃分,并在每臺(tái)宿主機(jī)上設(shè)置相同的路由该肴,類似以下:
$ ip route
...
172.18.0.0/24 via 172.17.1.41 dev ens160
172.18.1.0/24 via 172.17.1.42 dev ens160
利用Linux的路由功能實(shí)現(xiàn)容器的跨宿主機(jī)通信情竹。剩下的問(wèn)題就是如何保證各個(gè)宿主機(jī)上的路由表一致了。不同的插件方案使用的解決方案是不同的匀哄。例如flannel使用了etcd來(lái)存儲(chǔ)同步路由信息秦效,而Calico則使用了BGP來(lái)實(shí)現(xiàn)這一點(diǎn)。除此以外涎嚼,Calico方案也不會(huì)在宿主機(jī)上建立任何網(wǎng)橋設(shè)備阱州,這一點(diǎn)與flannel有很大區(qū)別。在Calico方案組veth-pair的另一端不會(huì)被插入到任何網(wǎng)橋設(shè)備中法梯,而只是放在宿主機(jī)的網(wǎng)絡(luò)命名空間內(nèi)苔货。
172.18.2.2 dev cali3863f3 scope link
宿主機(jī)上會(huì)有類似以上的路由記錄,將相關(guān)數(shù)據(jù)包發(fā)送至虛擬設(shè)備cali3863f3上立哑。
與flannel方案相比夜惭,由于沒了虛擬網(wǎng)橋連接同一宿主機(jī)內(nèi)各個(gè)容器,Calico方案需要在宿主機(jī)的路由表中添加多得多的路由表項(xiàng)铛绰。
基于路由的宿主機(jī)通信要求各個(gè)宿主機(jī)在二層網(wǎng)絡(luò)層面是直連互通的诈茧,對(duì)底層網(wǎng)絡(luò)有一定的要求。
常見容器網(wǎng)絡(luò)插件
由于k8s將網(wǎng)絡(luò)這部分開放給社區(qū)捂掰,目前CNI這部分的插件較多敢会,常見的有如下幾種:
- Flannel曾沈,目前最普遍的實(shí)現(xiàn),同時(shí)支持overlayer(UDP鸥昏,VxLAN)和underlayer(Host-gw)的網(wǎng)絡(luò)后端實(shí)現(xiàn)塞俱。
- Calico,基于BGP的underlayer方案互广,功能豐富敛腌,對(duì)底層網(wǎng)絡(luò)有一定要求卧土。
- Cilium惫皱,基于eBPF和XDP的高性能Overlayer方案
- Kube-Route,采用BGP提供網(wǎng)絡(luò)直連尤莺,集成基于LVS的負(fù)載均衡能力
- WeaveNet旅敷,采用UDP封裝實(shí)現(xiàn)的L2 Overlayer方案,支持用戶態(tài)(慢颤霎,可加密)和內(nèi)核態(tài)(快媳谁,無(wú)加密)兩種實(shí)現(xiàn)
總結(jié)
宿主機(jī)內(nèi)部的容器間網(wǎng)絡(luò)通訊有基于網(wǎng)橋的,基于宿主機(jī)網(wǎng)絡(luò)的友酱∏缫簦跨宿主機(jī)容器間網(wǎng)絡(luò)通訊有Overlayer和Underlayer兩種,區(qū)別在于容器間通訊是否和宿主機(jī)間通訊屬于同一層面缔杉。
現(xiàn)有普適性最強(qiáng)的方案是Flannel锤躁,同時(shí)具備Overlayer和Underlayer的方案,為了統(tǒng)一架構(gòu)或详,宿主機(jī)內(nèi)部通訊使用了Linux虛擬網(wǎng)橋方案系羞。需要注意的是,F(xiàn)lannel方案不支持K8S的NetworkPolicy霸琴,需要其他CNI插件配合椒振,比如Calico。
Calico方案是目前比較成熟的Underlayer方案梧乘,其采用了BGP來(lái)同步多宿主機(jī)的路由表澎迎。
本文討論的是網(wǎng)絡(luò)層的解決方案,著眼于從網(wǎng)絡(luò)層面聯(lián)通各個(gè)容器的網(wǎng)絡(luò)选调,至于訪問(wèn)控制夹供,熔斷,限流等內(nèi)容是在其他組件比如Service Proxy学歧,Service Mesh罩引,API Gateway來(lái)實(shí)現(xiàn)的,不在本文討論范圍內(nèi)枝笨。
最后袁铐,我們回顧一下K8S網(wǎng)絡(luò)模型的3原則:
- 任意兩個(gè)容器(或者POD-共享網(wǎng)絡(luò)命名空間的一組容器)之間可以直接通訊揭蜒,無(wú)需顯式NAT。
- 宿主機(jī)與容器(POD)之間也是可以直接通訊剔桨,無(wú)需顯式NAT屉更。
- 容器(POD)看到自己的IP和其他人看到它的IP是一致的。