什麼是docker網絡呢?總的來說,網絡中的容器們可以互相通信,網絡外的又通路不了這些容器。具體來說,在一個網絡中,它是一個容器的集合,在這個概念裡面的一個容器,它會通過容器的ip直接去通信,又能保證在這個集合外的一些容器不能夠通過這個容器ip去通信,能做到網絡隔離。網絡這個概念是由網絡的驅動去建立、管理的。網絡的驅動又分為全局的和本地的,全局的意思是這個網絡可以跨主機,沒必要說我的兩個容器非要在一個主機上才能通過這個網絡去通信,我可以在不同主機上還是通過容器的ip互相通信。

首先我們看一個本地網絡的圖,這個圖中有一台主機,上面跑了四個容器,分别分布在兩個網絡裡面,一個是net1,一個是net2。net1裡面的兩個容器是可以互相通過自己的ip通信的,但是net1裡面的容器和net2裡面的容器又是隔離的,這樣就是docker的一個網絡概念。
全局網絡意思是,一個網絡裡面的容器是可以跨主機的。c1的容器可以和主機2上的c3容器,通過它們ip直接通信,而不用管所在的主機在哪個地方。這個圖裡面我們還可以看到一個細節,同一個容器可以加入到兩個網絡裡面,比如c4它可以和網絡1裡面的兩個容器通信,也可以和網絡2裡面的c5進行通信,這樣我們就可以實作更加複雜的一些組合。比如c5是一個資料庫的容器,我不想讓這個資料被前面的web應用或者其他的應用去通路到,隻想讓我的中間鍵應用去通路到。這樣就可以通過讓中間鍵應用加入到一個背景網絡、一個前台網絡的組合來實作這種互聯和網絡的控制。
在1.10版本增加了網絡服務發現,什麼意思呢?比如,我的一個網絡有兩個容器,我們可以直接通過ip進行通信,但是我的容器它可能某一段時間跪掉了,它可能在跪掉之後再去重新開機,我這時就不知道它的ip了,或者它一關閉之後,我這邊感覺不到,這樣就要觸發很多複雜的一些處理流程。在1.10時它去做這樣的一件事,它在每個容器裡面内置了一個dns的伺服器,在伺服器run起來之後,會寫一個容器名,或者是這個容器的一個别名和它ip的一個解析。外部容器去通路我的db容器時,它能夠通過db這個名字來解析到db容器的ip位址,這樣在db容器重新開機之後,這個解析會動态去調整,就能實作一個服務的發現。
docker預設的會有一些網絡驅動,它本地的網絡驅動有這幾種。none網絡驅動意思是,我建立一個容器,但是沒有它的一些網絡配置,單獨一個命名空間,它不能和其他容器通信,也不能通路外網。host的網絡驅動和主控端共享網絡棧、它和主控端看到同樣的接口,包括它自己暴露的端口,都是和主控端在同一個網絡空間裡面。
bridge網絡驅動,它是一個本地的網絡驅動,上圖主機上有兩個bridge網絡,它實際上是通過linux的bridge去驅動的,通過容器裡面挂載一個網卡,對應的是在主控端上一個veth,容器的請求會直接轉發到這個veth上面,bridge上面會對這個請求進行洪泛,它就能到達這個網絡裡面的另外一個容器。同理,另外一個網絡,它跟這個網絡裡面的容器沒有在同一個網橋上面,是以他們之間是互相不能通信的。bridge驅動還會實作另外一個功能,比如說c1的容器,他要通路公網,它通過bridge出去的時候,他最終到達主控端的接口,他會做一個網絡位址的轉換,把容器的ip轉換成主控端的ip,這樣就能通路公網。或者我的容器可能希望服務映射到外面去,讓别的機器或别的公開服務可以通路到,他可以做一個dnat一個規則,比如我的容器是nginx,它裡面是80端口,我想映射到主機的一個9080,可以直接通過主機的9080去通路,他可以去做一個dnat的配置,這是bridge的驅動。
docker預設還有一個overlay的驅動,它主要是去實作跨主機的一個通信,它會在主機之間通過vxlan的方式打一個隧道,實作我的主機上的容器去通路主機上的容器,最終會轉換成一個vxlan裡面的通信。
沒有跨主機網絡時,比如一個應用有兩個服務,兩個服務不可能部署到同一個機器上,這樣沒辦法做到高可用,所在的主機一挂,上面所有的服務都挂了。如果讓它部署到兩個主機上面,但是兩個主機上面這個容器的ip在主機外面是看不到的。是以隻能把容器的ip轉換成了一個主控端的一個ip,或者主控端的一個通路端點,才能讓外部的主機通路。是以,以前的方式是通過端口映射,把容器的一個端口映射到主機的一個端口,讓另外一個主機和這個主機映射到這個容器上去通信。這樣會帶來哪些問題?首先我通過端口映射,比如說我映射到8090端口,我另外再想起一個容器。他就得映射到另外一個端口,因為主控端上隻有這一個8090端口,它不可能說共享這個端口,是以一個主機上去起容器的時候,需要管理端口的清單。映射出去之後是主控端的一個端口,這樣這個端口就是對外暴露的,可能要去控制一下,這個端口隻能說主機要去通路,這樣要去做一些安全的配置,是以這樣的整個架構是非常複雜的。在docker1.9之後,增加了跨主機的網絡,它就可以直接通過容器的ip去通信,通過這個外部跟它是隔離的,這樣又能做到安全,我就算再起一個容器,它容器的ip端口是不會沖突的。
封包模式,比如overlay驅動,它是用vxlan的協定去把容器之間的請求封裝成vxlan的請求,将鍊路層的以太網包封裝到udp包中進行傳輸。
這裡有一個簡單的圖來介紹封包模式,首先舉個例子,從c1去通路主控端上c2的容器,先通過c1發出這個包之後,它首先進行封包,要查找中心化存儲c2是在哪個主機上的,查到的是這個主機上,就把這個請求的包封裝成主控端之間的包,把這個包送達到對應主控端上,再通過一定的約定去解包,最終轉發到另外一個主機上的一個容器。其缺點是對帶寬的損耗比較大,因為看到這裡去封包,它肯定是把這個容器的包上面又加了一層包,這個包上面肯定會有一些自己的占用,一般像封包模式,mtu最終都會小于主控端之間的mtu,它還會造成一些資源占用。比如說在這個地方去封包的時候,它可能會占用cpu去做一下封包操作,還會占用cpu去做解包的操作,是以會增加主機上的負載,但是它的好處是對基礎設施要求比較低,它隻需要兩個主控端之間可以三層互通。
下面我們以docker的overlay驅動來介紹下封包的實作,它的封包方式是基于vxlan。上圖是它的針格式,vxlan内部把一個二層的包、鍊路層的一個請求封裝成一個vxlan的一個包,這裡面會有一個約定的資訊,比如vxlan id,還有一個外部的udp的頭,最終會把這個包通過udp發往對應的主機上面去,對應的主機再做vxline的解包。docker還會做ip的位址管理和網絡資訊同步。
如上圖,比如,主機1上的容器想要通路主機2的容器,首先這個機器上是沒有主機2的ip位址,它出了機器之後到達路由器,再通過路由器上的路由表,去把對應的請求網端轉發到對應的主機2上面,主機2上面是有這個容器的ip,是以它就能夠做到這個容器的跨主機通信。它的好處在于它沒有封包,是以它的性能很好,但是它對于基礎設施有一些要求,比如我的基礎設施要支援、能夠配置這個路由表,如果用linux路由要支援二層的轉發。
vpc網絡驅動是在阿裡雲的vpc基礎上,能夠實作容器互通的一種方案。首先在docker dm啟動時,或者是在建立網絡時,會從每個機器的一個中心化存儲裡面,拿到兩個自己的網端,比如這個機器上的網端,比如左邊主機上的網端就是172.19.1.1/24,它通過阿裡雲的openapi去配置這個轉發表,把這個網端的位址都轉發到這個主機上面。另外一個主機啟動時,把它拿到一個可用的網端,去配置阿裡雲的一個路由表,把這個網端的請求轉發到自己的主機上面,比如說我這邊的一個容器想要去通路主機2上c2的容器時,它這個包最終到達,通過vswitch到達阿裡雲vpc的vrouter時,通過查詢路由表把實時的請求包轉發到主機2上面,最終實作c1和c2的通信。
封包模式的優點是對基礎設施的要求比較低,但是它性能損耗比較大,路由模式是沒有封包的,是以它性能比較好,但是對基礎設施有一定要求,比如說要支援路由表,或者要支援二層的轉發。
容器服務上面現在有兩種叢集,一種是阿裡雲的ecs叢集,這種叢集的節點都是在阿裡雲的同地域或者同一個vpc下面的ecs,或者是混合雲的叢集,阿裡雲的混合雲叢集節點是在使用者自己建立的機房,或者分布在不同的雲供應商上面。
在阿裡雲的ecs叢集上面,網絡架構是這樣的,容器之間去通信,還是通過overlay驅動,它的好處是對基礎設施要求比較低,是以它隻需要主機之間互通就可以了,容器去通路外網,或者容器想要暴露端口給外網就通過一個gateway_bridge的網橋、本地的一個驅動,這個是供容器通路外網和容器的端口映射。在ecs叢集上面,vpc網絡是通過vpc驅動的路由表把對應的請求轉發到容器所在機器上面,它去通路外網和做端口映射也是和剛才所介紹的overlay是同樣的。
混合雲叢集是結合阿裡雲的vpc技術去實作容器之間的網絡互通。在混合雲叢集裡面是通過專線的方式,把對應的資料中心的請求轉發到我的資料中心裡面,或者把資料中心的請求通路vpc的,通過專線的一個路由也可以轉發到vpc裡面,再通過路由表把容器的請求轉發到對應的主機上面,最終實作的方式是把我vpc裡面的容器和資料中心裡面的容器互通。
剛才我們介紹的都是容器之間通信,我們可以不通過端口映射的方式做到端口不沖突,怎麼能夠做到我想要外部通路時,也能做到就一個負載均衡,怎麼能做到一個機器上面部署多個容器,然後端口不沖突呢?這時候阿裡雲就提供了這樣的一個負載均衡方案。比如說我的網站有兩個域名,一個是購物的域名,一個是付款的,但是我總共就三台機器,每個服務要做到高可用,是以起了很多的容器,這樣在同一個主機上就要去處理,怎麼讓它保障單個不沖突,容器服務是提供一個這樣的方案,我們在叢集裡面會部署一套路由的一個服務,首先請求,比如說我的公司域名的泛解析會解析到我的路由服務上面去,路由服務再通過請求的域名host,去把這個請求直接轉發到容器上面,最終實作我的一個請求可以轉發到容器上面,但是我的容器并沒有映射出端口,也不需要去解決端口沖突的問題。