一、集群之間的網絡
之前有搭建過wordpress應用,其中wordpress運行在manager節點上,mysql服務運行在worker節點上,它們之間的運行時都制定了對應的網絡overlay,但是當時只是在manager節點上創建了這個overlay網絡,worker節點上並沒有創建,但是當Swarm集群的manager節點啟動了mysql服務並且運行在worker節點上后,worker節點上竟然也有了這個overlay網絡,這是為什么呢?
這就涉及到了Swarm的網絡RoutingMesh,它有兩種體現的方式分別是:Internal和Ingress,其中Internal的通信通過overlay網絡;Ingress是如果服務有綁定接口,則此服務可以通過任意Swarm節點的相應接口進行訪問。
二、 Internal
1、創建overlay網絡
首先先在Swarm的集群中創建一個overlay的network:
[root@centos-7 ~]# docker network create -d overlay demo
然后查看:
[root@centos-7 ~]# docker network ls NETWORK ID NAME DRIVER SCOPE ... xcjljjqcw26b demo overlay swarm ...
2、啟動服務
在這個manager節點上利用demo這個網絡啟動一個服務
[root@centos-7 ~]# docker service create --name whoami -p 8000:8000 --network demo -d jwilder/whoami g0vvgklakfoqsjvy0fd1zefjo [root@centos-7 ~]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS g0vvgklakfoq whoami replicated 0/1 jwilder/whoami:latest *:8000->8000/tcp [root@centos-7 ~]# docker service ps whoami ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS pk15budjscr7 whoami.1 jwilder/whoami:latest centos-7 Running Preparing about a minute ago
當我們啟動這個服務后,可以嘗試去訪問它:
[root@centos-7 ~]# curl 127.0.0.1:8000 I'm 9842878ab31b
另外,我們再開啟一個服務:
[root@centos-7 ~]# docker service create --name test -d --network demo busybox /bin/sh -c "while true;do slepp 3600;done"
注意,此時我們這個服務是在worker節點節點上運行的。
[root@centos-7 ~]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS rqdbc7dtdh77 test replicated 1/1 busybox:latest g0vvgklakfoq whoami replicated 1/1 jwilder/whoami:latest *:8000->8000/tcp [root@centos-7 ~]# docker service ps test ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 0wqwt54vbiue test.1 busybox:latest localhost.localdomain Running Running 3 minutes ago
3、不同節點上服務之間的通信
如果此時進入到worker節點的test服務容器中,去和manager節點上whoami服務的容器通信們是否可行呢?
- 進入test服務的容器中
[root@localhost _data]# docker exec -it d161e7c46a8c /bin/sh / # ping whoami PING whoami (10.0.1.33): 56 data bytes 64 bytes from 10.0.1.33: seq=0 ttl=64 time=33.921 ms 64 bytes from 10.0.1.33: seq=1 ttl=64 time=0.088 ms
可以看到,這是可行的,並且返回的地址是10.0.1.33,這是不是就說明了manager節點服務容器的ip是這個呢?我們查看一下。
- 進入whoami服務容器中
[root@centos-7 ~]# docker exec -it 9842878ab31b /bin/sh /app # ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 266: eth1@if267: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP link/ether 02:42:0a:00:00:15 brd ff:ff:ff:ff:ff:ff inet 10.0.0.21/24 brd 10.0.0.255 scope global eth1 valid_lft forever preferred_lft forever 268: eth2@if269: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff inet 172.18.0.3/16 brd 172.18.255.255 scope global eth2 valid_lft forever preferred_lft forever 270: eth0@if271: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP link/ether 02:42:0a:00:01:22 brd ff:ff:ff:ff:ff:ff inet 10.0.1.34/24 brd 10.0.1.255 scope global eth0 valid_lft forever preferred_lft forever
顯然這里面並沒有10.0.1.33,這是怎么回事呢?我們知道scale參數可以將容器水平擴展,不同的容器都有一個ip,為了保證這些服務的ip不變,這里使用了虛擬的ip,這個ip地址是不會改變的,比如現在利用scale多創建幾個whoami服務的容器,然后再進行通信。
- 創建多個whoami服務的容器
[root@centos-7 ~]# docker service scale whoami=3 whoami scaled to 3 overall progress: 3 out of 3 tasks 1/3: running [==================================================>] 2/3: running [==================================================>] 3/3: running [==================================================>] verify: Service converged
- 再次測試
再在worker節點的test服務容器中進行測試:
[root@localhost ~]# docker exec -it 638b /bin/sh / # ping whoami PING whoami (10.0.1.33): 56 data bytes 64 bytes from 10.0.1.33: seq=0 ttl=64 time=49.712 ms 64 bytes from 10.0.1.33: seq=1 ttl=64 time=0.072 ms 64 bytes from 10.0.1.33: seq=2 ttl=64 time=0.072 ms
可以看到雖然whoami服務已經啟動了3個容器,但是這個虛擬的ip還是沒有變化,那么怎么查看這個虛擬ip對應的真實ip呢?可以通過以下命令查看:
[root@localhost ~]# docker exec -it 638b /bin/sh / # nslookup whoami Server: 127.0.0.11 Address: 127.0.0.11:53 / # nslookup tasks.whoami
我們也可以這樣去驗證,只需要在本地上去訪問這個服務,看它返回的主機信息:
[root@localhost ~]# curl 127.0.0.1:8000 I'm 6f5862805bcf [root@localhost ~]# curl 127.0.0.1:8000 I'm d274e2b20fd8 [root@localhost ~]# curl 127.0.0.1:8000 I'm 9842878ab31b [root@localhost ~]# curl 127.0.0.1:8000 I'm 6f5862805bcf
這就是借助overlay網絡實現的Swarm集群容器之間通信的模式。
4、總結
兩個服務whoami和test之間是建立了虛擬的ip進行聯系,但是一個虛擬的ip可能就對應一個服務中多個容器的ip,它們之間是通過lvs(Linux Virtual Server)技術來實現的,從而達到負載均衡的效果。
三、Ingress
1、什么是Ingress?
在Swarm集群中可能同一個service部署在不同的節點上,那么如果Swarm中有的節點沒有 這個service,是否這個節點就不能訪問這個服務呢?
現在先啟動兩個服務:
[root@centos-7 ~]# docker service create --name whoami -p 8000:8000 --network demo -d jwilder/whoami n9i8qe8obf5ke73ptbuejl49o [root@centos-7 ~]# docker service scale whoami=2 whoami scaled to 2 overall progress: 2 out of 2 tasks 1/2: running [==================================================>] 2/2: running [==================================================>] verify: Service converged
再看看這兩個啟動在節點上:
[root@centos-7 ~]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS n9i8qe8obf5k whoami replicated 2/2 jwilder/whoami:latest *:8000->8000/tcp [root@centos-7 ~]# docker service ps whoami ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS jb4sodzzwoke whoami.1 jwilder/whoami:latest centos-7 Running Running 49 seconds ago ajnie812nqsg whoami.2 jwilder/whoami:latest localhost.localdomain Running Running 20 seconds ago
可以看到兩個服務啟動在不同的節點上,一個在manager節點(centos-7),一個在worker節點(localhost.localdomain),我們現在manager節點上訪問:
[root@centos-7 ~]# curl 127.0.0.1:8000 I'm ae433c63efaf [root@centos-7 ~]# curl 127.0.0.1:8000 I'm d20d6ad9f7ae
可以看到manager節點上竟然兩個都能訪問,不是另一個部署在worker節點上了嗎?那么再去worker節點上測試:
[root@localhost ~]# curl 127.0.0.1:8000 I'm ae433c63efaf [root@localhost ~]# curl 127.0.0.1:8000 I'm d20d6ad9f7ae
竟然也是一樣的,這就是Swarm中的Ingress網絡,它可以將服務端口暴露在Swarm中的各個節點上。
2、Ingress的實現
- 轉發規則
我們可以看看worker節點上沒有存在的服務它是怎么訪問的,如果訪問它內部是怎么轉發的。
[root@localhost ~]# iptables -nL -t nat ... Chain DOCKER-INGRESS (2 references) target prot opt source destination DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8000 to:172.20.0.2:8000 RETURN all -- 0.0.0.0/0 0.0.0.0/0 ...
我們可以看到如果不存在的就會轉發到172.20.0.2::8000這個這個地址上去,那么我們看看它自己的ip:
[root@localhost ~]# ip a ... 12: docker_gwbridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP link/ether 02:42:ab:b2:f4:f0 brd ff:ff:ff:ff:ff:ff inet 172.20.0.1/16 brd 172.20.255.255 scope global docker_gwbridge valid_lft forever preferred_lft forever inet6 fe80::42:abff:feb2:f4f0/64 scope link valid_lft forever preferred_lft forever ...
我們沒有找到那個地址,但是找到了docker_gwbridge,可以看到兩個ip處於同一段,那么172.20.0.2應該也連接上docker_gwbridge:
[root@localhost ~]# brctl show bridge name bridge id STP enabled interfaces br-2d6d1e198a6c 8000.0242deeae757 no br-8abcc5cd875c 8000.02424eb653eb no docker0 8000.02424a3fbec6 no docker_gwbridge 8000.0242abb2f4f0 no
veth1b22ec2 veth6839368 virbr0 8000.525400dec34c yes virbr0-nic
可以看到它有兩個interface,但是哪一個才是 172.20.0.0使用的呢?那么我們現在可以從這個網絡連接的容器着手。
[root@localhost ~]# docker network inspect docker_gwbridge [ ... "Containers": { "ae433c63efafd9376ff82699d127563e780ae73caebcb604c3b0966000236e79": { "Name": "gateway_fe0a7f060fb9", "EndpointID": "6eddc5ac0721d319e11eb475c3f937ccf78713ed56f89fef563c13333d2526b4", "MacAddress": "02:42:ac:14:00:03", "IPv4Address": "172.20.0.3/16", "IPv6Address": "" }, "ingress-sbox": { "Name": "gateway_ingress-sbox", "EndpointID": "08639c027efb5c9e51aaf742587c9ae944d1d82275f92ef4f2d1f77965194656", "MacAddress": "02:42:ac:14:00:02", "IPv4Address": "172.20.0.2/16", "IPv6Address": "" } } ... ]
可以看到它有兩個容器,其中ingress-sbox容器的ip就是我們需要尋找的。它是什么呢?它是一個Network Namespace,那么也就是請求本轉發到這里面來了。
- 進入ingress-sbox
我們可以先查看docker中所有的Network Namespace:
[root@localhost ~]# ls /var/run/docker/netns 1-lg0vf65qxp 1-xcjljjqcw2 fe0a7f060fb9 ingress_sbox lb_xcjljjqcw
然后通過下面的命令進入:
[root@localhost ~]# nsenter --net=/var/run/docker/netns/ingress_sbox
查看ip:
[root@localhost ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP link/ether 02:42:0a:00:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.0.0.3/24 brd 10.0.0.255 scope global eth0 valid_lft forever preferred_lft forever inet 10.0.0.26/32 brd 10.0.0.26 scope global eth0 valid_lft forever preferred_lft forever 13: eth1@if14: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:14:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet 172.20.0.2/16 brd 172.20.255.255 scope global eth1 valid_lft forever preferred_lft forever
這個名稱空間中的ip就是我們要找的。
當被轉發到這個名稱空間后它又是怎么做的呢?我們先看看它的轉發規則:
root@localhost ~]# iptables -nL -t mangle Chain PREROUTING (policy ACCEPT) target prot opt source destination MARK tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8000 MARK set 0x10b Chain INPUT (policy ACCEPT) target prot opt source destination MARK all -- 0.0.0.0/0 10.0.0.26 MARK set 0x10b Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination
可以看到紅色的部分,當轉發8000端口時MARK set 0x10b,接下來執行如下的命令:
[root@localhost ~]# ipvsadm -l IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn FWM 271 rr -> 10.0.0.27:0 Masq 1 0 0 -> 10.0.0.28:0 Masq 1 0 0
實際上在這個名稱空間中也是講這個端口進行了轉發,轉發到了上面紅色字體的地址和8000端口,那這兩個地址是什么呢?其實就是Swarm中的所有節點的8000端口,這也就是Ingress的作用了。
3、總結
當我們訪問訪問本地8000端口時,只要我們這個節點處於Swarm集群中,不管服務部署到那個節點都能訪問,只要端口相同即可。我們本地的請求會被轉發到Ingress_sbox這個Network Namespace中,在這個名稱空間中再通過lvs轉發到具體服務容器的ip和8000端口中去。