Skip to main content

Network网络

network

什么是Docker网络?

在 Docker 中,网络(Network)是一种用于容器间通信的机制

Docker 网络允许创建虚拟网络,容器可以连接到这些虚拟网络,并通过网络进行通信,也可以与外部网络进行连接。

为什么需要Docker网络?

Docker 网络的存在解决了以下问题:

  • 容器之间通信:Docker 网络允许在同一主机上的容器之间进行通信,容器可以通过虚拟网络直接相互访问。
  • 跨主机通信:Docker 网络可以连接到多台主机,使得分布式应用程序中的容器可以在不同主机上通信。
  • 隔离性:不同的网络可以隔离容器,使得容器只能与其所在网络中的其他容器通信,提高了安全性。

网络类型

Docker 支持多种网络类型,包括:

  • 桥接网络(Bridge Network):这是 Docker 的默认网络类型。桥接网络为每个容器分配一个本地IP,并将它们连接到一个名为docker0的虚拟网桥。容器可以通过这个网桥相互通信。
  • 主机网络(Host Network):容器与宿主机共享同一个网络命名空间,这使得容器可以直接使用宿主机的网络接口。这种网络类型适用于需要最大性能和网络透明度的场景。
  • 覆盖网络(Overlay Network):覆盖网络允许在多台主机上创建一个逻辑网络,容器可以连接到这个逻辑网络,实现跨主机通信,适用于分布式应用程序。
  • Macvlan网络:Macvlan网络允许容器具有自己的MAC地址,这使得容器可以在网络中表现得像真实的物理设备,适用于一些特定的网络场景。

关键技术

Docker 关键技术有:

  • docker0
  • veth pair

下面通过一个实际的例子来介绍 Docker 网络底层原理。

假设我们有两个Docker容器,它们分别是"container1"和"container2"。我们将使用docker run命令来创建这两个容器,并观察它们在底层网络上是如何连接的。

  • 创建 container1 容器
docker run -it --name container1 alpine /bin/sh
  • 创建 container2 容器
docker run -it --name container2 alpine /bin/sh

现在,我们已经创建了两个 Docker 容器。我们将查看它们的网络配置和底层原理。

首先,我们可以使用以下命令查看"container1"容器的网络配置:

docker exec container1 ip addr show

这会显示类似以下内容的网络配置:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
...
13: eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever

现在,我们来解释一下上面的输出:

  • lo:代表回环接口。
  • eth0@if14eth0是容器内部的网络接口名,if14是该接口的索引号。
  • link/ether 02:42:ac:11:00:02:容器内部接口的MAC地址。
  • inet 172.17.0.2/16:容器内部接口的IP地址。
note
  1. lo(Loopback Interface): lo是Linux系统中的回环接口,也被称为回环网络接口。它是在本地主机上进行网络通信的虚拟接口。回环接口的IP地址通常被设置为127.0.0.1,是本地主机自身的地址。回环接口用于在本地主机上进行网络通信,而不需要通过物理网络设备传输数据。这对于本地主机上的进程进行自我通信和测试非常有用。

  2. eth0eth0是Linux系统中的物理以太网接口的命名。在Docker容器中,每个容器都有自己的网络命名空间,因此每个容器都会有一个名为eth0的网络接口。这个网络接口连接到Docker网络桥docker0,从而使容器能够与其他容器或宿主机进行通信。通过这个接口,容器可以接收来自docker0网络桥和其他容器的数据,并将数据发送回给它们。

在Docker容器中,eth0是默认的网络接口,用于连接容器与Docker网络。当容器启动时,Docker会为每个容器创建一个veth对,其中一个端口连接到容器的eth0,另一个端口连接到宿主机的Docker网络桥docker0。通过这种方式,容器能够在宿主机和其他容器之间进行网络通信。

接下来,我们查看"container2"容器的网络配置:

docker exec container2 ip addr show

输出类似如下:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
...
13: eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever

此时我们注意到,两个容器的MAC地址相同,但IP地址不同

MAC地址相同

在同一主机上,不同容器共享宿主机的网络设备,如Docker网络桥docker0。因此,它们的网络接口(eth0)的MAC地址相同。这是因为在宿主机的网络层面上,它们通过veth对与docker0相连,共享相同的网络栈和MAC地址。这种共享不会导致冲突,因为它们在网络命名空间内部的隔离确保了它们之间的独立性。

IP地址不同

当Docker守护进程在启动容器时,它会为每个容器分配一个唯一的IP地址。这些IP地址是从Docker守护进程管理的私有IP地址段中动态分配的(例如,默认情况下使用172.17.0.0/16网段)。因此,两个容器可以有不同的IP地址,从而使它们能够在同一主机上相互通信而不会发生冲突。

现在,让我们来了解veth对的概念。在上述两个容器中,eth0@if14接口实际上是由一个veth对连接的。veth对是Linux内核中的虚拟网络设备,它们以对的方式存在,一个端口连接到容器内部,另一个端口连接到宿主机的Docker网络桥docker0

通过使用ip link show命令,我们可以查看它们的存在:

ip link show

输出类似如下:

...
14: veth25c0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether fe:29:21:df:0f:0a brd ff:ff:ff:ff:ff:ff link-netnsid 1
15: veth33c7@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 82:0c:ad:4a:f8:7a brd ff:ff:ff:ff:ff:ff link-netnsid 2
...

在上述输出中,veth25c0veth33c7就是veth对的一部分。它们与Docker网络桥docker0相连,并通过link-netnsid指示连接到相应的网络命名空间。

因此,"container1"和"container2"实际上通过这两个veth对与docker0桥相连,并通过docker0桥连接到宿主机的网络。

同网络下容器通信

通过上述案例,我们可以知道在同网络环境下,不同容器之间可以通过(docker0)网桥进行通信。

此时,该网络下的容器IP地址不同,MAC地址相同,相互之间可以通信。

不同网络下容器通信

在不同的网络下,每一个网络都有类似的 docker0 网桥,在不同网络下运行的容器,也都通过 veth对技术和网桥打通。

理论上不同网络之间的容器是可以直接访问的,但受限于网络命名空间技术的隔离性,不同网络之间的网桥,是无法互相发现的。

因此,要实现不同网咯下的容器通信,应该采取:

  1. 使用共享网络: 通过将容器连接到多个网络,包括不同的网络,使得它们可以在多个网络之间进行通信。这可以通过在Docker命令中使用--network选项连接多个网络来实现。

    示例:

    # 创建网络network1和network2
    docker network create network1
    docker network create network2

    # 在network1中创建容器container1
    docker run -d --name container1 --network network1 your_image

    # 在network2中创建容器container2,并连接到network1
    docker run -d --name container2 --network network2 --network network1 your_image

    在这个示例中,container2连接到了两个网络network1network2,这样它就可以和这两个网络中的容器通信。

  2. 使用自定义网络: 使用Docker的自定义网络功能,将需要通信的容器连接到同一个自定义网络中,从而使它们处于相同的网络命名空间,可以直接通信。

    示例:

    # 创建自定义网络mynetwork
    docker network create mynetwork

    # 在mynetwork中创建容器container1和container2
    docker run -d --name container1 --network mynetwork your_image
    docker run -d --name container2 --network mynetwork your_image

    在这个示例中,container1container2都连接到了同一个自定义网络mynetwork,它们处于相同的网络命名空间,因此可以直接进行通信。