Docker網絡原則入門:EXPOSE,-p,-P,-link
構建多容器應用程序,需要定義網絡參數來設置容器間的通信,可以通過EXPOSE或者-expose暴露端口、使用-p發布特定端口,還可以用-link 等等來實現,這些方法可能會得到一樣的效果,但是這些方法之間是否有不同,應該選擇什么樣的方法,將是本文討論的重點內容。

如果你已經構建了一些多容器的應用程序,那么肯定需要定義一些網絡規則來設置容器間的通信。有多種方式可以實現:可以通過--expose參數在運行時暴露端口,或者在Dockerfile里使用EXPOSE指令。還可以在Docker run的時候通過-p或者-P參數來發布端口。或者通過--link鏈接容器。雖然這些方式幾乎都能達到一樣的結果,但是它們還是有細微區別。那么到底應該使用哪一種呢?
TL;DR
使用-p或者-P來創建特定端口綁定規則最為可靠,EXPOSE可以看做是容器文檔化的方式,謹慎使用--link的方式。 </blockquote> 在比較這些不同方式之前,我們先分別了解細節。
通過EXPOSE或者-expose暴露端口
有兩種方式可以用來暴露端口:要么用EXPOSE指令在Dockerfile里定義,要么在docker run時指定--expose=1234。這兩種方式作用相同,但是,--expose可以接受端口范圍作為參數,比如--expose=2000-3000。但是,EXPOSE和--expose都不依賴于宿主機器。默認狀態下,這些規則并不會使這些端口可以通過宿主機來訪問。
基于EXPOSE指令的上述限制,Dockerfile的作者一般在包含EXPOSE規則時都只將其作為哪個端口提供哪個服務的提示。使用時,還要依賴于容器的操作人員進一步指定網絡規則。和-P參數聯合使用的情況,下文會進一步闡述。不過通過EXPOSE命令文檔化端口的方式十分有用。
本質上說,EXPOSE或者--expose只是為其他命令提供所需信息的元數據,或者只是告訴容器操作人員有哪些已知選擇。
實際上,在運行時暴露端口和通過Dockerfile的指令暴露端口,這兩者沒什么區別。在這兩種方式啟動的容器里,通過docker inspect $container_id | $container_name查看到的網絡配置是一樣的:
"NetworkSettings": { "PortMapping": null, "Ports": { "1234/tcp": null } }, "Config": { "ExposedPorts": { "1234/tcp": {} } }
可以看到端口被標示成已暴露,但是沒有定義任何映射。注意這一點,因為我們查看的是發布端口。
ProTip:使用運行時標志--expose是附加的,因此會在Dockerfile的EXPOSE指令定義的端口之外暴露添加的端口。
使用-p發布特定端口
可以使用-p參數顯式將一個或者一組端口從容器里綁定到宿主機上,而不僅僅是提供一個端口。注意這里是小寫的p,不是大寫。因為該配置依賴于宿主機器,所以Dockerfile里沒有對應的指令,這是運行時才可用的配置。-p參數有幾種不同的格式:
ip:hostPort:containerPort| ip::containerPort \ | hostPort:containerPort | containerPort
實際中,可以忽略ip或者hostPort,但是必須要指定需要暴露的containerPort。另外,所有這些發布的規則都默認為tcp。如果需要udp,需要在最后加以指定,比如-p 1234:1234/udp。如果只是用命令docker run -p 8080:3000 my-image運行一個簡單的應用程序,那么容器里運行在3000端口的服務在宿主機的8080端口也就可用了。端口不需要一樣,但是在多個容器都暴露端口時,必須注意避免端口沖突。
避免沖突的最佳方法是讓Docker自己分配hostPort。在上述例子里,可以選擇docker run -p 3000 my_image來運行容器,而不是顯式指定宿主機端口。這時,Docker會幫助選擇一個宿主機端口。運行命令docker port $container_id | $container_name可以查看Docker選出的端口號。除了端口號,該命令只能顯示容器運行時端口綁定信息。還可以通過在容器上運行docker inspect查看詳細的網絡信息,在定義了端口映射時,這樣的信息就很有用。該信息在Config、HostConfig和NetworkSettings部分。我們查看這些信息來對比不同方式搭建的容器間的網絡區別。
ProTip:可以用-p參數定義任意數量的端口映射。
-expose/EXPOSE和-p對比
為了更好得理解兩者之間的區別,我們使用不同的端口設置來運行容器。
運行一個很簡單的應用程序,會在curl它的時候打印'hello world'。稱這個鏡像為no-exposed-ports:
FROM ubuntu:trusty MAINTAINER Laura Frank <laura.frank@centurylink.com> CMD while true; do echo 'hello world' | nc -l -p 8888; done
實驗時注意使用的是Docker主機,而不是boot2docker。如果使用的是boot2docker,運行本文示例命令前先運行boot2docker ssh。
注意,我們使用-d參數運行該容器,因此容器一直在后臺運行。(端口映射規則只適用于運行著的容器):
$ docker run -d --name no-exposed-ports no-exposed-ports e18a76da06b3af7708792765745466ed485a69afaedfd7e561cf3645d1aa7149
這兒沒有太多的信息,只是回顯了容器的ID,提示服務已經成功啟動。和預期結果一樣,運行docker port no-exposed-ports和docker inspect no-exposed-ports時沒顯示什么信息,因為我們既沒有定義端口映射規則也沒有發布任何端口。
因此,如果我們發布一個端口會發生什么呢,-p參數和EXPOSE到底有什么區別呢?
還是使用上文的no-exposed-ports鏡像,在運行時添加-p參數,但是不添加任何expose規則。在config.ExposedPorts里重新查看--expose參數或者EXPOSE指令的結果。
$ docker run -d --name no-exposed-ports-with-p-flag -p 8888:8888 no-exposed-ports c876e590cfafa734f42a42872881e68479387dc2039b55bceba3a11afd8f17ca $ docker port no-exposed-ports-with-p-flag 8888/tcp -> 0.0.0.0:8888
太棒了!我們可以看到可用端口。注意默認這是tcp。我們到網絡設置里看看還有什么信息:
"Config": { [...] "ExposedPorts": { "8888/tcp": {} } }, "HostConfig": { [...] "PortBindings": { "8888/tcp": [ { "HostIp": "", "HostPort": "8888" } ] } }, "NetworkSettings": { [...] "Ports": { "8888/tcp": [ { "HostIp": "0.0.0.0", "HostPort": "8888" } ] } }
注意”Config“部分的暴露端口字段。這和我們使用EXPOSE或者--expose暴露的端口是一致的。Docker會隱式暴露已經發布的端口。已暴露端口和已發布端口的區別在于已發布端口在宿主機上可用,而且我們可以在“HostConfig”和“NetworkSettings”兩個部分都能看到已發布端口的信息。
所有發布(-p或者-P)的端口都暴露了,但是并不是所有暴露(EXPOSE或--expose)的端口都會發布。
使用-P和EXPOSE發布端口
因為EXPOSE通常只是作為記錄機制,也就是告訴用戶哪些端口會提供服務,Docker可以很容易地把Dockerfile里的EXPOSE指令轉換成特定的端口綁定規則。只需要在運行時加上-P參數,Docker會自動為用戶創建端口映射規則,并且幫助避免端口映射的沖突。
添加如下行到上文使用的Web應用Dockerfile里:
EXPOSE 1000 EXPOSE 2000 EXPOSE 3000
構建鏡像,命名為exposed-ports。
docker build -t exposed-ports .
再次用-P參數運行,但是不傳入任何特定的-p規則。可以看到Docker會將EXPOSE指令相關的每個端口映射到宿主機的端口上:
$ docker run -d -P --name exposed-ports-in-dockerfile exposed-ports 63264dae9db85c5d667a37dac77e0da7c8d2d699f49b69ba992485242160ad3a $ docker port exposed-ports-in-dockerfile 1000/tcp -> 0.0.0.0:49156 2000/tcp -> 0.0.0.0:49157 3000/tcp -> 0.0.0.0:49158
很方便,不是么?
--link怎么樣呢?
你可能在多容器應用程序里使用過運行時參數--link name:alias來設定容器間關系。雖然--link非常易于使用,幾乎能提供和端口映射規則和環境變量相同的功能。但是最好將--link當做服務發現的機制,而不是網絡流量的門戶。
--link參數唯一多做的事情是會使用源容器的主機名和容器ID來更新新建目標容器(使用--link參數創建的容器)的/etc/hosts文件。
當使用--link參數時,Docker提供了一系列標準的環境變量,如果想知道細節的話可以查看 相應文檔。
雖然--link對于需要隔離域的小型項目非常有用,它的功能更像服務發現 的工具。如果項目中使用了編排服務,比如Kubernetes或者Fleet,很可能就會使用別的服務發現工具來管理關系。這些編排服務可能會不管理 Docker的鏈接,而是管理服務發現工具里包含的所有服務,在Panamax項目里使用的很多遠程部署適配器正是做這個的。
找到平衡
哪一種網絡選擇更為適合,這取決于誰(或者哪個容器)使用Docker運行的服務。需要注意的是一旦鏡像發布到Docker Hub之后,你無法知道其他人如何使用該鏡像,因此要盡可能讓鏡像更加靈活。如果你只是從Docker Hub里取得鏡像,使用-P參數運行容器是最方便迅速的方式,來基于作者的建議創建端口映射規則。記住每一個發布的端口都是暴露端口,但是反過來是不對的。
快速參考
原文鏈接:A Brief Primer on Docker Networking Rules: EXPOSE, -p, -P, –link(翻譯:崔婧雯 校對:魏小紅)
===========================
譯者介紹
崔婧雯,現就職于IBM,高級軟件工程師,負責IBM WebSphere業務流程管理軟件的系統測試工作。曾就職于VMware從事桌面虛擬化產品的質量保證工作。對虛擬化,中間件技術,業務流程管理有濃厚的興趣。
來自:http://dockone.io/article/455
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!