[轉載] Docker網絡原則入門:EXPOSE,-p,-P,-link

標籤:docker

原文: http://dockone.io/article/455ubuntu

若是你已經構建了一些多容器的應用程序,那麼確定須要定義一些網絡規則來設置容器間的通訊。有多種方式能夠實現:能夠經過--expose參數在運行時暴露端口,或者在Dockerfile裏使用EXPOSE指令。還能夠在Docker run的時候經過-p或者-P參數來發布端口。或者經過--link連接容器。雖然這些方式幾乎都能達到同樣的結果,可是它們仍是有細微區別。那麼到底應該使用哪種呢?網絡

TL;DR

使用-p或者-P來建立特定端口綁定規則最爲可靠,EXPOSE能夠看作是容器文檔化的方式,謹慎使用--link的方式。

在比較這些不一樣方式以前,咱們先分別瞭解細節。

app

經過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查看到的網絡配置是同樣的:ssh

"NetworkSettings": {"PortMapping": null,"Ports": {    "1234/tcp": null}},"Config": {"ExposedPorts": {    "1234/tcp": {}}}


能夠看到端口被標示成已暴露,可是沒有定義任何映射。注意這一點,由於咱們查看的是發佈端口。

ProTip:使用運行時標誌--expose是附加的,所以會在Dockerfile的EXPOSE指令定義的端口以外暴露添加的端口。

curl

使用-p發佈特定端口

可使用-p參數顯式將一個或者一組端口從容器裏綁定到宿主機上,而不只僅是提供一個端口。注意這裏是小寫的p,不是大寫。由於該配置依賴於宿主機器,因此Dockerfile裏沒有對應的指令,這是運行時纔可用的配置。-p參數有幾種不一樣的格式:tcp

ip:hostPort:containerPort| ip::containerPort | hostPort:containerPort | containerPort


實際中,能夠忽略ip或者hostPort,可是必需要指定須要暴露的containerPort。另外,全部這些發佈的規則都默認爲tcp。若是須要udp,須要在最後加以指定,好比-1234:1234/udp。若是隻是用命令docker run -8080:3000 my-image運行一個簡單的應用程序,那麼容器裏運行在3000端口的服務在宿主機的8080端口也就可用了。端口不須要同樣,可是在多個容器都暴露端口時,必須注意避免端口衝突。

避免衝突的最佳方法是讓Docker本身分配hostPort。在上述例子裏,能夠選擇docker run -3000 my_image來運行容器,而不是顯式指定宿主機端口。這時,Docker會幫助選擇一個宿主機端口。運行命令docker port $container_id | $container_name能夠查看Docker選出的端口號。除了端口號,該命令只能顯示容器運行時端口綁定信息。還能夠經過在容器上運行docker inspect查看詳細的網絡信息,在定義了端口映射時,這樣的信息就頗有用。該信息在Config、HostConfig和NetworkSettings部分。咱們查看這些信息來對比不一樣方式搭建的容器間的網絡區別。

ProTip:能夠用-p參數定義任意數量的端口映射。

ide

-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參數運行該容器,所以容器一直在後臺運行。(端口映射規則只適用於運行着的容器):ui

$ docker run -d --name no-exposed-ports no-exposed-ports
e18a76da06b3af7708792765745466ed485a69afaedfd7e561cf3645d1aa7149


這兒沒有太多的信息,只是回顯了容器的ID,提示服務已經成功啓動。和預期結果同樣,運行docker port no-exposed-portsdocker 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-flag8888/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 1000EXPOSE 2000EXPOSE 3000


構建鏡像,命名爲exposed-ports。

docker build -t exposed-ports .


再次用-P參數運行,可是不傳入任何特定的-p規則。能夠看到Docker會將EXPOSE指令相關的每一個端口映射到宿主機的端口上:

$ docker run -d -P --name exposed-ports-in-dockerfile exposed-ports63264dae9db85c5d667a37dac77e0da7c8d2d699f49b69ba992485242160ad3a$ docker port exposed-ports-in-dockerfile1000/tcp -> 0.0.0.0:491562000/tcp -> 0.0.0.0:491573000/tcp -> 0.0.0.0:49158


很方便,不是麼?

--link怎麼樣呢?

你可能在多容器應用程序裏使用過運行時參數 --link namealias來設定容器間關係。雖然--link很是易於使用,幾乎能提供和端口映射規則和環境變量相同的功能。可是最好將--link當作服務發現的機制,而不是網絡流量的門戶。

--link參數惟一多作的事情是會使用源容器的主機名和容器ID來更新新建目標容器(使用--link參數建立的容器)的/etc/hosts文件。

當使用--link參數時,Docker提供了一系列標準的環境變量,若是想知道細節的話能夠查看相應文檔

雖然--link對於須要隔離域的小型項目很是有用,它的功能更像服務發現的工具。若是項目中使用了編排服務,好比Kubernetes或者Fleet,極可能就會使用別的服務發現工具來管理關係。這些編排服務可能會無論理Docker的連接,而是管理服務發現工具裏包含的全部服務,在Panamax項目裏使用的不少遠程部署適配器正是作這個的。

找到平衡

哪種網絡選擇更爲適合,這取決於誰(或者哪一個容器)使用Docker運行的服務。須要注意的是一旦鏡像發佈到Docker Hub以後,你沒法知道其餘人如何使用該鏡像,所以要儘量讓鏡像更加靈活。若是你只是從Docker Hub裏取得鏡像,使用-P參數運行容器是最方便迅速的方式,來基於做者的建議建立端口映射規則。記住每個發佈的端口都是暴露端口,可是反過來是不對的。

快速參考

技術分享

相關文章
相關標籤/搜索