1. 程式人生 > >通過一次思維碰撞看清docker 網橋模型

通過一次思維碰撞看清docker 網橋模型

背景

這幾天在研究Kubernetes, 遇到一個有意思的nodejs映象:luksa/kubia

# 不帶埠對映啟動容器
docker run -it -d luksa/kubia
# 連線到預設的Bridge網橋,容器IP是 172.17.0.2

之後,在宿主機使用容器IP和8080 埠可訪問該容器nodejs服務

對此我有幾個疑問,這幾個疑問在我看來有點與我之前對docker 網路的認知相沖突。
Q1. 不是說如果容器沒有埠對映,容器內外隔離嗎,怎麼在宿主機使用容器IP還可以訪問?
Q2. 容器IP:80 訪問不到容器內nodejs服務,容器IP:8080可以訪問,這個8080從哪裡來?

頭腦風暴

首先排除一些同事說法:這個容器是以host
網路模型連到宿主機,所以可以在宿主機通過容器IP訪問

  • All containers without a --network specified, are attached to the default bridge network.
  • In terms of Docker, a bridge network uses a software bridge which allows containers connected to the same bridge network to communicate, while providing isolation from containers which are not connected to that bridge network.

對於問題1,我有個誤區:沒有埠對映,容器內外網路隔離,宿主機是無法訪問容器的。
實際上,對於加入同一bridge網橋上的容器,網橋內外網路確實是隔離的,網橋上的容器都可以相互連線。
而我們的宿主機也在這個預設的bridge網橋裝置上,其IP地址是網橋裝置的閘道器(172.17.0.1)。

Q3.那埠對映到底起什麼作用呢?
網橋模型確保了網橋內容器可相互訪問,但除此之外的宿主機網路(127.0.0.1/宿主機物理IP)均不能訪問容器, 這也正是bridge網路隔離的效果。
埠對映-p表示容器繫結宿主機的網絡卡埠來實現轉發訪問,繫結的網絡卡決定了你對外暴露的程度。

  1. 繫結宿主機的迴環地址127.0.0.1
 docker run -it  -d  -p 127.0.0.1:8080:8080 luksa/kubia

那麼在宿主機內只能使用127.0.0.1:8080可訪問容器

  1. 繫結宿主機的實體地址 10.201.80.126
docker run -it  -d  -p 10.201.80.126:8080:8080 luksa/kubia

那麼在宿主機內可使用物理IP10.201.80.126:8080訪問容器,這樣區域網機器就能訪問到容器了 3. 不寫IP,這樣會繫結到0.0.0.0,也就是宿主機所有的網絡卡。

docker run -it  -d  -p 8080:8080 luksa/kubia

很顯然,宿主機內迴環地址和實體地址均可以訪問該容器了。

再回到上面的Q2問題,通過容器IP:8080訪問容器,8080是哪裡來的?

8080是容器內nodejs程序的監聽埠,我們在構建映象時本就無所謂使用expose指令

The EXPOSE instruction does not actually publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container, about which ports are intended to be published.

所以在docekr ps時候,並不會在PORTS列顯示任何內容,但是通過容器IP可直接連通容器內程序監聽埠。

為啥訪問容器IP:8080 就可以訪問容器內nodejs提供的服務?

這是因為容器映象在構建的時候,一般在0.0.0.0地址上監聽請求,這意味著程式在所有地址的8080埠上監聽請求。

這樣就延伸出一個有趣的現象,讓我們進入容器內部:

 docker exec -it 3cc9f428fc25 bash
curl 127.0.0.1:8080
curl 127.0.0.2:8080
curl 127.0.1:8080
curl 172.17.0.2:8080
curl 172.17.2:8080

幾個看起來錯誤的IP竟然也可以訪問nodejs服務, 這正是nodejs在http://0.0.0.0:8080地址監聽請求的結果。

# 擷取自該映象構建原始碼: https://github.com/luksa/kubia-qps/blob/master/kubia-qps/app.js
var www = http.createServer(handler);
www.listen(8080);

# nodejs: server.listen([port[, host[, backlog]]][, callback]) api
If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise.

猜想,驗證,原始碼支援,解答了我一開始的幾個疑問,對容器Bridge的網路認知進一步加深。

總結輸出

  1. bridge網橋內容器通過容器IP相互訪問,外部網路隔離
  2. docker run -p 引數通過埠對映,讓容器內外部網路(宿主機)可以訪問容器
  3. 一般情況下,對外提供web服務的docker映象會在0.0.0.0 地址上監聽請求
  • https://en.wikipedia.org/wiki/0.0.0.0
  • https://github.com/luksa/kubia-qps/blob/master/kubia-qps/app.js
  • https://linuxize.com/post/check-listening-ports-linux/