我司产品最近更换了license策略,当前的策略与mac地址强相关。我在使用docker部署应用时发现不同servicec产生的容器具有不同的mac地址,从而导致部分应用无法通过license验证。在弄清楚docker的网络基础后,我决定写下这篇文章,以作记录并分享。
Docker网络驱动
docker默认为用户提供了bridge、host、overlay、ipvlan、macvlan、none 六种网络驱动,除此之外docker还支持安装第三方网络插件。
Bridge
桥接网络是docker默认提供的网络驱动。桥接网络适用于在同一 Docker 守护进程主机上运行的容器。桥接网络使用软件桥接器,允许连接到同一桥接网络的容器进行通信,同时与未连接到该桥接网络的容器进行隔离。启动docker时,docker会自动创建一个默认的桥接网络,没有指定网络的容器会自动加入默认的Bridge网络。在生产环境中不推荐使用默认桥接网络,推荐使用用户自定义桥接网络。在用户定义的网络上(默认桥接网络上不支持),容器不仅可以通过 IP 地址通信,还可以将容器名称解析为 IP 地址。这种功能被称为自动服务发现(automatic service discovery)。自动服务发现只能解析自定义容器名称,而不能解析默认自动生成的容器名称。下面的示例介绍了桥接网络的一些使用方法:
$ docker network create --driver bridge sky-net
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
ea4149385972 bridge bridge local
b52c3a0b16b6 host host local
5ace4d37b28e none null local
39212517ca43 sky-net bridge local
可以看到此时多了一个NAME为 sky-net ,DRIVER为bridge的网络。
$ docker network inspect sky-net
[
{
"Name": "sky-net",
"Id": "39212517ca430ab2b20d7a6146981f9e26a179040fd2e2c187673330ab2af283",
"Created": "2023-08-14T09:33:20.6672236Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.19.0.0/16",
"Gateway": "172.19.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "ea4149385972507060305f96fc99cbb37ae5f1d408a1ee11d2d8764ecc9da6e2",
"Created": "2023-08-09T13:39:29.1437855Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
从上面的信息我们可以注意到,sky-net桥接网络的网关地址是172.19.0.1,默认桥接网络的网关地址是:172.17.0.1
docker run -dit --name app1 --network sky-net alpine ash
docker run -dit --name app2 --network sky-net alpine ash
docker run -dit --name app3 alpine ash
docker run -dit --name app4 --network sky-net alpine ash
docker network connect bridge app4
验证容器是不是在运行:
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a67b888fcaa4 alpine "ash" 15 seconds ago Up 8 seconds app4
21be79e6c881 alpine "ash" 23 seconds ago Up 16 seconds app3
5bfdb189138b alpine "ash" 28 seconds ago Up 23 seconds app2
6661cc226330 alpine "ash" 37 seconds ago Up 28 seconds app1
$ docker network inspect sky-net
[
{
"Name": "sky-net",
"Id": "39212517ca430ab2b20d7a6146981f9e26a179040fd2e2c187673330ab2af283",
"Created": "2023-08-14T09:33:20.6672236Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.19.0.0/16",
"Gateway": "172.19.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"5bfdb189138b5c429c7dafd213784b922229a6a32f2b22836e25f7646cf9f04b": {
"Name": "app2",
"EndpointID": "bba3bed49a1ac7a902d8394b636c1e88fb5d7d27fc33f61c9abe35b2c9d39321",
"MacAddress": "02:42:ac:13:00:03",
"IPv4Address": "172.19.0.3/16",
"IPv6Address": ""
},
"6661cc226330f6102732eb7d89719be91644e3259d9e44517915a775f3f1d5bd": {
"Name": "app1",
"EndpointID": "14f8e1e9d5f94864f5629eda5168cfec22beba78b80d9443abaa62bed6c8d5a7",
"MacAddress": "02:42:ac:13:00:02",
"IPv4Address": "172.19.0.2/16",
"IPv6Address": ""
},
"a67b888fcaa4ecc2c11c224967df945175550c523dbe3ec779f554b2961a28c7": {
"Name": "app4",
"EndpointID": "d8d2186a72464dca80fb3c39a0e1a2b7dd033b03555d3fbb7d345ed8965d0bbe",
"MacAddress": "02:42:ac:13:00:04",
"IPv4Address": "172.19.0.4/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
容器 app1 , app2 , app4 已连接到 sky-net网络
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "ea4149385972507060305f96fc99cbb37ae5f1d408a1ee11d2d8764ecc9da6e2",
"Created": "2023-08-09T13:39:29.1437855Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"21be79e6c88186f876ddf30f5abd0868132aa0e047928da03f1acfe11cd21b49": {
"Name": "app3",
"EndpointID": "bf21e7577015a04b776d6519db21844d97bf470bfac1cb8e9472d24318df639f",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"a67b888fcaa4ecc2c11c224967df945175550c523dbe3ec779f554b2961a28c7": {
"Name": "app4",
"EndpointID": "89524c8f6392946355bfef388b76ddce2c52c679ebc63ffc1b0ef894a49a5aae",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
容器 app3 和 app4 已连接到 默认桥接网络。
$ docker container attach app1
/ # ping -c 2 app2
PING app2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.067 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.053 ms
--- app2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.053/0.060/0.067 ms
# ping -c 2 app4
PING app4 (172.19.0.4): 56 data bytes
64 bytes from 172.19.0.4: seq=0 ttl=64 time=0.066 ms
64 bytes from 172.19.0.4: seq=1 ttl=64 time=0.055 ms
--- app4 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.055/0.060/0.066 ms
# ping -c 2 app1
PING app1 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.043 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.054 ms
--- app1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.043/0.048/0.054 ms
# ping -c 2 app3
ping: bad address 'app3'
不仅如此,我们也无法 在 app1 通过 IP地址来 访问 app3。 app3 的IP地址是:172.17.0.2
# ping -c 2 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
--- 172.17.0.2 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss
$ docker container attach app4
/ # ping -c 2 app1
PING app1 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.054 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.055 ms
--- app1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.054/0.054/0.055 ms
/ # ping -c 2 app2
PING app2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.096 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.062 ms
--- app2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.062/0.079/0.096 ms
/ # ping -c 2 app3
ping: bad address 'app3'
/ # ping -c 2 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.062 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.057 ms
--- 172.17.0.2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.057/0.059/0.062 ms
/ # ping -c 2 app4
PING app4 (172.19.0.4): 56 data bytes
64 bytes from 172.19.0.4: seq=0 ttl=64 time=0.025 ms
64 bytes from 172.19.0.4: seq=1 ttl=64 time=0.042 ms
--- app4 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.025/0.033/0.042 ms
Host
host 网络模式下,容器与主机共享网络命名空间,容器不会具有自己的IP地址。举个例子, 如果容器绑定80 端口并使用 host 网络,那么可以通过主机 IP 地址上的 80 端口访问容器的应用程序。host 网络可用于优化性能,也可用于容器需要处理大量端口的情况,因为它不需要网络地址转换(NAT),也不会为每个端口创建 "userland-proxy"。host 网络驱动程序仅适用于 Linux 主机。
Overlay
Overlay 网络驱动程序在多个 Docker 守护进程主机之间创建分布式网络。该网络位于主机特定的网络之上(overlays),允许连接到它的容器(包括Swarm服务容器)在启用加密时进行安全通信。当启用加密时,Docker会透明地处理每个数据包的路由,确保数据包正确地在不同的Docker守护程序主机之间以及到达正确的目标容器之间进行传递。这种方式实现了在多主机环境下的容器通信,并且确保了数据的安全性。需要在不同Docker主机上运行的容器进行通信,或者多个应用程序通过Swarm服务协同工作时 Overlay 是最佳选择。
Ipvlan
IPvlan 网络允许用户完全控制 IPv4和IPv6地址分配。VLAN驱动程序在此基础上构建,在为有兴趣进行底层网络集成的用户提供了完全控制第2层VLAN标记甚至IPvlan L3路由的能力。IPvlan 是一种新颖的、经过验证的网络虚拟化技术。Linux实现非常轻量级,因为它们与Linux以太网接口或子接口相关联,以实现网络之间的隔离和与物理网络的连接,而不是使用传统的Linux桥接进行隔离。IPvlan提供了许多独特的特性,并在各种模式下具有进一步创新的空间。这些方法的两个高级优点是,绕过Linux桥接带来的积极性能影响,以及减少移动部件的简单性。将传统上位于Docker主机NIC和容器接口之间的桥接移除,留下了一个简单的设置,其中容器接口直接附加到Docker主机接口。这种结果对于外部服务的访问非常方便,因为在这些情况下不需要端口映射。
Macvlan
Macvlan网络允许您为容器分配MAC地址,使其在网络上显示为物理设备。Docker守护程序通过容器的MAC地址路由流量。在处理希望直接连接到物理网络而不是通过Docker主机的网络堆栈进行路由的旧应用程序时,使用 macvlan 驱动程序有时是最佳选择。请记住以下几点:
- 由于IP地址耗尽或“VLAN扩散”(即网络中有大量不恰当的唯一MAC地址)可能会无意中降低网络性能。
- 您的网络设备需要支持“混杂模式”,其中一个物理接口可以被分配多个MAC地址。
- 如果您的应用程序可以使用桥接(在单个Docker主机上)或覆盖(在多个Docker主机之间进行通信),从长远来看,这些解决方案可能更好
None(no networking)
如果您想要完全隔离容器的网络堆栈,可以在启动容器时使用 --network none 标志。在容器内,只会创建回环设备
网络驱动总结
- 默认的桥接网络适用于不需要特殊网络功能的容器运行。
- 用户定义的桥接网络使得同一Docker主机上的容器可以彼此通信。用户定义的网络通常为属于共同项目或组件的多个容器定义了一个隔离的网络。
- 主机网络将主机的网络与容器共享。当使用这个驱动程序时,容器的网络与主机不隔离。
- 覆盖网络在需要在不同的Docker主机上运行的容器进行通信,或者多个应用程序使用Swarm服务协同工作时最为适用。
- Macvlan网络在从VM设置迁移或需要使容器看起来像网络上的物理主机时最为适用,每个主机都有一个唯一的MAC地址。
- IPvlan类似于Macvlan,但不会为容器分配唯一的MAC地址。当存在对网络接口或端口可以分配的MAC地址数量的限制时,考虑使用IPvlan。
- 第三方网络插件允许您将Docker与专门的网络堆栈集成。