这几天在研究Kubernetes, 遇到一个有意思的nodejs镜像:luksa/kubianode
# 不带端口映射启动容器
docker run -it -d luksa/kubia
# 链接到默认的Bridge网桥,容器IP是 172.17.0.2
以后,在宿主机使用容器IP和8080 端口可访问该容器nodejs服务 linux
对此我有几个疑问,这几个疑问在我看来有点与我以前对docker 网络的认知相冲突。
Q1. 不是说若是容器没有端口映射,容器内外隔离吗,怎么在宿主机使用容器IP还能够访问?
Q2. 容器IP:80 访问不到容器内nodejs服务,容器IP:8080能够访问,这个8080从哪里来?git
host
网络模型连到宿主机,因此能够在宿主机经过容器IP访问
- All containers without a --network specified, are attached to the default bridge network.
- In terms of Docker, a bridge network uses a software bridge which allows containers connected to the same bridge network to communicate, while providing isolation from containers which are not connected to that bridge network.
对于问题1,我有个误区:没有端口映射,容器内外网络隔离,宿主机是没法访问容器的。
实际上,对于加入同一bridge网桥上的容器,网桥内外网络确实是隔离的,网桥上的容器均可以相互链接。
而咱们的宿主机也在这个默认的bridge网桥设备上,其IP地址是网桥设备的网关(172.17.0.1)。github
Q3.那端口映射到底起什么做用呢?
网桥模型确保了网桥内容器可相互访问,但除此以外的宿主机网络(127.0.0.1/宿主机物理IP)均不能访问容器, 这也正是bridge网络隔离的效果。
端口映射-p表示容器绑定宿主机的网卡端口来实现转发访问,绑定的网卡决定了你对外暴露的程度。web
docker run -it -d -p 127.0.0.1:8080:8080 luksa/kubia
那么在宿主机内只能使用127.0.0.1:8080
可访问容器 docker
docker run -it -d -p 10.201.80.126:8080:8080 luksa/kubia
那么在宿主机内可以使用物理IP10.201.80.126:8080
访问容器,这样局域网机器就能访问到容器了 3. 不写IP,这样会绑定到0.0.0.0,也就是宿主机全部的网卡。api
docker run -it -d -p 8080:8080 luksa/kubia
很显然,宿主机内回环地址和物理地址
都可以访问该容器了。bash
容器IP:8080
访问容器,8080是哪里来的?8080是容器内nodejs进程的监听端口,咱们在构建镜像时本就无所谓使用expose指令
网络
The EXPOSE instruction does not actually publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container, about which ports are intended to be published.app
因此在docekr ps时候,并不会在PORTS列显示任何内容,可是经过容器IP可直接连通容器内进程监听端口。
这是由于容器镜像在构建的时候,通常在0.0.0.0
地址上监听请求,这意味着程序在全部地址的8080端口上监听请求。
这样就延伸出一个有趣的现象,让咱们进入容器内部:
docker exec -it 3cc9f428fc25 bash
curl 127.0.0.1:8080
curl 127.0.0.2:8080
curl 127.0.1:8080
curl 172.17.0.2:8080
curl 172.17.2:8080
几个看起来错误的IP居然也能够访问nodejs服务, 这正是nodejs在http://0.0.0.0:8080
地址监听请求的结果。
# 截取自该镜像构建源码: https://github.com/luksa/kubia-qps/blob/master/kubia-qps/app.js
var www = http.createServer(handler);
www.listen(8080);
# nodejs: server.listen([port[, host[, backlog]]][, callback]) api
If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise.
猜测,验证,源码支持,解答了我一开始的几个疑问,对容器Bridge的网络认知进一步加深。