本文主要讲解 Docker 的网络原理。在此之前,最好对网络命名空间、Veth 设备对、网桥、路由、netfilter 与 iptables 等Linux基础网络知识有所了解,详见《Docker的Linux网络基础》。
一、Docker 的网络原理
1. Docker 的网络模式
标准的 Docker 支持 4 种网络模式,可以在使用 docker run 命令启动容器的时候通过 --net 参数指定容器的网络模式: (1)bridge:--net=bridge,Docker 默认的网络模式,新建的容器将拥有独立的网络命名空间,并连接到 Docker 的网桥 docker0 中。 (2)container:--net=container:NAME_or_ID,新建的容器将与指定容器使用同一个网络命名空间,共享网络栈,可以直接通过 lo 环回接口进行通信,但其他资源还是相互隔离的。 (3)host:--net=host,新建的容器将与宿主机使用同一个网络命名空间,但其他资源还是隔离的。容器进程可以跟主机其它 root 进程一样打开低范围的端口,如果进一步的使用 --privileged=true,容器会被允许直接配置主机的网络堆栈。 (4)none:--net=none,新建的容器将拥有独立的网络命名空间,但不进行网络配置,后续需要手动配置。2. Docker 启动后的系统情况
(1)创建了 docker0 网桥并分配了 IP 地址
Docker Daemon 首次启动时会创建一个虚拟网桥,默认的名称是 docker0,然后按照 RPC1918 的模型在私有网络空间中给这个网桥分配一个子网。Docker Daemon 会在几个备选地址段里给 docker0 选一个地址,通常是以 172 开头的一个地址。(2)添加了 iptables 规则
第 6 条:从 docker0 发出的包可以被中转给 docker0 本身,即连接在 docker0 网桥上的不同容器之间的通信是允许的。 第 3 条:如果接收到发给 docker0 网桥的数据包属于以前已经建立好的连接,那么允许直接通过,这样接收到的数据包自然又走回 docker0 并中转到相应的容器。 第 5 条:从 docker0 发出的包,如果需要转发到非 docker0 本地 IP 地址的设备,则是允许的,这样 docker0 设备发出的包就可以根据路由规则中转到宿主机的网卡设备,从而访问外面的网络了。 第 1 条:若本地 docker0 网段发出的数据包不是发往 docker0 的,而是发往主机之外的设备的,则都需要进行动态地址修改 (MASQUERADE) ,将源地址从容器的地址修改为宿主机网卡的 IP 地址,之后就可以发送给外面的网络了。(3)Linux 的 ip_forward 功能开启
二、bridge 网络模式
在 bridge 模式下,针对由 Docker 建的每一个容器,都会创建一个虚拟以太网设备 —— Veth 设备对,其中一端关联到网桥 docker0 上,另一端使用 Linux 的网络命名空间技术映射到容器内的 eth0 设备,然后在网桥的地址段内给 eth0 接口分配一个没有使用过的 IP 地址。 Docker 的 bridge 模式的网络模型如下: docker0 地址段默认情况下在宿主机外部是不可见的,所以在同一台机器内的容器之间可以相互通信,不同主机上的容器不能相互通信,实际上它们甚至有可能在相同的网络地址范围内(不同主机上的 docker0 地址段可能是一样的)。 为了让它们跨节点相互通信,就必须在主机的地址上分配端口,然后通过这个端口将网络流量路由或代理到目标容器上。这样做显然意味着一定要在容器之间小心谨慎地协调好端口的分配情况,或者使用动态端口的分配技术,而这二者都是困难且会增加复杂度的事情。这都是 Docker 的网络模型在跨主机访问时面临的问题。(1)创建一个 Docker 容器,默认使用 bridge 网络模式,同时指定端口映射
将宿主机 8080 端口映射到容器 4000 端口。(2)查看容器的网络模式
使用了 Docker 的默认网络模式——bridge。(3)查看网桥连接与容器路由
宿主机上的 Veth 设备对已经建立,连接容器与网桥 docker0。 容器内默认停止的回环设备 lo 被启动,外面宿主机连接进来的 Veth 设备也被命名成了 eth0, 并且已经配置了地址 172.17.0 2。 容器内路由信息表包含一条到 docker0 的子网路由和一条到 docker0 的默认路由。(4)查看 iptables 规则
请求宿主机 8080 端口的数据包目的地址将被转换为容器的服务地址 172.17.0.2:4000。三、container 网络模式
使用 container 网络模式创建的容器将与指定容器使用同一个网络命名空间,共享网络栈,可以直接通过 lo 环回接口进行通信,但其他资源还是相互隔离的。(1)使用 container 网络模式创建一个容器
(2)查看容器网络模式
(3)查看容器网络
可以看到,container 模式的容器与指定容器处于同一网络命名空间,使用同一个 ip,两个容器之间可通过 lo 设备通信,使用端口不可重复。 container 网络模式的容器无法进行端口映射。四、host 网络模式
使用 host 网络模式创建的容器将与宿主机使用同一个网络命名空间,但其他资源还是隔离的。容器进程可以跟主机其它 root 进程一样打开低范围的端口,如果进一步的使用 --privileged=true,容器会被允许直接配置主机的网络堆栈。(1)使用 host 网络模式创建一个容器
(2)查看容器网络模式
(3)查看容器网络
容器与宿主机使用同一网络命名空间,在容器中可以看到 docker0 网桥。(4)查看宿主机端口占用
容器直接占用宿主机的端口。五、none 网络模式
使用 none 网络模式创建的容器将拥有独立的网络命名空间,但不进行网络配置,后续需要手动配置。(1)使用 none 网络模式创建一个容器
(2)查看容器网络模式
(3)查看容器网络
只有 lo 设备,暂无 veth 设备,未设置 ip。六、Docker 网络的局限性
Docker 一直以来的理念都是“简单为美”,所以一开始没有考虑到多机互联的网络解决方案。若要基于 Docker 的网络模型实现跨主机访问,要么在容器之间小心谨慎地协调好端口的分配情况,要么使用动态端口的分配技术,但这二者都是困难且会增加复杂度的事情。
参考: 《Kubernetes 权威指南第 5 版》 标签:容器,docker0,宿主机,网络,网桥,原理,Docker From: https://www.cnblogs.com/wujuntian/p/18093048