《菜農升職記》之 Docker網路

蔡不菜丶發表於2022-02-27

大家好呀,我是小菜~

本文主要介紹 docker 網路

微信公眾號已開啟,小菜良記,沒關注的同學們記得關注哦!

剛結束完一個需求的小菜農剛想開啟公眾號《菜農曰》看看部落格劃劃水,叮叮又響了!

“小菜農,這個測試環境的伺服器連線資訊,你登上去通過docker部署下你剛剛提交的分支測試下哈!”

導師程立給小菜農發來了訊息

"docker是個啥玩意?之前不都是直接在伺服器找個目錄上傳然後 nohup java -jar ... & 一套組合拳下來嗎?"

小菜農頓時又自閉了,自從來到了職場真是處處碰壁!想想在學校還是三好學生真是唏噓不已!

“等等!我記得公眾號《菜農曰》寫了一篇關於 docker 的,趕緊翻出來看看”

Docker上手,看完覺得自己又行了!

許久之後,小菜農方從文章中回過神來,“這下不得拿捏了!”。他開啟伺服器輸入 docker ps 試了下指令

“有點意思啊!”他按照文章中的教程開始部署自己的應用專案。先是編寫了自己應用的 dockerfile,然後通過 docker build 構建出自己的映象,最後執行 docker run來執行自己的映象,一整套下來一氣呵成,行雲流水!然後便在瀏覽器測試了下自己的功能內容,測試通過後。小菜農便陷入了思考,docker 是一個容器,可以內嵌一個作業系統,每個容器之間是相互隔離的,那為啥我可以從外界訪問容器內的內容?容器之間是如何通訊的?又怎麼建立自定義網路?

幾個問題想的小菜農頭都大了!放棄是不可能放棄的,沒準這以後可以成為自己升職加薪,走上人生巔峰的墊腳石之一呢!從這裡可以看出小菜農還是很有鬥志的嘛~

docker 容器在安裝的時候會自動在 host 上建立三個網路,小菜農看到了這句話,那要怎麼檢視這三個網路了,通過help指令看看會不會有所發現?他快速地在終端敲入docker --help

“哦豁,果然有!network 不就是我想要的嗎!”。機智的小菜農開始順藤摸瓜,繼而敲入docker network --help

ls 可以檢視docker 的網路,那我來試試”

image-20220220191636020

終端上正如小菜農所料,出現了他想要看的內容。“bridge、host、none?” 小菜農還是有些網路基礎的,前兩個看名稱都有點熟悉,但是 none 是個啥玩意?什麼都沒有的網路嗎?。小菜農通過一番查詢,沒想到還真是,不由得佩服命名的開發者,這可真讓人顧名思義~ 查詢發現,掛在這個網路下的容器除了 lo,沒有任何網路卡

我們啟動容器是通過 docker run來執行的,這個指令後面我們可以附加引數,通過 --network = none就可以指定該容器是使用 none 網路~

“那麼這樣一個封閉的網路有啥用,都上不了網”,小菜農嘀咕道

沒錯,就是要上不了網,有些特殊的應用場景需要將網路封閉起來,往往這種場景意味著安全性要求很高,因此需要進行隔離,這種情況就可以放到 none 網路防止被人侵入。

“既然是特殊場景才需要,那我目前應該還接觸不到特殊場景,pass pass!讓我康康 host 是什麼網路”

小菜農知道了可以通過 --network指定網路型別,他試著啟動了一個網路型別為host 的nginx來檢視

小菜農看到這個網路配置不禁陷入了沉思,這怎麼似曾相似?他將終端命令列往上滾了幾頁,終於發現為啥眼熟了!

原來他在上面的時候曾經輸入 ip l 檢視了宿主機的網路,你可以發現這兩個配置是一模一樣的!那就說明連線到 host 網路的容器共享 Docker host 的網路棧,容器的網路配置與宿主機的配置完全一樣。那這樣子的話直接使用 Docker host 網路的最大好處就是效能了。如果容器對網路傳輸效率有較高要求的時候,就可以使用 host 網路,也相當於是使用宿主機的網路,那麼這樣做的缺陷是什麼?那就是埠會衝突,在宿主機上已經使用的網路,容器就不能再使用了!

小菜農覺得應該沒那麼簡單,又查了下 host 網路的使用場景,不一會就有所發現了,使用 host 網路的另一個用途就是可以讓容器直接配置 host 網路,比如某些需要跨 host 的網路解決方案,其本身也是以容器的方式執行的,這些方案需要對網路進行配置,比如管理 iptables 之類的場景。

“真是丈母孃看郎,越看越愛~ ”小菜農已經有點迷上了docker,越深入才發現越有意思。“還剩下 bridge 網路型別,讓我康康!”

bridge 是一種橋接模式的網路,橋接顧名思義就是一端在這裡另一端在那裡。小菜農又大膽猜測了,那肯定就是一端在容器上,一端在宿主機上!接下來便進行了驗證

小菜農想起了一個命令 brctl,該命令brctl 可用於設定、維護和檢查linux核心中的乙太網網橋配置。

無法使用 brctl 命令的童鞋可以使用以下命令安裝

yum install -y bridge-utils

他通過brctl show檢視宿主機的網橋配置,發現有個命為 docker0 的網橋,然後他通過啟動一個容器繼續觀察網橋變化:

小菜農發現 interface 的內容發生了變化!有一個新的網路介面 veth9b75794 掛在了 docker 0 上,而 veth9b75794 就是剛剛建立容器的網路卡,小菜農緊接著檢視了下剛建立容器的網路配置

容器中有個一個 eth0@if67 網路卡,小菜農會心一笑

“嘿嘿,被我發現了吧,要不是我會點網路,還真被這忽悠住了。剛剛上面檢視到的網路介面是 veth9b75794,而這裡是 eth0@if67,換成剛接粗網路的童鞋肯定一頭霧水,這兩個怎麼不一樣!實際上 eth0@if67veth9b75794 是一對 veth pairveth pair 是一種成對出現的特殊網路配置,而這就是我上面猜測的那樣,網橋就是可以把它們想象成由一根虛擬網線連線起來的一對網路卡,網路卡的一頭(eth0@if67)在容器中,另一頭(veth9b75794)掛在網橋docker0上,其效果就是將eth0@if67也掛在了docker0上

172.17.0.2/16 這個 IP 也就是宿主機配置的,小菜農檢視了下docker 的bridge配置

發現有個bridge網路配置的subnet就是172.17.0.0/16,並且閘道器是172.17.0.1。那麼這個閘道器就是 docker 0!小菜農在宿主機輸入 ifconfig' 進行驗證

而這時,小菜農的腦子裡浮現了一張圖

到這裡,小菜農已經熟悉了 docker 的三種網路型別的情況,便開始思考如何可以自定義網路,肯定存在很多場景使用者需要根據業務來建立自定義網路來滿足需求。在上面小菜農想要查詢docker的網路配置時有輸入過命令 docker network --help,輸出結果的時候小菜農有點印象其中好像有個 create 的命令,趕緊又輸入一遍進行確認:

果然,docker 已經支援使用者建立自定義網路。很快呀!小菜農便在終端輸入了建立網路的命令:

眼尖的小菜農一眼就發現了不對勁,建立網路的時候並沒有指定 DRIVER 呀!而這個時候建立出來的網路驅動預設就是 bridge,那就說明是否還可以建立其他網路驅動的網路?小菜農再一次進行了嘗試,但這次結果確認小菜農碰壁了

“難道只能建立 bridge 驅動型別的網路嗎?” 小菜農嘀咕。依舊不死心便上了某搜尋引擎進行結果查詢。查詢後發現原來Docker提供了三種 user-defined 的網路驅動,分別是 bridgeoverlaymacvlanoverlaymacvlan用於建立跨主機的網路!

“跨主機?那就是叢集咯!目前導師分配給我的伺服器只有一臺,看來沒法驗證了,只能等後續許可權大點再驗證了,嘿嘿!”,既然無法驗證其他兩種,那就先將 bridge 搞明白~ 小菜農壞笑了起來!

上面已經建立了一個bridge驅動型別的網路,那宿主機的網路配置應該發生了變化,小菜農輸入了 brctl show 進行檢視:

果然這個時候多了一個名為 br-76c202387b0c 的網橋,通過ifconfig可以發現網段也已經分配了一個:

而這個應該就是新網路的閘道器了~ 小菜農真是越來越熟練起來,看來學習真能使人自信!

一切都如小菜農所料,172.18.0.1 就是 Docker 自動分配的 IP 網段

“但是如果自定義的網路多了起來,分配的網段肯定也是隨之增加,那由 docker 分配的網段肯定是隨機,不方便記憶,能不能自己指定 IP 網段”,小菜農突發奇想,輸入 docker network create --help 會不會有所發現?

看到這結果,小菜農忍不住笑了起來,真有我的!這 subnetgateway 不就是我想要的嗎!~

這不就成了嗎!而且宿主機上也有了新的網路,網段為 172.10.0.0/16,閘道器為 172.10.0.1

既然建立了自己的網路,小菜農頓時成就感滿滿,這不得趕緊啟動一個容器來試試自己的網路!

可以看到使用自己配置網路啟動的容器,分配到的 IP 地址是 172.10.0.2/16,“既然容器的 IP 都是docker 自動從 subnet中分配的,那我能不能指定一個靜態IP呢?”小菜農又提出了疑問,那麼只能再祭出老套路 --help 進行解決了!小菜農手腳麻利的敲下 docker run --help | grep 'ip' 檢視結果

小菜農笑容逐漸變態,沒想到自己掌握了 help 神器!看來可以通過 --ip xxx來指定自己想要的IP,敲命令的手法逐漸嫻熟起來,這不,一會功夫結果就出來了:

到目前為止,小菜農已經啟動了三個容器

而網路之間的拓撲關係也忽然而出,小菜農連忙動手畫了起來,以免忘記!

小菜農看著圖發呆了一會,這圖其實已經挺清晰了,nginx-customnginx-ip 之間肯定能夠互相連通,小菜農驗證一下猜想

結果是對的,那 nginx-default 是否能和其他兩個容器相通呢?驗證一下就知道了!

由此可見同一網路中的容器、閘道器之間是相通的,而屬於不同網橋之間的容器是不能互相通訊的。難道這樣子就結束了嗎?兩個不同網路之間的容器難道真的不能相通了?小菜農不甘心就這樣放棄,盯著螢幕一陣發呆,“有了!加個路由的話,那麼兩個網路之間應該就能通訊了吧!” 如果 host 上對每個網路都只有一條路由,同時作業系統上開啟了 ip forwarding,那麼 host 就會形成一個路由器,掛接在不同網橋上的網路就能夠通訊!

小菜農通過ip r 檢視宿主機上的路由表

可以看到 172.17.0.0/16172.10.0.0/16 兩個路由都已經定義好了,下一步就需要看下 ip forwarding 是否已經開啟,小菜農快速的敲下指令 sysctl net.ipv4.ip_forward

可以看到 ip forwarding 也已經開啟了。那這個時候只需要為 nginx-default 容器新增一塊 c-custom的網路卡就可以通訊了!萬事俱備,只欠東風~

小菜農依舊通過熟悉的配方檢視新增網路卡的步驟

瞭解之後便開始動手操作

“nice,沒有出錯,這下應該可以通訊了吧!” 小菜農嘀嘀咕咕,緊張的手顫抖的心繼續敲下 ping 命令進行驗證

瞪大了眼,第一次這麼有成就感,沒想到真的被自己折騰出來了!小菜農激動的差點站了起來,恨不得大聲笑出來,但他知道,這只是自己成長的第一步,自己已經成功的跨出去了!平復下了激動的心,得出一個自己剛剛實驗出的結論

兩個容器要能通訊,就必須要有屬於同一個網路的網路卡,當滿足這一條件後,容器就可以通過 ip 進行互動了

結論來之不易,小菜農趕緊在自己的小本本上記錄下來!嚐到甜頭的小菜農繼續在網上進行搜尋,看是否存在其他可以在容器間通訊的方式,畢竟用 IP 進行通訊還是屬於比較基礎的~ 很快,小菜農便找到了答案,有的! 容器間可以通過是那種方式進行通訊

  • IP
  • Docker DNS Server
  • joined

IP 通訊自己已經實驗過了,接下來就要嘗試其他兩種方式。Docker DNS Server ?看解釋是通過 容器名的方式,在上面建立的容器,小菜農都已經指定了容器名,接下來只要進行實驗就行了

果然是可以的!可以通過容器名進行訪問,那就意味這可以不用記憶那麼長一串的 IP 了!

前兩種方式都已經實驗過了,看到 joined 小菜農又有點懵了,加入 ? 小菜農繼續往下看了解釋

joined 容器是另一種實現容器間通訊的方式

joined 容器非常特別,它可以使兩個或多個容器共享一個網路棧,共享網路卡和配置資訊,joined 容器之間可以通過 127.0.0.1 直接通訊

“還真神奇,docker中處處充滿了驚喜呀!”,小菜農隨後便進行了嘗試

這邊值得注意的是,兩個容器共享同一個網路棧,那麼埠就不能衝突,因此啟動的時候需要分離埠

到這裡,小菜農不由得心滿意足了,剛要退出伺服器開始繼續工作,卻被自己的網頁吸引過去了,網頁上開著正是小菜農剛剛啟動的docker專案。“咦,外界是怎麼與容器進行連線的?” 小菜農再次提出了疑問,容器如何訪問外界?外界又如何訪問容器? 看來還是得繼續學習(划水)呀!

小菜農再次切換到伺服器頁面,嘗試從容器內進行訪問外界

其實不用嘗試,小菜農也清楚,容器肯定能訪問到外界,不然外部介面如何呼叫~ 但是為什麼能訪問到呢?小菜農檢視了宿主機的 iptables 規則

可以看到有這麼兩條規則,一條是 docker 0,一條是小菜農剛剛自定義的,那它是什麼意思呢?小菜農又犯迷糊了,看了旁邊的肖老大,這不有現成的大佬可以問嗎!肖老大有些好奇,小菜農這是接到啥需求了,怎麼會問到這個問題,不過也沒多想,新人總要照顧好的不是!便開始巴拉巴拉解釋起來,小菜農也聽得雲裡霧裡,“這樣說明白了嗎?”。“啊?哦!差不多知道這個意思了,嘿嘿,謝謝肖老大”,“不客氣不客氣,下次遇到不會記得還來找我哈”,肖老大略微粗獷的嗓門,讓小菜農倍感溫馨!總結了下肖老大的解釋:如果網橋 docker 0 接收到來自 172.17.0.0/16 網段的外出包,就會把它交給 MASQUERADE 處理,而 MASQUERADE 的處理方式就是將包的源地址替換成 host 的地址傳送出去,即做了一次網路地址轉換 NAT。

既然如此,那麼通過抓包應該就可以看清楚網路地址轉化的過程,小菜農打算使用 tcpdump 來做一次簡易抓包檢視下結果,首先他起了兩個終端,一個終端通過 ping www.baidu.com 來傳送網路包,一個包通過 tcpdump -i docker0 -n icmp 抓取網路資訊

172.17.0.2nginx-default 容器的 IP,通過轉包可以發現該 IP 地址是往 180.101.49.11 傳送資料包,到這裡小菜農完成了第一步,接受繼續通過 tcpdump -i eth0 -n icmp 檢視宿主機的網路流出資訊

“啊哈!果然變了,這個時候ping包的原地址,變成了 ehp0 的 IP 172.26.19.234

弄清楚了 容器內部訪問外界 後,那麼接下來就是搞清楚 外界如果訪問容器

小菜農記得剛剛自己啟動容器後,在 PORTS 那裡有顯示埠,便在瀏覽器試了下就可以順利訪問了!小菜農又起了一個容器進行實驗

啟動容器後,便可以利用 32771 這個埠號進行訪問,而32771這個埠是宿主機自動分配的,小菜農又試了下自己指定埠訪問

指定埠後,通過自己指定的埠也是可以訪問的,而這其中的緣由,小菜農也通過查詢發現是通過 docker-proxy 實現的

每一個對映的埠,host 都會啟動一個 docker-proxy 程式來處理訪問容器的流量。

到這裡,小菜農就搞懂了 Docker 中的網路使用,但是小菜農眉頭一皺,今天玩的都是單機,那麼在叢集中網路又是如何訪問呢?

不要空談,不要貪懶,和小菜一起做個吹著牛X做架構的程式猿吧~點個關注做個伴,讓小菜不再孤單。我們們下文見!

今天的你多努力一點,明天的你就能少說一句求人的話!

我是小菜,一個和你一起變強的男人。 ?

微信公眾號已開啟,小菜良記,沒關注的同學們記得關注哦!

相關文章