Docker网络原则入门:EXPOSE,-p,-P,-link

本文讲的是Docker网络原则入门:EXPOSE,-p,-P,-link,【编者的话】构建多容器应用程序,需要定义网络参数来设置容器间的通信,可以通过EXPOSE或者-expose暴露端口、使用-p发布特定端口,还可以用-link等等来实现,这些方法可能会得到一样的效果,但是这些方法之间是否有不同,应该选择什么样的方法,将是本文讨论的重点内容。

如果你已经构建了一些多容器的应用程序,那么肯定需要定义一些网络规则来设置容器间的通信。有多种方式可以实现:可以通过--expose参数在运行时暴露端口,或者在Dockerfile里使用EXPOSE指令。还可以在Docker run的时候通过-p或者-P参数来发布端口。或者通过--link链接容器。虽然这些方式几乎都能达到一样的结果,但是它们还是有细微区别。那么到底应该使用哪一种呢?

TL;DR

使用-p或者-P来创建特定端口绑定规则最为可靠,EXPOSE可以看做是容器文档化的方式,谨慎使用--link的方式。

在比较这些不同方式之前,我们先分别了解细节。

通过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-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-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从事桌面虚拟化产品的质量保证工作。对虚拟化,中间件技术,业务流程管理有浓厚的兴趣。

原文发布时间为:2015-06-22

本文作者:崔婧雯

本文来自合作伙伴DockerOne,了解相关信息可以关注DockerOne。

原文标题:Docker网络原则入门:EXPOSE,-p,-P,-link

时间: 2024-11-01 14:26:07

Docker网络原则入门:EXPOSE,-p,-P,-link的相关文章

Docker网络和服务发现

本文讲的是Docker网络和服务发现[编者的话] 本文是<Docker网络和服务发现>一书的全文,作者是Michael Hausenblas.本文介绍了Docker世界中的网络和服务发现的工作原理,并提供了一系列解决方案. 前言 当你开始使用Docker构建应用的时候,对于Docker的能力和它带来的机会,你会感到很兴奋.它可以同时在开发环境和生产环境中运行,只需要将一切打包进一个Docker镜像中,然后通过Docker Hub分发镜像,这是很直接了当的.你会发现以下过程很令人满意:你可以快速

利用OpenVSwitch构建多主机Docker网络

本文讲的是利用OpenVSwitch构建多主机Docker网络,[编者的话]当你在一台主机上成功运行Docker容器后,信心满满地打算将其扩展到多台主机时,却发现前面的尝试只相当于写了个Hello World的入门程序,多主机的网络设置成了下一道门槛.在你尝试各种方案时不妨先看看本文,或许就会豁然开朗,发现原来也不复杂.嗯,是的,本文用到了OpenVSwitch. 运行Docker已经不是什么新鲜事,网上有很多入门教程来帮助你在一台主机上运行容器.这台主机可以是Linux服务器,也可以是Mac(

[翻译]Macvlan 网络驱动入门

Macvlan 网络驱动入门 Macvlan网络驱动是为了在Docker的用户的使用场景中提供一个稳定的,生产就绪的网络驱动.目前Libnetwork 允许用户控制IPv4和IPv6地址管理.对于需要将容器网络和底层网络集成的用户来说,VLAN的驱动也允许他们完全控制二层VLAN taggine.而对于使用不依赖于物理网络约束的overlay网络方式部署网络结构的用户,可以参考multi-host overlay 的驱动部署容器的网络. Macvlan是一种新的网络虚拟化技术.Linux使用非常

Docker 网络配置 【已翻译100%】(1/3)

摘要 当docker启动时,它会在宿主机器上创建一个名为docker0的虚拟网络接口.它会从RFC 1918定义的私有地址中随机选择一个主机不用的地址和子网掩码,并将它分配给docker0.例如当我启动docker几分钟后它选择了172.17.42.1/16-一个16位的子网掩码为主机和它的容器提供了65,534个ip地址. 注意: 本文讨论了Docker的高级网络配置和选项.通常你不会用到这些.如果你想查看一个较为简单的Docker网络介绍和容器概念介绍来着手,请参见Docker用户指南. 但

Docker网络详解

Docker网络详解 转载请注明来自:http://blog.csdn.net/wsscy2004 网络基础 Docker使用linux桥接,在主机虚拟一个docker0网络接口,在主机中运行命令查看: # List host bridges $ sudo brctl showbridge name bridge id STP enabled interfacesdocker0 8000.000000000000 no # Show docker0 IP address $ sudo ifcon

DockOne微信分享(六十六): Docker网络方案初探

本文讲的是DockOne微信分享(六十六): Docker网络方案初探[编者的话]这次主要跟大家聊聊Docker的网络方案,首先是现有容器网络方案介绍, 接下来重点讲解Calico的特性及技术点,作为引申和对比再介绍下Contiv的特性,最后给出对比测试结果. 随着容器的火热发展,数人云越来越多的客户对容器网络特性要求也开始越来越高,比如: 一容器一IP: 多主机容器互联: 网络隔离: ACL: 对接SDN等等. 这次主要跟大家聊聊Docker的网络方案,首先是现有容器网络方案介绍, 接下来重点

Docker 网络模式(四种)详细介绍_docker

Docker 网络模式 本文首先介绍了Docker自身的4种网络工作方式, Docker作为目前最火的轻量级容器技术,有很多令人称道的功能,如Docker的镜像管理.然而,Docker同样有着很多不完善的地方,网络方面就是Docker比较薄弱的部分.因此,我们有必要深入了解Docker的网络知识,以满足更高的网络需求. 四种网络模式 我们在使用docker run创建Docker容器时,可以用--net选项指定容器的网络模式,Docker有以下4种网络模式: · host模式,使用--net=h

Docker 网络工作原理详解_docker

Docker 网络工作原理 当Docker server也就是docker daemon启动时,会自动创建一个名字是docker0的bridge,每当docker创建一个Container时,会在主机上面创建一个名字是veth*的ethernet 端口,并把这个eth*加入到docker0的bridge,在container中会自动创建一个名字是eth0的ethernet端口,这个eth0和veth*会形成一个类似管道的对,一一对应. 配置DNS docker是如何分配每个container的h

更安全的本地Docker网络

本文讲的是更安全的本地Docker网络,[编者的话]本文主要是解决了上一篇探索本地Docker桥接网络提出的问题.我们可以在Docker启动时设置icc=false参数来禁止任意的跨容器通信. 上周我写了<探索本地Docker桥接网络>.在这篇文章中我介绍了如何使用nmap探索由同一桥接网络上的其它容器公开的服务,并展示了在这些服务没有映射到主机的公有接口的端口的情况下如何去访问这些服务.这是一个问题,因为大多数镜像的工具.数据库以及微服务都带有不安全的默认配置.较为变通的工程师或管理员可能会