[转载] 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参数运行容器是最方便迅速的方式,来基于做者的建议建立端口映射规则。记住每个发布的端口都是暴露端口,可是反过来是不对的。

快速参考

技术分享

相关文章
相关标签/搜索