docker 网络-用户定义网络

如果你的dokcer版本更新到1.10+,你会发现docker cli中的管理命令中多出一个选项: network Manage networks

Management Commands:
  builder     Manage builds
  buildx*     Docker Buildx
  compose*    Docker Compose
  container   Manage containers
  context     Manage contexts
  image       Manage images
  manifest    Manage Docker image manifests and manifest lists
  network     Manage networks
  plugin      Manage plugins
  system      Manage Docker
  trust       Manage trust on Docker images
  volume      Manage volumes

使用Help查看下命令的使用方式

[root@package image]# docker network --help 
Usage:  docker network COMMAND

Manage networks

Commands:
  connect     Connect a container to a network
  create      Create a network
  disconnect  Disconnect a container from a network
  inspect     Display detailed information on one or more networks
  ls          List networks
  prune       Remove all unused networks
  rm          Remove one or more networks

Run 'docker network COMMAND --help' for more information on a command.

docker network ls

[root@package image]# docker network ls
NETWORK ID     NAME            DRIVER    SCOPE
0d04f5291ccc   bridge          bridge    local
9c5758e54af4   host            host      local
384c4cfdaf03   none            null      local

默认情况下,基础 Docker 安装已定义这三个网络。这些网络是永久的,无法修改。仔细观察,您可能会注意到这些预定义网络与我们在早期 Docker 版本中使用的网络模型相同。您可以默认以桥接模式启动容器,也可以指定“–net=host”以主机模式启动容器,还可以指定“–net=none”以不使用网络接口启动容器。简而言之,之前的所有网络设置都保留了下来。为了确保一切仍然按预期运行,让我们在每种网络类型下构建一个容器。

Host mode主机模式

docker run -d --name web1 --net=host nginx:stable-alpine3.21

执行上述命令将启动一个测试 nginx Web 服务器,容器的网络堆栈将直接映射到主机的网络堆栈。容器运行后,我们应该能够通过 Docker 主机的 IP 地址访问 Web 服务器…

注意:您需要在 Docker 主机上禁用防火墙或添加适当的规则才能使其正常工作

Bridge Mode 桥接模式

docker run -d --name web1 -p 8081:80 nginx:stable-alpine3.21
docker run -d --name web2 -p 8082:80 nginx:stable-alpine3.21

这里我们运行默认的桥接模式,并将端口映射到容器中。运行这两个容器应该会在端口 8081 和 8082 上显示所需的 Web 服务器……

此外,如果我们直接连接到容器,我们可以看到两个容器之间的通信直接通过 docker0 网桥进行
![[Pasted image 20250527195505.png]]

这里我们可以看到 web1 上有一个 web2 的 APR 条目。查看 Web2 的 MAC 地址,我们发现两者完全相同……
在这里插入图片描述

None mode 无网络模式

docker run -d --name web1 --net=none nginx:stable-alpine3.21

在这个例子中,我们可以看到容器根本没有接收任何接口……

[root@package image]# docker exec -it web1 /bin/sh
/ # ifconfig
lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

如你所见,这三种模式的工作方式与 Docker 早期版本中的一样。既然我们已经介绍了现有的网络功能,现在让我们来谈谈新的用户定义网络……

User defined bridge networks 用户定义的桥接网络

最容易使用的用户自定义网络是网桥。定义一个新的网桥非常简单。这里有一个简单的例子……

docker network create --driver=bridge --subnet=192.168.127.0/24 --gateway=192.168.127.1 --ip-range=192.168.127.128/25 testbridge

网关 – 在本例中,我将其设置为 192.168.127.1,这将是 Docker 主机上创建的网桥的 IP 地址。我们可以通过查看 Docker 主机接口来查看……

[root@package ~]# docker network ls
NETWORK ID     NAME            DRIVER    SCOPE
358bb3bf4fb1   bridge          bridge    local
bde3f7cf4b93   host            host      local
c67ef592c63e   none            null      local
7a95dd37854b   testbridge      bridge    local

[root@package ~]# ifconfig 
br-26b47815a863: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.18.0.1  netmask 255.255.0.0  broadcast 172.18.255.255
        inet6 fe80::42:12ff:fe5e:a49f  prefixlen 64  scopeid 0x20<link>
        ether 02:42:12:5e:a4:9f  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

br-7a95dd37854b: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 192.168.127.1  netmask 255.255.255.0  broadcast 192.168.127.255
        ether 02:42:85:e0:17:8e  txqueuelen 0  (Ethernet)
        RX packets 1578425  bytes 1405516662 (1.3 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1128558  bytes 87851971 (83.7 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

子网 – 我将其指定为 192.168.127.0/24。我们可以在上面的输出中看到,这是与网桥关联的 CIDR。

我们可以通过删除现有容器、删除“testbridge”并稍微重新定义它来使这个例子更有趣……

docker stop web1
docker rm web1

docker network rm testbridge

docker network create --driver=bridge --subnet=192.168.127.0/24 --gateway=192.168.127.1 --ip-range=192.168.127.128/25 –-internal testbridge

这里唯一的变化是添加了“—internal”标志。这将阻止来自网桥的任何外部通信。让我们通过像这样定义容器来检查一下……

docker run -d --net=testbridge -p 8081:80 --name web1 nginx:stable-alpine3.21

在该网桥上启动的容器,如果通过主机网络访问将会得到一个不可达的错误
显然,“—internal”标志会阻止连接到网桥的容器与主机外部通信。因此,虽然我们现在可以定义新的网桥并将新生成的容器关联到它们,但这本身并没有什么意义。更有趣的是将现有容器连接到这些新网桥的能力。幸运的是,我们可以使用 docker network 的“connect”和“disconnect”命令在任何已定义的网桥上添加和删除容器。让我们首先将容器 web1 连接到默认的 docker0 网桥(网桥)……

docker network connect bridge web1

如果我们查看容器的网络配置,可以看到它现在有两个网卡。一个与“bridge”(docker0 网桥)关联,另一个与“testbridge”关联……
在这里插入图片描述

如果我们再次检查,我们会发现我们现在可以再次通过“桥接”接口上的映射端口访问 Web 服务器……
在这里插入图片描述

我们有一台物理主机 (docker1),其网卡名为“ENS0”,位于 IP 地址为 10.20.30.230 的物理网络中。该主机有两个 Linux 网桥,分别名为“bridge”(docker0)和“testbridge”,每个网桥都有各自定义的 IP 地址。我们还有两个容器,一个名为 web1,它同时与两个网桥关联;另一个名为 web2,它只与原生 Docker 网桥关联。
鉴于此图,您可能会认为 web1 和 web2 能够直接相互通信,因为它们连接到同一个网桥。但是,如果您还记得我们之前的帖子,Docker 有一种称为 ICC(容器间通信)模式的机制。当 ICC 设置为 false 时,容器将无法通过 docker0 网桥直接相互通信。

在这个例子中,我将 ICC 模式设置为 false,这意味着除非我们定义链接,否则 web1 无法通过 docker0 网桥与 web2 通信。但是,ICC 模式仅适用于默认网桥 (docker0)。如果我们将两个容器都连接到网桥“testbridge”,它们应该能够通过该网桥直接通信。我们来试试吧……

docker network connect testbridge web2

在这里插入图片描述

那么让我们从容器中尝试一下,看看会发生什么……
在这里插入图片描述

成功了!用户自定义网桥很容易定义,并且能够将容器映射到网桥上。

Container Linking 容器链接

Docker 的 Linking 机制自早期版本就已存在,并且经常被误认为是某种网络功能。实际上,它与网络策略几乎没有关系,尤其是在 Docker 的默认配置中
在默认配置中,Docker 将 ICC 值设置为 true。在此模式下,docker0 网桥上的所有容器都可以在它们指定的任意端口上直接通信。我们之前在网桥模式示例中看到过这种情况,其中 web1 和 web2 能够互相 ping 通。如果我们更改默认配置并禁用 ICC,则会看到不同的结果。例如,如果我们在“/etc/sysconfig/docker”中将 ICC 值更改为“false”,我们会发现上述示例不再有效……
在这里插入图片描述

如果我们希望 web1 能够访问 web2,我们可以将容器“链接”起来。将一个容器链接到另一个容器,可以让容器之间通过容器暴露的端口相互通信。
在这里插入图片描述

上图显示,一旦链接到位,我无法从 web2 ping 通 web1,但我可以通过其暴露的端口访问 web1。在本例中,该端口为 80。因此,禁用 ICC 的链接仅允许链接的容器在其暴露的端口上相互通信。这是将链接利益与网络或安全策略关联的唯一方式。链接提供的另一个功能是名称和服务解析。例如,让我们看看将 web2 链接到 web1 后,web2 上的环境变量……
在这里插入图片描述

除了环境变量之外,您还会注意到 web2 的 hosts 文件已更新,包含 web1 容器的 IP 地址。这意味着我现在可以通过名称而不是 IP 地址访问容器。如您所见,Docker 早期版本中的链接功能已有其用途,并且该功能现在仍然可用。
用户自定义网络提供了一种非常巧妙的链接替代方案。让我们回到上面的例子,web1 和 web2 通过“testbridge”网桥进行通信。目前,我们还没有定义任何链接,但让我们尝试从 web1 通过名称 ping web2……
在这里插入图片描述

好的,这很酷,但它是如何工作的呢?让我们检查一下容器上的环境变量和 hosts 文件……
在这里插入图片描述

这里根本没有任何内容可以将名称 web2 静态映射到 IP 地址。那么它是如何工作的呢?Docker 现在拥有一个内置的 DNS 服务器。容器名称现在已在 Docker 守护进程中注册,并且可以被同一主机上的任何其他容器解析。但是,此功能仅适用于用户定义的网络。您会注意到,上面的 ping 命令返回的是与“testbridge”关联的 IP 地址,而不是默认的 docker0 网桥。
这意味着我不再需要静态地将容器链接在一起,以便它们能够通过名称进行通信。除了这种自动行为之外,您还可以在用户定义的网络上定义全局别名和链接。例如,现在尝试运行这两个命令……

docker network disconnect testbridge web1
docker network connect --alias=thebestserver --link=web2:webtwo testbridge web1

上面我们从“testbridge”中删除了 web1,然后重新添加它并指定了链接和别名。当将链接标志与用户定义网络一起使用时,其功能与传统链接方法中的功能大致相同。Web1 将能够通过容器名称或其链接别名“webtwo”来解析容器 web2。此外,用户定义网络还提供所谓的“网络范围别名”。这些别名可以由用户定义网段上的任何容器解析。因此,链接由希望使用该链接的容器定义,而别名由发布该别名的容器定义。让我们登录到每个容器并尝试通过链接和别名进行 ping 操作……
在这里插入图片描述

User defined overlay networks 用户定义的覆盖网络

第二种也是最后一种内置用户定义网络类型是覆盖网络 (Overlay)。与桥接网络不同,覆盖网络需要外部键值存储来存储网络、端点、IP 地址和发现信息等信息。在大多数示例中,该键值存储通常是 Consul,但也可以是 Etcd 或 ZooKeeper。那么,让我们来看看本例中要使用的实验……

在这里插入图片描述

这里我们有 3 个 Docker 主机。Docker1 和 docker2 位于 10.20.30.0/24 上,docker3 位于 192.168.30.0/24 上。所有主机都从零开始,定义了 docker0 网桥,但没有运行其他用户定义的网络或容器。
我们要做的第一件事是告诉所有 Docker 主机在哪里找到键值存储。这可以通过编辑“/etc/sysconfig/docker”中的 Docker 配置设置并添加一些选项标志来完成。就我而言,我的“OPTIONS”现在如下所示……

OPTIONS='-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=consul://10.20.30.230:8500/network --cluster-advertise=ens18:2375'

确保调整选项以涵盖运行 Consul 的 Docker 主机的 IP 地址以及“cluster-advertise”标志下定义的接口名称。请在所有加入集群的主机上更新这些选项,然后重启 Docker 服务。
Docker 恢复运行后,我们需要部署前面提到的键值存储,供覆盖驱动程序使用。幸运的是,Consul 以容器的形式提供服务。那么,让我们在 docker1 上部署这个容器……

docker run -d -p 8500:8500 -h consul --name consul progrium/consul -server -bootstrap

一旦 Consul 容器运行起来,我们就可以开始定义覆盖网络了。让我们转到 docker3 主机并定义一个覆盖网络……

docker network create -d overlay --subnet=10.10.10.0/24 testoverlay

现在,如果我们查看 docker1 或 docker2,我们应该看到新定义的覆盖……
在这里插入图片描述

完美,一切按预期进行。现在我们在主机 docker3 上运行一个 Web 容器……
注意:与网桥不同,覆盖网络不会在 Docker 主机上预先创建所需的接口,直到容器使用它们为止。如果您在创建网络时没有看到这些接口生成,请不要感到惊讶。

docker run -d --net=testoverlay -p 8081:80 --name web1 jonlangemak/docker:webinstance1

让我们在 docker2 上启动同一个容器,看看我们得到了什么……
在这里插入图片描述

所以看起来,跨用户自定义覆盖层的容器名称不可能通用。这很合理,所以我们改为加载此主机上的第二个 Web 实例……

docker run -d --net=testoverlay -p 8082:80 --name web2 jonlangemak/docker:webinstance2

一旦此容器运行,让我们通过从 web2 ping web1 来测试覆盖…
![[Pasted image 20250528100942.png]]

很酷。如果我们观察 docker2 和 docker3 之间的物理网络,我们实际上会看到 VXLAN 封装的数据包在两个物理 docker 主机之间传输……

在这里插入图片描述

需要注意的是,覆盖网络本身并没有关联的网桥。但是,每台主机上都定义了一个网桥,用于将物理主机的端口映射到覆盖网络中的容器。例如,让我们看看主机 docker3 上定义的接口……
在这里插入图片描述

注意,这里定义了一个“docker_gwbridge”网桥。如果我们查看容器本身的接口,我们会发现它也有两个接口……
Eth0 是覆盖网络的成员,而 eth1 是我们在主机上看到的网关桥的成员。如果您需要从覆盖网络上的容器公开端口,则需要使用“docker_gwbridge”桥。但是,与用户定义的桥非常相似,您可以在网络创建期间指定“—internal”标志来阻止外部访问。这将阻止容器接收与网关桥关联的额外接口。但这不会阻止在主机上创建“docker_gwbridge”。
由于我们的最后一个例子将使用内部覆盖,让我们删除 web1 和 web2 容器以及覆盖网络,并使用内部标志重建覆盖网络……

#Docker2
docker stop web2
docker rm web2

#Docker3
docker stop web1
docker rm web1
docker network rm testoverlay
docker network create -d overlay --internal --subnet=10.10.10.0/24 internaloverlay
docker run -d --net=internaloverlay --name web1 jonlangemak/docker:webinstance1

#Docker 2
docker run -d --net=internaloverlay --name web2 jonlangemak/docker:webinstance2

现在我们有两个容器,每个容器在覆盖网络上都有一个接口。让我们确保它们可以相互通信……
在这里插入图片描述

完美,叠加层正常工作了。所以现在——我们的图表看起来就像这样……
![[Pasted image 20250528101321.png]]

目前看来,情况并不乐观,尤其是考虑到我们无法从外部访问这两个容器上运行的 Web 服务器。为了解决这个问题,我们可以在 docker1 上部署一个负载均衡器。为此,我们将使用 HAProxy,因此第一步是创建一个配置文件。我使用的示例如下所示……

global
    log 127.0.0.1   local0
    log 127.0.0.1   local1 notice

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    option forwardfor
    option http-server-close
    timeout connect 5000
    timeout client 50000
    timeout server 50000
    stats enable
    stats auth user:password
    stats uri /haproxyStats

frontend all
    bind *:80
    use_backend webservice

backend webservice
    balance roundrobin
    option httpclose
    option forwardfor
    server web1 web1:80 check
    server web2 web2:80 check
    option httpchk HEAD /index.html HTTP/1.0

为了进行此测试,我们只需关注后端部分,它定义了两个服务器。一个名为 web1,可通过 web1:80 地址访问;另一个名为 web2,可通过 web2:80 地址访问。将此配置文件保存到主机 docker1 上,在本例中,我将其保存在“/root/haproxy/haproxy.cfg”中。然后,我们只需使用以下语法启动容器即可……

docker run -d --net=internaloverlay --name haproxy -p 80:80 -v ~/haproxy:/usr/local/etc/haproxy/ haproxy

这个容器启动后,我们的拓扑现在看起来更像这样
在这里插入图片描述

因此,虽然 HAProxy 容器现在可以与两个后端服务器通信,但我们仍然无法在前端与其通信。如果您还记得的话,我们将覆盖网络定义为内部网络,因此我们需要找到另一种访问前端的方法。这可以通过使用网络连接命令将 HAProxy 容器连接到原生 docker0 网桥轻松实现……

docker network connect bridge haproxy

完成后,您应该能够通过访问 docker1 主机的端口 80 来访问 HAProxy 容器的前端,因为这是我们公开的端口
如果运气好的话,你应该会看到 HAProxy 节点在两个 Web 服务器容器之间负载均衡请求。注意,这也可以通过将覆盖网络保留为外部网络来实现。在这种情况下,我们为 HAProxy 容器进行的 80 端口映射将通过“docker_gwbridge”网桥公开,我们无需在容器中添加第二个网桥接口。我这样做只是为了向你展示你还有其他选择。

原英文链接: https://www.dasblinkenlichten.com/docker-networking-101-user-defined-networks/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Achilles.Wang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值