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

使用-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 -p8080: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参数运行容器是最方便迅速的方式,来基于作者的建议创建端口映射规则。记住每一个发布的端口都是暴露端口,但是反过来是不对的。

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

  1. [转载] Docker网络原则入门:EXPOSE,-p,-P,-link

    原文: http://dockone.io/article/455 如果你已经构建了一些多容器的应用程序,那么肯定需要定义一些网络规则来设置容器间的通信.有多种方式可以实现:可以通过--expose参 ...

  2. docker网络模式----入门docker的难点

    众所周知,现在docker是轻量级虚拟化的典型代表!这段时间想要建立一个分布式系统,但是手头上主机没那么多,所以使用docker进行虚拟化,但是在使用的过程中对网络这一部分是一直不太理解,特别找了一篇 ...

  3. Docker 网络基础介绍

    [编者按]本文作者为 Mesosphere 开发大使 Michael Hausenblas,主要介绍配置 Docker 单主机网络的基本知识.文章系国内 ITOM 管理平台 OneAPM 编译呈现. ...

  4. Docker容器基础入门认知-网络篇

    这篇文章中,会从 docker 中的单机中的 netns 到 veth,再到单机多个容器之间的 bridge 网络交互,最后到跨主机容器之间的 nat 和 vxlan 通信过程,让大家对 docker ...

  5. 《Docekr入门学习篇》——Docker网络及数据卷

    Docker网络设置 默认情况下docker会创建一个桥接网卡[docker 0],docker有两种映射方式,一种是随机映射,一种是指定映射. 提示:生产场景一般不使用随机映射,但是随机映射的好处是 ...

  6. 脑残式网络编程入门(四):快速理解HTTP/2的服务器推送(Server Push)

    本文原作者阮一峰,作者博客:ruanyifeng.com. 1.前言 新一代HTTP/2 协议的主要目的是为了提高网页性能(有关HTTP/2的介绍,请见<从HTTP/0.9到HTTP/2:一文读 ...

  7. docker网络配置方法总结

    docker启动时,会在宿主主机上创建一个名为docker0的虚拟网络接口,默认选择172.17.42.1/16,一个16位的子网掩码给容器提供了65534个IP地址.docker0只是一个在绑定到这 ...

  8. Docker网络模式

    [编者的话] 本文是<Docker网络及服务发现>一书的一个章节,介绍了搭建Docker单主机网络的基础内容.关于Docker网络的更多内容,包括多主机的网络,请参考该书的其他章节. @C ...

  9. Docker网络解决方案 - Weave部署记录

    前面说到了Flannel的部署,今天这里说下Docker跨主机容器间网络通信的另一个工具Weave的使用.当容器分布在多个不同的主机上时,这些容器之间的相互通信变得复杂起来.容器在不同主机之间都使用的 ...

随机推荐

  1. restTemplate源码解析(目录)

    restTemplate是spring实现的,基于restful风格的http请求模板.使用restTemplate可以简化请求操作的复杂性,同时规范了代码风格.本系列文章,将根据以下目录顺序,从源码 ...

  2. Hadoop HA 搭建

    Hadoop HA 什么是 HA HA是High Available缩写,是双机集群系统简称,指高可用性集群,是保证业务连续性的有效解决方案,一般有两个或两个以上的节点,且分为活动节点及备用节点.通常 ...

  3. ftl 列表弄成js数组

    例子:ftl 列表:bannerViewUrls var arrayBanner=[<#list bannerViewUrls as list>"${list}"< ...

  4. Tomcat安装及环境配置

    欢迎任何形式的转载,但请务必注明出处. 本章内容 安装 环境变量入口 三个系统变量配置 测试安装配置是否成功 安装之前请安装jdk并进行环境配置(点击进入jdk教程) 一.安装 点击进入官网下载 二. ...

  5. How to change SAPABAP1 schema password In HANA

    Symptom How to change SAPABAP1 schema password Environment HANA 1.x HANA 2.x Resolution Shutdown the ...

  6. iOS 开发之模糊效果的五种实现

    前言 在iOS开发中我们经常会用到模糊效果使我们的界面更加美观,而iOS本身也提供了几种达到模糊效果的API,如:Core Image,使用Accelerate.Framework中的vImage A ...

  7. sed 修改文件内容

    修改命令对照表 练习实例 修改/etc/passwd中第1行第1个root为ROOT sed -i '1s/root/ROOT/' passwd 修改/etc/passwd中第5行到第10行中所有的/ ...

  8. c# 定义和调用索引器

  9. /etc/apt/sources.list 和 /etc/apt/sources.list.d

    转自:大数据云技术基础之Linux源:/etc/apt/sources.list文件 导读 1./etc/apt/sources.list的作用是什么?2.为什么会产生 /etc/apt/source ...

  10. nginx配置文件详解【nginx.conf】

    #基本配置: #user nobody;#配置worker进程运行用户worker_processes 1;#配置工作进程数目,根据硬件调整.通常等于CPU数量或者2倍于CPU数量 比如四核电脑(可以 ...