背景

这几天在研究Kubernetes, 遇到一个有意思的nodejs镜像:luksa/kubia

# 不带端口映射启动容器
docker run -it -d luksa/kubia
# 连接到默认的Bridge网桥,容器IP是 172.17.0.2

之后,在宿主机使用容器IP和8080 端口可访问该容器nodejs服务

对此我有几个疑问,这几个疑问在我看来有点与我之前对docker 网络的认知相冲突。

Q1. 不是说如果容器没有端口映射,容器内外隔离吗,怎么在宿主机内使用容器IP还可以访问容器

Q2. 容器IP:80 访问不到容器内nodejs服务,容器IP:8080可以访问,这个8080从哪里来

头脑风暴

首先排除一些同事说法:这个容器是以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,我有个误区:没有端口映射,容器内外网络隔离,宿主机是无法访问容器的。

在网桥上,各个容器有自己的eth0网卡,容器所有的网络请求都通过eth0向外传递。但是为了实现容器之间的通信,容器必须增加一个网络设备veth,并将它们加到宿主机上的docker0网桥之上,实现不同网段数据包的转发功能,进而具备通信能力。

宿主机也在这个网桥上,我们在宿主机使用容器IP访问容器,实际上是走的网桥网络,能直接寻址容器 (不需要映射 端口一说)。

Q3.那端口映射到底起什么作用呢?

网桥模型确保了网桥内容器可相互访问,但除此之外的网络(127.0.0.1/宿主机物理IP 这些均其他网络)均不能访问容器, 这也正是bridge网络隔离的效果。

考虑到外界与与容器通信的情况,我们知道容器只是活在宿主机上的一个进程而已,如何让外界追溯到特定的容器呢?

docker使用的是端口绑定的方式,并将外界的端口流量转发到目的端口,也就是特定的容器之上,这就实现了外界和容器之间的通信

  1. 绑定宿主机的回环地址127.0.0.1
 docker run -it  -d  -p 127.0.0.1:8080:8080 luksa/kubia

那么在宿主机内只能使用127.0.0.1:8080可访问容器

  1. 绑定宿主机的物理地址 10.201.80.126
docker run -it  -d  -p 10.201.80.126:8080:8080 luksa/kubia

那么在宿主机内可使用物理IP10.201.80.126:8080访问容器,这样局域网机器就能访问到容器了 3. 不写IP,这样会绑定到0.0.0.0,也就是宿主机所有的网卡。

docker run -it  -d  -p 8080:8080 luksa/kubia

很显然,宿主机内回环地址和物理地址均可以访问该容器了。

再回到上面的Q2问题,通过容器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.

所以在docekr ps时候,并不会在PORTS列显示任何内容,但是通过容器IP可直接连通容器内进程监听端口。

为啥访问容器IP:8080 就可以访问容器内nodejs提供的服务?

这是因为容器镜像在构建的时候,一般在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的网络认知进一步加深。

总结输出

  1. bridge网桥内容器通过容器IP相互访问,外部网络隔离
  2. docker run -p 参数通过端口映射,让容器内外部网络(宿主机)可以访问容器
  3. 一般情况下,对外提供web服务的docker镜像会在0.0.0.0 地址上监听请求
  • https://en.wikipedia.org/wiki/0.0.0.0
  • https://github.com/luksa/kubia-qps/blob/master/kubia-qps/app.js
  • https://linuxize.com/post/check-listening-ports-linux/

最新文章

  1. 一些牛逼哄哄的javascript面试题
  2. 3.Git的诞生和其分布式的优点
  3. c++ boost (递归)遍历目录
  4. hdu 5543 Pick The Sticks(动态规划)
  5. Poj1611The Suspects
  6. 使用PowerShell向SharePoint中写入数据
  7. 【设计模式六大原则6】开闭原则(Open Close Principle)
  8. Java 程序检查远程服务器状态
  9. TCP与UDP区别
  10. phpcms总结(转)
  11. 乐观的并发策略——基于CAS的自旋
  12. windows7 dos修改mysql root密码
  13. HTML和CSS怎么用
  14. css格式
  15. promise 链式
  16. vue-cli3安装创建项目以及目录结构
  17. 简易版AC自动机
  18. LeetCode--414--第三大的数
  19. 如何打开Windows Server 2008 R2的域安全策略
  20. 监控http服务脚本

热门文章

  1. 一次完整的HTTP请求响应过程(很详细)
  2. python Ajax的使用
  3. MS15-020 文件共享漏洞利用
  4. LeetCode Day 12
  5. python3之urllib基础
  6. idea 内存溢出解决方法
  7. CF580D_Kefa and Dishes
  8. Docker Linux下安装
  9. 苹果会放弃iPhone吗?
  10. 对话|首席研究员童欣:从长远看,AR的应用范围远比VR广泛