Docker网络核心:单机自定义网络实战指南

大家好!在容器化的世界里,Docker已经成为不可或缺的工具。除了容器本身,容器间的网络通信同样至关重要。虽然Docker自带了bridgehostnone等默认网络模式,但在实际开发和生产环境中,默认的bridge网络存在一些局限性(比如容器间无法通过容器名直接通信,需要依赖旧的--link方式或手动管理IP)。为了更灵活、更可靠地管理容器网络,自定义网络成为了我们的首选。

本文将通过一系列实战操作,带你一步步了解如何在Docker单机环境下创建、管理和使用自定义网络,让你轻松驾驭容器间的通信。

阅读本文,你将学会:

  1. 查看和管理Docker网络。
  2. 创建基础的自定义桥接网络。
  3. 在自定义网络中运行容器,并实现容器名互访。
  4. 创建具有指定网段、网关、IP范围的高级自定义网络。
  5. 为容器分配静态IP地址。
  6. 清理不再使用的Docker网络。

准备工作:

  • 一台安装了Docker的主机(本文以root@docker102为例)。
  • 基本的Linux命令行操作知识。

一、初识Docker网络:查看现有网络

在开始创建自定义网络之前,我们先看看当前Docker环境中已有的网络。

root@docker102:~# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
870e105321fc   bridge    bridge    local  # 默认桥接网络
d97f63ed6d57   host      host      local  # Host网络模式
2646275ea427   none      null      local  # None网络模式

通常,你会看到至少这三个默认网络:

  • bridge: 容器默认连接的网络。启动容器时不指定--network,就会连接到这个网络。同一bridge网络内的容器可以通过IP地址互相访问,但默认不支持通过容器名访问
  • host: 容器共享宿主机的网络命名空间,拥有宿主机的IP和端口。性能好,但隔离性差。
  • none: 容器拥有独立的网络命名空间,但不进行任何网络配置,没有网络连接。

二、创建第一个自定义网络

默认bridge网络的主要痛点在于容器间无法通过服务名(容器名)直接通信。自定义网络完美解决了这个问题。让我们创建一个名为oldboyedu的自定义网络。

# 1. 创建自定义网络(默认使用 bridge 驱动)
root@docker102:~# docker network create oldboyedu
797363b947c9529c8d14964d70bb9752e135600300a4fd9cb5099d5d330f6760

# 2. 再次查看网络列表,确认已创建
root@docker102:~# docker network ls
NETWORK ID     NAME        DRIVER    SCOPE
870e105321fc   bridge      bridge    local
d97f63ed6d57   host        host      local
2646275ea427   none        null      local
797363b947c9   oldboyedu   bridge    local  # 新创建的网络

我们看到oldboyedu网络已经创建成功,它使用的驱动也是bridge,但这是一个用户自定义的bridge网络,与默认的bridge网络是隔离的。

三、深入了解网络详情:检查网络配置

我们可以使用docker network inspect命令查看网络的详细信息,包括它的网段、网关等。

root@docker102:~# docker network inspect oldboyedu
[
    {
        "Name": "oldboyedu",
        "Id": "797363b947c9...", // ID已省略
        "Created": "2024-07-23T09:13:59.264513362+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": { // IP地址管理配置
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",  // 自动分配的子网
                    "Gateway": "172.19.0.1"   // 自动分配的网关
                }
            ]
        },
        "Internal": false,
        "Attachable": false, // 在Swarm模式下,控制非服务容器是否能连接
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {}, // 当前连接到此网络的容器列表(现在为空)
        "Options": {},
        "Labels": {}
    }
]

可以看到,Docker自动为oldboyedu网络分配了子网172.19.0.0/16和网关172.19.0.1

四、连接容器:在自定义网络中运行应用

现在,我们在oldboyedu网络中启动两个容器c1c2

# 使用的镜像是包含基本网络工具(如ping)的简单Web应用镜像
IMAGE_NAME="registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1"

# 1. 启动容器 c1,并将其连接到 oldboyedu 网络
root@docker102:~# docker run -d --name c1 --network oldboyedu $IMAGE_NAME
d5e6af85962a...

# 2. 启动容器 c2,同样连接到 oldboyedu 网络
root@docker102:~# docker run -d --name c2 --network oldboyedu $IMAGE_NAME
e6bba8b4701d...

# 3. 查看容器 c1 的 IP 地址
root@docker102:~# docker container inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" c1
172.19.0.2

# 4. 查看容器 c2 的 IP 地址
root@docker102:~# docker container inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" c2
172.19.0.3

注意:我们使用了--network oldboyedu参数将容器连接到了我们创建的网络。Docker会从网络的可用IP池(172.19.0.0/16)中自动为c1c2分配IP地址(172.19.0.2172.19.0.3)。

五、验证通信:容器名解析的魔力

自定义网络最核心的优势之一就是内置了DNS服务。这意味着同一自定义网络内的容器可以通过容器名直接互相访问。让我们进入c1容器来验证一下。

# 1. 进入 c1 容器的 shell 环境
root@docker102:~# docker exec -it c1 sh

# 2. 查看 c1 容器内的 hosts 文件
# 注意:Docker会自动将同网络容器名和IP的映射关系添加到hosts文件(或者通过内置DNS解析)
/ # cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.19.0.3	c2             # Docker 自动添加了 c2 的映射 (在较新版本中可能不直接写入hosts,而是通过内置DNS)
172.19.0.2	d5e6af85962a   # c1 自身的映射

# 3. 在 c1 容器内,直接通过容器名 ping c2
/ # ping c2 -c 3
PING c2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.134 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.079 ms
64 bytes from 172.19.0.3: seq=2 ttl=64 time=0.077 ms

--- c2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.077/0.096/0.134 ms

# 4. 退出容器
/ # exit

成功了!c1可以直接pingc2,无需关心c2的具体IP地址。这对于构建微服务应用或任何需要容器间通信的场景来说,都极大地简化了配置。

六、网络管理:清理不再使用的网络

随着时间的推移,可能会创建一些测试或临时的网络。Docker提供了清理这些未使用网络的方法。

6.1 批量删除所有未使用的网络

# 1. 先创建两个临时的未使用网络
root@docker102:~# docker network create xixi
71040ade4e22...
root@docker102:~# docker network create haha
c890b2f9fc1f...

# 2. 查看当前网络列表
root@docker102:~# docker network ls
NETWORK ID     NAME        DRIVER    SCOPE
...
c890b2f9fc1f   haha        bridge    local # 新网络
797363b947c9   oldboyedu   bridge    local # 正在使用
71040ade4e22   xixi        bridge    local # 新网络
...

# 3. 使用 prune 命令批量删除所有未被容器连接的网络
# -f 参数表示强制执行,不进行确认提示,生产环境慎用!
root@docker102:~# docker network prune -f
Deleted Networks:
xixi
haha

# 4. 再次查看,确认未使用网络已被删除
root@docker102:~# docker network ls
NETWORK ID     NAME        DRIVER    SCOPE
...
797363b947c9   oldboyedu   bridge    local # 正在使用的 oldboyedu 仍然存在
...

重要提示:

  • docker network prune只会删除未被任何容器(包括已停止的容器)使用自定义网络。
  • 默认的bridge, host, none网络不会被prune命令删除。
  • 在生产环境中使用prune命令(尤其带-f)前,请务必确认不会误删重要网络。

6.2 删除指定的网络

如果你只想删除某个或某几个特定的网络,可以使用docker network rm命令。

# 1. 再次创建两个临时网络
root@docker102:~# docker network create xixi
1b64707304a0...
root@docker102:~# docker network create haha
f8e49cbe8464...

# 2. 查看列表
root@docker102:~# docker network ls
NETWORK ID     NAME        DRIVER    SCOPE
...
f8e49cbe8464   haha        bridge    local
797363b947c9   oldboyedu   bridge    local
1b64707304a0   xixi        bridge    local
...

# 3. 删除指定的 xixi 和 haha 网络
root@docker102:~# docker network rm xixi haha
xixi
haha

# 4. 确认已删除
root@docker102:~# docker network ls
NETWORK ID     NAME        DRIVER    SCOPE
...
797363b947c9   oldboyedu   bridge    local
...

注意: 如果有任何容器(即使是已停止的)连接到你尝试删除的网络,docker network rm命令会失败报错,需要先断开或删除这些容器。

七、高级定制:创建具有特定参数的网络

有时,自动分配的网段可能与你现有的网络环境冲突,或者你希望对容器的IP地址范围进行更精细的控制。docker network create提供了丰富的参数来实现这些需求。

7.1 创建自定义子网、网关和IP范围

假设我们想创建一个网络linux92,要求:

  • 使用bridge驱动。
  • 整个子网是172.21.0.0/16
  • 网关地址是172.21.0.254
  • Docker自动分配给容器的IP地址必须在172.21.100.0/24这个范围内。
root@docker102:~# docker network create \
  -d bridge \                 # 指定驱动为 bridge (虽然默认就是)
  --subnet 172.21.0.0/16 \    # 定义整个网络的子网范围
  --gateway 172.21.0.254 \   # 指定网关 IP
  --ip-range 172.21.100.0/24 \ # 指定容器自动获取 IP 的范围
  linux92                    # 网络名称
ee50da66577d...

# 查看新网络的配置
root@docker102:~# docker network inspect linux92
[
    {
        "Name": "linux92",
        ...
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.21.0.0/16",     # 确认子网
                    "IPRange": "172.21.100.0/24", # 确认IP范围
                    "Gateway": "172.21.0.254"     # 确认网关
                }
            ]
        },
        ...
        "Containers": {},
        ...
    }
]

7.2 在定制网络中运行容器并验证IP

现在,我们在这个linux92网络中启动容器,看看它们的IP地址是否符合预期。

# 清理之前的同名容器(如果存在)
# docker rm -f c1 c2 c3 || true

# 启动 c1 和 c2 到 linux92 网络
root@docker102:~# docker container run -d --name c1 --network linux92 $IMAGE_NAME
3d461764e6bf...
root@docker102:~# docker container run -d --name c2 --network linux92 $IMAGE_NAME
5bbf3289b885...

# 查看 c1 的网络接口信息
root@docker102:~# docker exec c1 ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 02:42:AC:15:64:00
          inet addr:172.21.100.0  Bcast:172.21.255.255  Mask:255.255.0.0 # IP 在 172.21.100.0/24 范围内
          ...

# 查看 c2 的网络接口信息
root@docker102:~# docker exec c2 ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 02:42:AC:15:64:01
          inet addr:172.21.100.1  Bcast:172.21.255.255  Mask:255.255.0.0 # IP 也在 172.21.100.0/24 范围内
          ...

正如预期,c1c2自动获取的IP地址172.21.100.0172.21.100.1都落在了我们通过--ip-range指定的172.21.100.0/24范围内。

7.3 手动为容器分配固定IP地址

在某些场景下,比如配置依赖特定IP的服务或防火墙规则时,我们希望容器拥有一个固定的IP地址。可以使用--ip参数在docker run时指定。

# 启动 c3,并手动指定 IP 为 172.21.1.102
root@docker102:~# docker container run -d --name c3 --network linux92 --ip 172.21.1.102 $IMAGE_NAME
2f0cfe0936dd...

# 查看 c3 的网络接口信息
root@docker102:~# docker exec c3 ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 02:42:AC:15:01:66
          inet addr:172.21.1.102  Bcast:172.21.255.255  Mask:255.255.0.0 # IP 是我们手动指定的
          ...

关键点:

  • 手动指定的--ip地址必须位于网络的--subnet范围内(172.21.0.0/16)。
  • 手动指定的--ip地址不必位于--ip-range范围内(172.21.100.0/24)。--ip-range仅用于Docker自动分配IP地址。
  • 指定的IP地址不能已被其他容器或网关使用。

7.4 最终验证:固定IP与自动分配IP容器间的通信

最后,我们验证一下手动分配IP的c3能否与自动分配IP的c1通信。

# 在 c3 容器内 ping c1 (通过容器名)
root@docker102:~# docker exec c3 ping c1 -c 3
PING c1 (172.21.100.0): 56 data bytes
64 bytes from 172.21.100.0: seq=0 ttl=64 time=0.239 ms
64 bytes from 172.21.100.0: seq=1 ttl=64 time=0.097 ms
64 bytes from 172.21.100.0: seq=2 ttl=64 time=0.079 ms

--- c1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.079/0.138/0.239 ms

通信完全正常!这证明了在同一个自定义网络中,无论是自动分配IP还是手动指定IP的容器,都可以利用Docker提供的DNS服务通过容器名进行无缝通信。

八、总结与生产建议

通过本文的实战演练,我们掌握了Docker单机自定义网络的核心用法:

  1. 创建自定义网络 (docker network create) 是实现容器间可靠通信(尤其是通过容器名)的基础。
  2. 检查网络配置 (docker network inspect) 帮助我们了解网络的拓扑结构。
  3. 连接容器到网络 (docker run --network) 是应用自定义网络的关键步骤。
  4. 内置DNS解析 是自定义网络相较于默认bridge网络的最大优势。
  5. 高级定制 (--subnet, --gateway, --ip-range) 提供了对网络地址规划的精细控制。
  6. 静态IP分配 (--ip) 满足了特定场景下固定IP的需求。
  7. 网络清理 (docker network prune, docker network rm) 是保持环境整洁的必要操作。

生产环境建议:

  • 优先使用自定义网络: 对于任何需要多个容器协作的应用(如微服务架构),强烈建议使用自定义网络,而不是依赖默认的bridge网络。
  • 有意义地命名网络: 给网络起一个能反映其用途的名字(如 app-backend-net, database-net),方便管理和识别。
  • 规划IP段: 在复杂的环境中,提前规划好自定义网络的子网和IP范围,避免与宿主机或其他网络环境产生冲突。
  • 使用Docker Compose: 对于多容器应用,使用docker-compose.yml来定义服务和网络是更推荐的方式。Compose可以自动创建和管理网络,简化部署流程。
  • 了解其他驱动: 除了bridge,Docker还支持overlay(用于Docker Swarm集群跨主机通信)、macvlan(让容器在物理网络上拥有独立MAC和IP)等网络驱动,根据需求选用。

希望这篇详细的指南能帮助你更好地理解和运用Docker的自定义网络功能,为你的容器化应用构建稳定、高效的网络基础。如果你有任何问题或想法,欢迎在评论区交流!


posted on 2025-04-08 16:37  Leo-Yide  阅读(105)  评论(0)    收藏  举报