以前就好奇docker run时的port公开方式-p 8080:8080和--expose(或者Dockerfile里的EXPOSE)的区别,
当时相信了stackoverflow上高手的回答Difference between "expose" and "publish" in docker - Stack Overflow,提到
如果什么都不指定,那么除了该容器自己,谁都不能访问那个port。
**后来有一天细看Docker的网络的时候,觉得不应该是这样。**从Docker主机之外的确不能访问,这点没有疑问,可是从Docker主机之内或者其他容器,其实能够访问,而且很合理。
Docker在主机上做了个docker0网卡172.17.0.1。又为每个容器创建了一个vethXxxx网卡。
网卡是有两头的,一头接外边,一头接容器,就像管道。
这个veth网卡,一头给了容器做eth0 172.17.0.N,另一头被默认地被加入到docker0网卡上,相当把所有的容器的网卡都插到一个hub上了,这就是互通的基础。
照这么说,在其中一个容器里侦听eth0的端口,那么主机和其他容器都应该能够访问到才对。
实验环境: Mac OS X EI Capitan 10.11.5
$ docker-machine -v
docker-machine version 0.8.0-rc1, build fffa6c9
$ docker -v
Docker version 1.12.0-rc3, build 91e29e8, experimental
启动一个容器,什么-p -P --expose都不指定。 就用busybox吧,很小。起了以后就是一个bash等在那儿。
$ docker run -it busybox
得到他的ip,一般第一个启动的就是172.17.0.2。
/ # ifconfig eth0 |grep 'inet addr:'
inet addr:172.17.0.2 ...
在容器里执行一个netcat侦听端口7777。 一旦得到连接就发送date命令的结果给对方。
/ # nc -ll -p 7777 -e date
在别的容器里连接这个端口
$ docker run busybox nc 172.17.0.2 7777
Fri Jul 8 03:50:34 UTC 2016
能通!
在docker主机里连接这个端口
$ docker-machine ssh default nc 172.17.0.2 7777
Fri Jul 8 03:49:40 UTC 2016
自然也能通。
因为不是在Linux主机上,所以这里的docker主机实际值docker-machine这个boot2docker虚拟机。
这个现象应该不是因为实验版本导致的问题,就是这么设计的。
- 如果什么都不指定,那么从Docker主机以及其他容器,都能访问那个port。
- 当有时候忘了加-p端口映射选项就启动了容器做了一堆事儿之后,如果立刻想从Docker主机外访问的,可是却舍不得退出容器,那就还有补救机会。
就在Docker主机里用iptables加个端口转发规则,(VirtualBox里也可以加一个端口映射让本地访问):
$ docker ssh default
docker@default:~$ sudo iptables -A PREROUTING -t nat -p tcp --dport 7777 -j DNAT --to 172.17.0.2:7777
或者在真正的Mac OS X上加个路由,允许经Docker主机(一般为192.168.99.100)访问到172.17.0.N
sudo route -n add 172.17.0.0/24 192.168.99.100
- 而对于-p 7777:7777这样的用法,这是没有疑问的,的确就在Docker主机上被加了个端口转接的rule了,所以和从Docker主机之外能够访问这个端口了。
- 剩下一个--expose 7777(或者Dockerfile里的EXPOSE 7777),实质没有什么用,也许是给发现服务用的。并没有对iptables做任何改变。
- 直到-P(--publish-all)时才会被用到--expose里的端口信息。
- 就是说, 只有既指定--expose 7777(或者Docerfile里的EXPOSE 7777)又指定-P(--publish-all)时,才会有实际动作。但这是把Docker主机上的随机端口映射给容器里的7777,所以一般不用。
例如,先做一个EXPOSE 7777端口的Docker image。
$ cat Dockerfile
FROM busybox
EXPOSE 7777
$ docker build -t busybox_expose_7777 - < Dockerfile
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM busybox
---> 2b8fd9751c4c
Step 2 : EXPOSE 7777
---> Running in bddf30c42dcd
---> 2c92defacdd4
Removing intermediate container bddf30c42dcd
Successfully built 2c92defacdd4
然后运行时加上 --publish-all,
docker run -it --publish-all busybox_expose_7777
/ # nc -ll -p 7777 -e date
这时再看看到底Docker主机的哪个端口被映射到容器里的7777了。
docker ps
...
fca504f3bc49 busybox_expose_7777 "sh" ... 0.0.0.0:32769->7777/tcp nostalgic_galileo
就是这个32769端口了。那访问Docker主机的32769端口看看。
(Docker主机的ip一般是192.168.99.100(可以从DOCKER_HOST环境变量里看出来))
$ nc 192.168.99.100 32769
Fri Jul 8 11:14:08 UTC 2016
的确如此。
真真是聊胜于无的东西,难以置信谁会用这个。却搞得不好理解,文档也说的不明不白。
对于新型的,不用VirtualBox的Docker for Mac
或者Docker for Windows
,会怎么样呢?
结果:在其他容器里也是可以访问别的容器的端口的。在docker主机里就不知道了,因为进不去,不过应该是行的。
这两种都直接利用Mac/Windows自带的虚拟设备API(例如Hypervisor.framework)直接生成一个linux虚拟机作为docker主机,
而且它刻意淡化这个虚拟机的存在,我暂时找不到命令进入这个虚拟机,所以没做完这个实验,不过无所谓了,原理一样,应该是一样的结果。
顺便,这种新型工具,以xhyve虚拟机为基础,非常轻巧好用,连VirtualBox都不用装了,一切和以前一样的用法,除了docker-machine命令不起作用。