Docker 提供了 overlay driver,使用戶可以創(chuàng)建基于 VxLAN 的 overlay 網(wǎng)絡(luò)。VxLAN 可將二層數(shù)據(jù)封裝到 UDP 進行傳輸,VxLAN 提供與 VLAN 相同的以太網(wǎng)二層服務(wù),但是擁有更強的擴展性和靈活性。linux下是使用了net namespace來隔離docker創(chuàng)建的overlay網(wǎng)絡(luò)。
Docker 網(wǎng)絡(luò)模型如下:
Sandbox
一個Sandbox包含了一個容器網(wǎng)絡(luò)棧的配置。其中包括了對容器的網(wǎng)卡,路由表以及對DNS設(shè)置的管理。通常,一個Sandbox的實現(xiàn)可以是一個Linux Network Namespace,一個FreeBSD Jail或者其他類似的東西。一個Sandbox可以包含多個處于不同Network的Endpoint。
Endpoint
Endpoint將一個Sandbox加入一個Network。Endpoint的實現(xiàn)可以是一個veth對,一個Open vSwitch internal port或者其他類似的東西。一個Endpoint只能屬于一個Network和一個Sandbox。
Network
Network是一個能夠互相通信的Endpoint的集合。Network的實現(xiàn)可以是一個Linux網(wǎng)橋,一個VLAN等等。
Docker swarm中的服務(wù)發(fā)現(xiàn):
每一個docker容器都有一個域名解析器,用來把域名查詢請求轉(zhuǎn)發(fā)到docker engine;docker engine內(nèi)部dns的服務(wù)器收到請求后就會在發(fā)出請求的容器所在的所有網(wǎng)絡(luò)中檢查域名對應(yīng)的是不是一個容器或者是服務(wù),如果是,docker引擎就會從存儲的key-value建值對中查找這個容器名、任務(wù)名、或者服務(wù)名對應(yīng)的IP地址,并把這個IP地址或者是服務(wù)的虛擬IP地址返回給發(fā)起請求的域名解析器。如果目的容器或服務(wù)和源容器不在同一個網(wǎng)絡(luò)里面,Docker引擎會把這個DNS查詢轉(zhuǎn)發(fā)到配置的默認DNS服務(wù)。
上圖展示了task1.client請求兩個不同資源dns返回的不同結(jié)果
Docker swarm 中的LB分為兩種情況:
1. Ingress Load Balancing
2. Internal Load Balancing
簡要介紹測試環(huán)境下docker swarm中的網(wǎng)絡(luò)分布情況:
環(huán)境:
swarm-a(manager node):10.10.8.92
swarm-b(work node):10.10.8.93
swarm-c(work node):10.10.8.94
在docker swarm集群創(chuàng)建的開始,docker 會給每臺host創(chuàng)建除了docker0以外的兩個網(wǎng)絡(luò),分是bridge類型(docker_gwbridge網(wǎng)橋)和overlay類型(ingress)的網(wǎng)絡(luò),以及一個過度的命名空間ingress_sbox,我們可以使用如下命令自建overlay網(wǎng)絡(luò),結(jié)果如下:
docker network create --driver overlay mynet (后續(xù)會有用到)
- ingress網(wǎng)絡(luò)的IPAM( IP Address Management)分配如下:
- mynet自建的overlay會使用docker自動分配的IPAM:
- 建完的overlay網(wǎng)絡(luò)的同時會在host本地創(chuàng)建對應(yīng)的Net Namespace如下:
注意1:要是想看到容器創(chuàng)建的兩個Net Namespace需要執(zhí)行
ln -s /var/run/docker/netns /var/run/netns
1. Ingress Load Balancing
1)、部署一個service使用默認的ingress網(wǎng)絡(luò):
docker service create --name web_ingress_lb --replicas=2 --publish 8090:80 httpd
- 部署的兩個容器分別處在a和b節(jié)點上:
- service:web_ingress_lb的網(wǎng)絡(luò)連接結(jié)構(gòu)圖如下:
Swarm mode下,docker會創(chuàng)建一個默認的overlay網(wǎng)絡(luò)—ingress network。Docker也會為每個worker節(jié)點創(chuàng)建一個特殊的net namespace(sandbox)-- ingress_sbox。ingress_sbox有兩個endpoint,一個用于連接ingress network,另一個用于連接local bridge network docker_gwbridge。Ingress network的IP空間為10.255.0.0/16,所有router mesh的service都共用此空間。
2)、Ingress Load Balancing實現(xiàn)方式:
1>宿主機網(wǎng)絡(luò)通過worker節(jié)點IP和service published port來訪問服務(wù)。比如:上面服務(wù)定義-p 8090:80,可以通過workerIP:8090 訪問服務(wù)
2>每個節(jié)點iptables中NAT表定義規(guī)則,對于匹配published的宿主機端口(8090)的數(shù)據(jù),將其dst IP轉(zhuǎn)換成ingress_sbox中的ip:172.18.0.2。使數(shù)據(jù)包轉(zhuǎn)發(fā)到ingress_sbox的ns中交給該ns處理做下一步的轉(zhuǎn)發(fā)。
3>Ingress_sbox是swarm為每個節(jié)點默認創(chuàng)建的net namespace,用于連接ingress overlay network。此處會設(shè)置mangle表,將dst port為8090的數(shù)據(jù)做標記(fwmark)。同時做DNAT轉(zhuǎn)換成vip地址使數(shù)據(jù)包能正常轉(zhuǎn)發(fā)到ingress的ns中,該vip由ingress_sbox的ipvs做負載轉(zhuǎn)發(fā)。
4>Ingress_sbox會設(shè)置kernel中的LVS模塊,將標記fwmark的包LB到各個實際IP中,默認round-robin算法,forware為VS/NAT方式。容器底層間通過overlay網(wǎng)絡(luò)互連通信。
- 數(shù)據(jù)包在這一步進入ingress的ns后怎么實現(xiàn)到后端真實容器上呢?我們猜想下ingress想要轉(zhuǎn)發(fā)就需要有各個節(jié)點容器對應(yīng)的ingress veth pair網(wǎng)卡的mac地址才能做轉(zhuǎn)發(fā)是吧,好的那我們來看下ingress的ns空間中的fdb(linux bridge forward db)信息。
- 查看b節(jié)點上web_ingress_lb.1容器的mac地址信息
這樣一來即使容器的副本沒有落到host上我們?nèi)钥梢酝ㄟ^這種轉(zhuǎn)發(fā)方式來訪問到服務(wù)。這應(yīng)該就是routing mesh吧!
5>Service的各個容器會將dst port為8080的數(shù)據(jù)的dst port轉(zhuǎn)換成80,從而訪問到真實的服務(wù)。
可以看到一個請求到主機端口8090之后, 數(shù)據(jù)包的流向如下所示:
主機端口8090 => Ingress-sbox-VIP:8090 => 容器Ingress-sbox => IPVS分發(fā)到containers。
大家可以看到訪問主機之后數(shù)據(jù)包流到了一個特殊的Sandbox容器里, 這個容器和我們的容器共享一個Ingress網(wǎng)絡(luò),通過Iptables和IPVS等重定向到了最終容器之上。 達到了服務(wù)在任何一臺主機的8090端口都可達的目的。
2. Internal Load Balancing
1)、部署一個service使用我們自己創(chuàng)建的mynet網(wǎng)絡(luò):
docker service create --name web_mynet --replicas=2 --network=mynet --publish 8080:80 httpd
部署的兩個容器分別處在a和c節(jié)點上:
--publish #--在這里的用意是將容器內(nèi)部的服務(wù)暴露到host上這樣我們就可以訪問這個services,一般情況下我們在swarm中部署service后容器中的網(wǎng)絡(luò)只有一張網(wǎng)卡使用的是docker0網(wǎng)絡(luò),當我們將服務(wù)發(fā)布出去后,swarm會做如下操作:
- 給容器添加三塊網(wǎng)卡eth0和eth1,eth2,eth0連接overlay類型網(wǎng)絡(luò)名為ingress用于在不同主機間通信,eth1連接bridge類網(wǎng)絡(luò)名為docker_gwbridge,用于讓容器能訪問外網(wǎng)。eth2連接到我們自己創(chuàng)建的mynet網(wǎng)絡(luò)
上,同樣的作用也是用于容器之間的訪問(區(qū)別于eth2網(wǎng)絡(luò)存在dns解析即服務(wù)發(fā)現(xiàn)功能)。 - swarm各節(jié)點會利用ingress overlay網(wǎng)絡(luò)負載均衡將服務(wù)發(fā)布到集群之外。
結(jié)合例子如下:
2)、查看web_mynet.1容器和mynet網(wǎng)絡(luò)命名空間的網(wǎng)卡情況:
$docker exec web_mynet.1.kammwchnoeend86w3e5pho88i ip add
上面的命令可以查看a節(jié)點上的容器的網(wǎng)絡(luò)有四張網(wǎng)卡eth0和eth1,eth2和lo,eth2網(wǎng)卡可以看出其
對應(yīng)的veth pair為mynet網(wǎng)絡(luò)中的veth0,eth1的網(wǎng)卡比較容易找到在host上對應(yīng)的veth pair
$ip netns exec 1-j6s2r8ahdh ip add
查看mynet網(wǎng)絡(luò)命名空間下的網(wǎng)卡情況。
$ip netns exec 1-j6s2r8ahdh brctl show
查看mynet網(wǎng)絡(luò)空間下網(wǎng)橋掛載情況可以看出veth0掛到了br0網(wǎng)橋上。
3)、查看web_mynet.1容器和ingress\ingress_sbox網(wǎng)絡(luò)命名空間的網(wǎng)卡對應(yīng)情況:
- 獲取mynet和ingress網(wǎng)絡(luò)的vxlan-id:
## 執(zhí)行如下命令查看mynet空間下vxlan0網(wǎng)卡所帶的vlan-id:
$ip netns exec 1-j6s2r8ahdh ip -d l show vxlan0
可以看mynet網(wǎng)絡(luò)下vlan-id 為4097,ingress網(wǎng)絡(luò)空間同樣操作可以得到vlan-id為4096
swarm-c節(jié)點上的情況也是差不多就不操作了,好了我們來畫下網(wǎng)絡(luò)連接的大致圖:
可以看到這里ingress_sbox和創(chuàng)建容器的ns共用一個ingress網(wǎng)絡(luò)空間。
4)、 Internal Load Balancing實現(xiàn)方式:
有兩種實現(xiàn)方式dns rr和vip形式,在dns rr 的情況下可能會存在一定是的問題,當容器重啟后dns的解析會存在一定時間的延遲。vip則是由vip+內(nèi)核ipvs來實現(xiàn)。docker swarm默認使用的是vip,這里就以vip的形式來解析。(想要了解dns rr的可以看文章后面的參考鏈接都是大牛寫的)
VIP形式下的流量路徑:
1> 同處于網(wǎng)絡(luò)mynet中的容器可以通過service域名或者VIP來訪問service;通過域名訪問時,容器會訪問docker engine中內(nèi)置的DNS服務(wù),從而獲取VIP。
2> CNM網(wǎng)絡(luò)模型中一個容器對應(yīng)一個sandbox,也即容器的net namespace。我們查web_mynet.1容器的sandbox中iptables的mangle表的配置情況:mangle表中OUTPUT鏈,將destIP==VIP的包標記fwmark。
操作流程如下:
通過busybox服務(wù)做dns解析,可以發(fā)現(xiàn)該服務(wù)后端掛載的容器和該服務(wù)對應(yīng)的
VIP地址。web_mynet服務(wù)對應(yīng)的VIP為10.0.0.6。
- 進入web_mynet.1容器的ns:
$docker inspect container_id/container_name | grep -i sandbox
$nsenter --net=SandboxKey(/var/run/docker/netns/xxxx) sh
3>web_mynet.1的 Sandbox中會設(shè)置kernel中的LVS模塊,將標記fwmark的包LB到各個實際IP中,默認round-robin算法,VS/NAT方式。容器底層間通過overlay網(wǎng)絡(luò)互連通信。在web_mynet.1的ns中執(zhí)行如下獲取LB信息:
$ipvsadm -L
- 簡單的來說就是在web_mynet.1容器中定義好了web_mynet服務(wù)的vip數(shù)據(jù)包的標簽和LB,然后數(shù)據(jù)包通過容器本地路由從eth2接口出去,進入到mynet的ns中:
-
帶有具體目容器的MAC數(shù)據(jù)包進入mynet的ns后,由mynet網(wǎng)絡(luò)中的fdb來進行轉(zhuǎn)發(fā):
mynet-9.png
總結(jié)下:
在Internal Load Balancing也就是文中我們自建的mynet overlay網(wǎng)絡(luò)中,我們會看到創(chuàng)
建的service會同時應(yīng)用到兩個網(wǎng)絡(luò)環(huán)境里面去,為何要這樣呢?
原因是swarm自帶ingress不具備有服務(wù)發(fā)現(xiàn)的功能,而容器的生命周期又是不固定的,
service每次的消亡和啟用都會改變?nèi)萜鲀?nèi)部的ip地址以及vip地址,那么集群中服務(wù)之間
的通信勢必會造成問題,這里有人會說,要使多個service之間能夠互相通信可以將所有
的service都publish出去,然后通過routing mesh 訪問,這樣是沒錯也能行得通,但是存
在一個缺點,那就是不安全,我們僅僅只需要的是將最終提供服務(wù)的端口publish即可。
那么不publish所有的service需要做到以下幾點:
讓service通過簡單的方法訪問其他service
當service副本的ip發(fā)生變化時,不會影響訪問該service的其他service
當service的副本數(shù)發(fā)生變化時,不會影響訪問該service的其他service
這其實就是服務(wù)發(fā)現(xiàn),docker swarm提供了這些功能將LB和服務(wù)發(fā)現(xiàn)集成在一起,通過服務(wù)發(fā)現(xiàn)service,使用者不需要知道service運行在哪里,ip是多少有多少個副本,就能實現(xiàn)集群內(nèi)service與service的通信以及LB。
這里我理解的是ingress是單單提供LB實現(xiàn)routing mesh而mynet是服務(wù)發(fā)現(xiàn)和LB的結(jié)合
所以上文中Internal Load Balancing中的數(shù)據(jù)流應(yīng)該分成兩種情景如下:
1、當一個外部請求到主機端口8080之后, 數(shù)據(jù)包的流向如下所示:
主機端口8080 => Ingress-sbox-VIP:8080 => 容器Ingress-sbox => IPVS分發(fā)到containers。
2、處于 同mynet網(wǎng)絡(luò)的service內(nèi)部通信時:
處于 同mynet網(wǎng)絡(luò)的test service(busybox容器)發(fā)起訪問web_mynet域名的請求=>請求轉(zhuǎn)發(fā)到docker engine內(nèi)置的DNS解析web_mynet的vip=>web_mynet(容器)在其ns中將
VIP數(shù)據(jù)包打上標簽,并通過ipvs來負載到后端對應(yīng)的容器=>數(shù)據(jù)包通過vip地址路由到
mynet的ns,由mynet中的fdb來做轉(zhuǎn)發(fā)走tunnel出去。
文章中存在不足之處希望路過的大牛多踩踩^^!
參考:
https://zhuanlan.zhihu.com/p/25954203
http://www.lxweimin.com/p/4433f4c70cf0