Docker 的网络原理
Posted on Wed, 25 Dec 2024 11:13:58 +0800 by LiangMingJian
基本原理
Docker 的本地网络实现依赖于 Linux 上的网络命名空间和虚拟网络设备。直观上看,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)与外界相通,并可以收发数据包。此外,如果不同子网之间要进行通信,还需要额外的路由机制。
Docker 中的网络接口默认都是虚拟接口。虚拟接口的最大优势就是转发效率极高。这是因为 Linux 通过在内核中进行数据复制来实现虚拟接口之间的数据转发,即发送接口的发送缓存中的数据包将被直接复制到接收接口的接收缓存中,而无需通过外部物理网络设备进行交换。对于本地系统和容器内系统来看,虚拟接口跟一个正常的以太网卡相比并无区别,只是它速度要快得多。
Docker 在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通,这样的一对接口叫做 veth pair 。
一般情况下,Docker 在创建一个容器时,会具体执行如下操作:
- 创建一对虚拟接口,分别放到本地主机和新容器的命名空间中。
- 本地主机一端的虚拟接口连接到默认的 docker0 网桥或指定网桥上,并具有一个以 veth 开头的唯一名字,如 veth1234。
- 容器一端的虚拟接口将放到新创建的容器中,并修改名字作为 eth0,这个接口只在容器的命名空间可见。
- 从网桥可用地址段中获取一个空闲地址分配给容器的 eth0(例如172.17.0.2/16),并配置默认路由网关为docker0 网卡的内部接口 docker0 的IP地址(例如172.17.42.1/16)。
- 完成上述操作后,容器就可以使用 eth0 虚拟网卡来连接其他网络或外网。
Docker 的网络模式
Docker 默认有三个网络模式,可以使用 docker network ls
查看
- bridge:此模式会为每一个容器分配、设置IP等,并将容器连接到一个 docker0 虚拟网桥,通过 docker0 网桥以及 Iptables nat 表配置与宿主机通信,默认使用。
- host:容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。
- null:该模式关闭了容器的网络功能。
在使用 docker run 命令启动容器的时候,可以通过 --net
参数来指定容器的网络配置。
--net=bridge
:默认值,在 Docker 网桥 docker0 上为容器创建新的网络栈。--net=none
:让 Docker 将新容器放到隔离的网络栈中,但是不进行网络配置,之后,用户可以自行进行配置。--net=container:NAME_or_ID
:让 Docker 将新建容器的进程放到一个已存在容器的网络栈中,新容器进程有自己的文件系统、进程列表和资源限制,但会和已存在的容器共享 IP 地址和端口等网络资源,两者进程可以直接通过 lo 环回接口通信。--net=host
:告诉 Docker 不要将容器网络放到隔离的命名空间中,即不要容器化容器内的网络。此时容器使用本地主机的网络,它拥有完全的本地主机接口访问权限。容器进程可以跟主机其他 root 进程一样打开低范围的端口,可以访问本地网络服务,还可以让容器做一些影响整个主机系统的事情,比如重启主机。--net=user_defined_network
:用户自行用 network 相关命令创建一个网络,通过这种方式将容器连接到指定的已有网络上去。
容器之间的通信方式
IP 通信
当两个容器有属于同一个网络的网卡时,容器就可以通过 IP 交互了。具体做法是在容器创建时通过 --network
指定相应的网络,或者通过 docker network connect
将现有容器加入到指定网络。
Docker DNS Server
从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过"容器名"通信。
docker run -it --network=bridge2 --name box1 busybox
docker run -it --network=bridge2 --name box2 busybox
joined 容器
joined 容器是另一种实现容器间通信的方式。它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过 127.0.0.1 直接通信。
docker run -it --name box1 http
docker run -it --network=container:box1 busybox
此时 busybox 和 box1 的网卡 mac 地址与 IP 完全一样,它们共享了相同的网络栈。busybox 可以直接用 127.0.0.1 访问 box1 的 http 服务。