第 5 章 docker网络

文章目录

  • [第 5 章 docker网络](#第 5 章 docker网络)
    • [5.1 none 和 host 网络的适用场景](#5.1 none 和 host 网络的适用场景)
      • [none 网络](#none 网络)
      • [none 网络适用场景](#none 网络适用场景)
      • [host 网络](#host 网络)
      • [host 网络适用场景](#host 网络适用场景)
    • [5.2 学容器必须懂 bridge 网络](#5.2 学容器必须懂 bridge 网络)
      • [实践:bridge 网络工作原理](#实践:bridge 网络工作原理)
        • [步骤 1:安装 bridge-utils 工具(查看网桥)](#步骤 1:安装 bridge-utils 工具(查看网桥))
        • [步骤 2:创建容器,观察网桥变化](#步骤 2:创建容器,观察网桥变化)
        • [步骤 3:查看容器网络配置](#步骤 3:查看容器网络配置)
        • [步骤 4:验证 bridge 网络配置](#步骤 4:验证 bridge 网络配置)
        • [步骤 5:验证 host 上的 veth 接口](#步骤 5:验证 host 上的 veth 接口)
    • [5.3 如何自定义容器网络?](#5.3 如何自定义容器网络?)
      • [创建自定义 bridge 网络](#创建自定义 bridge 网络)
        • [示例 1:创建默认配置的自定义 bridge 网络](#示例 1:创建默认配置的自定义 bridge 网络)
        • [示例 2:指定子网和网关创建自定义 bridge 网络](#示例 2:指定子网和网关创建自定义 bridge 网络)
      • 容器使用自定义网络
        • [示例 1:容器加入自定义网络(自动分配 IP)](#示例 1:容器加入自定义网络(自动分配 IP))
        • [示例 2:指定静态 IP 启动容器](#示例 2:指定静态 IP 启动容器)
      • 注意事项
    • [5.4 理解容器之间的连通性](#5.4 理解容器之间的连通性)
      • 同一网络容器连通性(my_net2)
      • [不同网络容器连通性(docker0 vs my_net2)](#不同网络容器连通性(docker0 vs my_net2))
      • [原因:Docker 网络隔离(iptables 规则)](#原因:Docker 网络隔离(iptables 规则))
      • [解决:容器加入多个网络(docker network connect)](#解决:容器加入多个网络(docker network connect))
    • [5.5 容器通信的三种方式](#5.5 容器通信的三种方式)
      • [1. IP 通信](#1. IP 通信)
      • [2. Docker DNS Server](#2. Docker DNS Server)
        • 实践示例
        • [步骤 1:在 user-defined 网络中启动容器(指定名称)](#步骤 1:在 user-defined 网络中启动容器(指定名称))
        • [步骤 2:通过容器名通信](#步骤 2:通过容器名通信)
        • [限制:默认 bridge 网络不支持 DNS 解析](#限制:默认 bridge 网络不支持 DNS 解析)
      • [3. joined 容器](#3. joined 容器)
        • 实践示例
        • [步骤 1:启动基础容器(web1,httpd 服务)](#步骤 1:启动基础容器(web1,httpd 服务))
        • [步骤 2:创建 joined 容器(共享 web1 网络栈)](#步骤 2:创建 joined 容器(共享 web1 网络栈))
        • [步骤 3:通过 127.0.0.1 访问 web1 服务](#步骤 3:通过 127.0.0.1 访问 web1 服务)
      • [joined 容器适用场景](#joined 容器适用场景)
    • [5.6 容器如何访问外部世界](#5.6 容器如何访问外部世界)
    • [5.7 外部世界如何访问容器](#5.7 外部世界如何访问容器)
        • 访问容器服务
        • [动态端口映射(不指定 host 端口)](#动态端口映射(不指定 host 端口))
      • 端口映射原理:docker-proxy
      • [实战:安装 Tomcat 并映射端口](#实战:安装 Tomcat 并映射端口)
        • [步骤 1:搜索并拉取 Tomcat 镜像](#步骤 1:搜索并拉取 Tomcat 镜像)
        • [步骤 2:启动 Tomcat 容器(端口映射)](#步骤 2:启动 Tomcat 容器(端口映射))
        • [步骤 3:访问 Tomcat 首页(解决 404 问题)](#步骤 3:访问 Tomcat 首页(解决 404 问题))
        • [步骤 4:重新访问 Tomcat 首页](#步骤 4:重新访问 Tomcat 首页)
      • 本章小结
  • 进入容器
  • [替换 webapps 目录](#替换 webapps 目录)

第 5 章 docker网络

5.1 none 和 host 网络的适用场景

本章开始讨论 Docker 网络。

我们会首先学习 Docker 提供的几种原生网络,以及如何创建自定义网络。然后探讨容器之间如何通信,以及容器与外界如何交互。

Docker 网络从覆盖范围可分为单个 host 上的容器网络和跨多个 host 的网络,本章重点讨论前一种。对 于更为复杂的多 host 容器网络,我们会在后面进阶技术章节单独讨论。

Docker 安装时会自动在 host 上创建三个网络,我们可用 docker network ls 命令查看:

复制代码
[root@docker ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
a1b809382f0d bridge bridge local
6ad21ed9ea1f host host local
4e31521b27c6 none null local

下面我们分别讨论它们。

none 网络

none 网络的 driver 类型是 null,IPAM 字段为空。挂在 none 网络上的容器只有 lo, 无法与外界通信。

故名思议,none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo, 没有其他任何网卡。容器 创建时,可以通过 --network=none 指定使用 none 网络。

复制代码
[root@docker ~]# docker run -it --network=none busybox
/ # 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)
/ # hostname
4027da12afaf

none 网络适用场景

封闭意味着隔离,一些对安全性要求高并且不需要联网的应用可以使用 none 网络。

比如某个容器的唯一用途是生成随机密码,就可以放到 none 网络中避免密码被窃取。

host 网络

挂在 host 网络上的容器共享宿主机的 network namespace。即容器的网络配置与 host 网络配置完全一 样。

复制代码
host
host network namespace
C1 nginx
ens160:192.168.108.30

连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样。可以通过 --network=host 指定使用 host 网络。

复制代码
[root@docker ~]# docker run -it --network=host busybox
/ # ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq qlen 1000
    link/ether 00:0c:29:33:15:f6 brd ff:ff:ff:ff:ff:ff
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
    link/ether 02:42:02:fd:82:b3 brd ff:ff:ff:ff:ff:ff

在容器中可以看到 host 的所有网卡,并且连 hostname 也是 host 的。

host 网络适用场景

  1. 性能优先:直接使用 Docker host 的网络,无需网络转发,性能最高,适合对网络传输效率有高要求的场景。
  2. 配置 host 网络:部分跨 host 的网络解决方案(以容器方式运行),需要直接配置 host 网络(如管理 iptables)。

注意:使用 host 网络需考虑端口冲突,Docker host 上已使用的端口,容器不能再使用。

下一节讨论应用更广的 bridge 网络。

5.2 学容器必须懂 bridge 网络

上一节我们讨论了 none 和 host 类型的容器网络,本节学习应用最广泛也是默认的 bridge 网络。

Docker 安装时会创建一个命名为 docker0 的 linux bridge, 实际上它是 Linux 的一个 bridge (网桥), 可以理解为一个软件交换机,它会在挂载到它的网口之间进行转发。如果不指定--network , 创建的容 器默认都会挂到 docker0 上。

Docker 会为每个容器创建一对 veth pair 接口(虚拟网线):

  • 一端在容器内,命名为 eth0;
  • 另一端在 host 上,挂载到 docker0 网桥,命名以 veth 开头(如 vethAQI2QT)。

通过这种方式,host 与容器、容器与容器之间可通过 docker0 网桥通信。

实践:bridge 网络工作原理

步骤 1:安装 bridge-utils 工具(查看网桥)
复制代码
[root@docker ~]# cd /etc/yum.repos.d/
[root@docker yum.repos.d]# vim cloud.repo
[centos-openstack-victoria]
name=CentOS 8 - OpenStack victoria
baseurl=https://mirrors.aliyun.com/centos-vault/8-stream/cloud/x86_64/openstack-victoria/
enabled=1
gpgcheck=0

[root@docker yum.repos.d]# yum install -y bridge-utils
[root@docker ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.02420be905a5       no

当前 docker0 网桥无挂载的网络接口。

步骤 2:创建容器,观察网桥变化
复制代码
[root@docker ~]# docker run -itd --name busybox1 busybox
5225d246f751dbfc4cdf745675d9c79d3de1b91dd4174268c35e671b9f1b0c80
[root@docker ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.02420be905a5       no              vethddb2744

新的 veth 接口(vethddb2744)被挂载到 docker0 网桥,对应 busybox1 容器的虚拟网卡。

步骤 3:查看容器网络配置
复制代码
[root@docker ~]# docker exec -it busybox1 sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
24: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
  • 容器内网卡为 eth0@if25,与 host 上的 vethddb2744@if24 是一对 veth pair(24 和 25 相互对应)。
  • 容器 IP 为 172.17.0.2,来自 docker0 网桥的子网。
步骤 4:验证 bridge 网络配置
复制代码
[root@docker ~]# docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "9c8f4ff0c361c169536a912736d7a305b93abd9f3acc69fee066233260930a69",
        "Created": "2025-07-31T13:31:35.155868533+08:00",
        "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": {
            "5225d246f751dbfc4cdf745675d9c79d3de1b91dd4174268c35e671b9f1b0c80": {
                "Name": "busybox1",
                "EndpointID": "ecc590ff010723831b97c0439ef9a9ca13fda695185d846d25662caecb066b7f",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/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": {}
    }
]
  • bridge 网络默认子网为 172.17.0.0/16,网关为 172.17.0.1(即 docker0 网桥的 IP)。
  • com.docker.network.bridge.enable_icc: true:允许容器间通信。
  • com.docker.network.bridge.enable_ip_masquerade: true:启用 NAT,容器可访问外部网络。
步骤 5:验证 host 上的 veth 接口
复制代码
[root@docker ~]# ip a | grep vethddb2744
25: vethddb2744@if24: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 2e:6b:2e:f3:7a:8c brd ff:ff:ff:ff:ff:ff link-netnsid 0

vethddb2744@if24 表示 host 的 25 号网卡对应容器的 24 号网卡(eth0),验证了 veth pair 的关联关系。

除了 none, host, bridge 这三个自动创建的网络,用户也可以根据业务需要创建 user-defined 网络,下 一节我们将详细讨论。

5.3 如何自定义容器网络?

除了 none, host, bridge 这三个自动创建的网络,用户也可以根据业务需要创建 user-defined 网络。

Docker 提供三种 user-defined 网络驱动:bridge, overlay 和 macvlan。overlay 和 macvlan 用于创建 跨主机的网络,我们后面有章节单独讨论。

创建自定义 bridge 网络

示例 1:创建默认配置的自定义 bridge 网络
复制代码
[root@docker ~]# docker network create --driver bridge my_net
89f7bc11b602e84452ae01786113ac196a535f7296b9989ad941b3a48a5e6d04

查看网络结构和配置:

复制代码
[root@docker ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
br-89f7bc11b602 8000.0242194d039e       no
docker0         8000.02420be905a5       no              vethddb2744

[root@docker ~]# docker network inspect my_net
[
    {
        "Name": "my_net",
        "Id": "89f7bc11b602e84452ae01786113ac196a535f7296b9989ad941b3a48a5e6d04",
        "Created": "2025-07-31T15:57:17.487644996+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]
  • 自动创建网桥 br-89f7bc11b602(名称为网络 ID 前缀)。
  • 自动分配子网 172.18.0.0/16,网关 172.18.0.1(网桥 IP)。
示例 2:指定子网和网关创建自定义 bridge 网络
复制代码
[root@docker ~]# docker network create --driver bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_net2
ec761bc51778f67c2245af72fd969f00cd517ee617a10a70dc01904f9c10279d

验证配置:

复制代码
[root@docker ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
br-89f7bc11b602 8000.0242194d039e       no
br-ec761bc51778 8000.02427fa54b88       no
docker0         8000.02420be905a5       no              vethddb2744

[root@docker ~]# docker network inspect my_net2
[
    {
        "Name": "my_net2",
        "Id": "ec761bc51778f67c2245af72fd969f00cd517ee617a10a70dc01904f9c10279d",
        "Created": "2025-07-31T16:00:06.021244096+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.22.16.0/24",
                    "Gateway": "172.22.16.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

[root@docker ~]# ip a | grep br-ec761bc51778
27: br-ec761bc51778: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    inet 172.22.16.1/24 brd 172.22.16.255 scope global br-ec761bc51778
  • 网桥 br-ec761bc51778 的 IP 为指定的网关 172.22.16.1。

容器使用自定义网络

示例 1:容器加入自定义网络(自动分配 IP)
复制代码
[root@docker ~]# docker run -it --network=my_net2 --name busybox2 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:ac:16:10:02 brd ff:ff:ff:ff:ff:ff
    inet 172.22.16.2/24 brd 172.22.16.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # ctrl+P,ctrl+q退出容器

[root@docker ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
br-89f7bc11b602 8000.0242194d039e       no
br-ec761bc51778 8000.02427fa54b88       no              veth02dd704
docker0         8000.02420be905a5       no              vethddb2744
  • 容器 busybox2 分配到 IP 172.22.16.2,对应的 veth 接口 veth02dd704 挂载到 my_net2 网桥。
示例 2:指定静态 IP 启动容器
复制代码
[root@docker ~]# docker run -it --network=my_net2 --ip 172.22.16.8 --name busybox3 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
30: eth0@if31: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:ac:16:10:08 brd ff:ff:ff:ff:ff:ff
    inet 172.22.16.8/24 brd 172.22.16.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # ctrl+P,ctrl+q退出容器

[root@docker ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
br-89f7bc11b602 8000.0242194d039e       no
br-ec761bc51778 8000.02427fa54b88       no              veth02dd704
                                                        veth5fbb7f6
docker0         8000.02420be905a5       no              vethddb2744
  • 容器 busybox3 成功指定静态 IP 172.22.16.8,对应的 veth 接口 veth5fbb7f6 挂载到 my_net2 网桥。

注意事项

  • 只有使用 --subnet 手动指定子网的网络,才能通过 --ip 指定静态 IP。

  • 未指定子网的网络(如 my_net),指定静态 IP 会报错:

    复制代码
    [root@docker ~]# docker run -it --network=my_net --ip 172.18.0.8 busybox
    docker: Error response from daemon: invalid config for network my_net: invalid endpoint settings: user specified IP address is supported only when connecting to networks with user configured subnets.
    See 'docker run --help'.

当前 docker host 的网络拓扑:

下一节讨论这几个容器之间的连通性。

5.4 理解容器之间的连通性

基于当前网络拓扑,验证不同网络中容器的连通性:

同一网络容器连通性(my_net2)

复制代码
[root@docker ~]# docker exec -it busybox2 sh
/ # ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 02:42:AC:16:10:02  
          inet addr:172.22.16.2  Bcast:172.22.16.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:18 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:1436 (1.4 KiB)  TX bytes:0 (0.0 B)

# ping busybox3(同一网络,IP 172.22.16.8)
/ # ping -c 3 172.22.16.8
PING 172.22.16.8 (172.22.16.8): 56 data bytes
64 bytes from 172.22.16.8: seq=0 ttl=64 time=0.197 ms
64 bytes from 172.22.16.8: seq=1 ttl=64 time=0.137 ms
64 bytes from 172.22.16.8: seq=2 ttl=64 time=0.158 ms
--- 172.22.16.8 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.137/0.164/0.197 ms

# ping my_net2 网关(172.22.16.1)
/ # ping -c 3 172.22.16.1
PING 172.22.16.1 (172.22.16.1): 56 data bytes
64 bytes from 172.22.16.1: seq=0 ttl=64 time=0.149 ms
64 bytes from 172.22.16.1: seq=1 ttl=64 time=0.116 ms
64 bytes from 172.22.16.1: seq=2 ttl=64 time=0.182 ms
--- 172.22.16.1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.116/0.149/0.182 ms

结论:同一网络中的容器、容器与网关之间可正常通信。

不同网络容器连通性(docker0 vs my_net2)

复制代码
# 在 busybox2 中 ping busybox1(docker0 网络,IP 172.17.0.2)
/ # ping -c 3 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
--- 172.17.0.2 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

结论:不同网络(不同网桥)的容器默认无法通信。

原因:Docker 网络隔离(iptables 规则)

Docker 会通过 iptables 的 DOCKER-ISOLATION 链隔离不同网络,禁止不同网桥之间的流量转发:

复制代码
[root@docker ~]# iptables-save | grep DOCKER-ISOLATION
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A DOCKER-ISOLATION-STAGE-1 -i br-ec761bc51778 ! -o br-ec761bc51778 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -i br-89f7bc11b602 ! -o br-89f7bc11b602 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
-A DOCKER-ISOLATION-STAGE-2 -o br-ec761bc51778 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o br-89f7bc11b602 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
  • 规则 DOCKER-ISOLATION-STAGE-2 -o br-ec761bc51778 -j DROP 表示禁止转发到 my_net2 网桥的流量。

解决:容器加入多个网络(docker network connect)

通过 docker network connect 为容器添加额外的网络网卡,实现跨网络通信:

复制代码
[root@docker ~]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED         STATUS         PORTS     NAMES
0899976a1c59   busybox   "sh"      About an hour ago   Up About an hour             busybox3
e9da18f962d6   busybox   "sh"      About an hour ago   Up About an hour             busybox2
5225d246f751   busybox   "sh"      About an hour ago   Up About an hour             busybox1

# 为 busybox1 添加 my_net2 网络
[root@docker ~]# docker network connect my_net2 busybox1

# 查看 busybox1 网络配置
[root@docker ~]# docker exec -it busybox1 sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
24: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
32: eth1@if33: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:ac:16:10:03 brd ff:ff:ff:ff:ff:ff
    inet 172.22.16.3/24 brd 172.22.16.255 scope global eth1
       valid_lft forever preferred_lft forever

# 验证 busybox2 与 busybox1 通信
[root@docker ~]# docker exec -it busybox2 sh
/ # ping -c 3 172.22.16.3
PING 172.22.16.3 (172.22.16.3): 56 data bytes
64 bytes from 172.22.16.3: seq=0 ttl=64 time=0.199 ms
64 bytes from 172.22.16.3: seq=1 ttl=64 time=0.134 ms
64 bytes from 172.22.16.3: seq=2 ttl=64 time=0.141 ms
--- 172.22.16.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.134/0.158/0.199 ms
  • busybox1 新增 eth1 网卡,分配 my_net2 网络 IP 172.22.16.3。
  • busybox2 可通过该 IP 与 busybox1 正常通信。

更新后的网络拓扑:

下一节我们讨论容器间通信的三种方式。

5.5 容器通信的三种方式

容器之间可通过 IP,Docker DNS Server 或 joined 容器三种方式通信。

1. IP 通信

核心原理

两个容器要能通信,必须要有属于同一个网络的网卡(可通过 --network 指定网络,或 docker network connect 添加网络)。

实践示例

参考上一节,busybox1 通过 docker network connect 加入 my_net2 网络后,busybox2 可通过 my_net2 网络的 IP 172.22.16.3 与 busybox1 通信。

2. Docker DNS Server

通过 IP 通信需要提前知道容器 IP,灵活性差。Docker 内嵌 DNS Server,支持容器通过 "容器名" 通信(仅在 user-defined 网络中生效)。

实践示例
步骤 1:在 user-defined 网络中启动容器(指定名称)
复制代码
[root@docker ~]# docker run -it --network my_net2 --name bbox1 busybox
/ # ctrl+P,ctrl+q退出容器

[root@docker ~]# docker run -it --network my_net2 --name bbox2 busybox
/ # ctrl+P,ctrl+q退出容器
步骤 2:通过容器名通信
复制代码
[root@docker ~]# docker exec -it bbox2 sh
/ # ping -c 3 bbox1
PING bbox1 (172.22.16.2): 56 data bytes
64 bytes from 172.22.16.2: seq=0 ttl=64 time=0.177 ms
64 bytes from 172.22.16.2: seq=1 ttl=64 time=0.161 ms
64 bytes from 172.22.16.2: seq=2 ttl=64 time=0.187 ms
--- bbox1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.161/0.175/0.187 ms
  • Docker DNS Server 自动解析容器名到对应的 IP,无需手动配置。
限制:默认 bridge 网络不支持 DNS 解析
复制代码
# 在默认 bridge 网络启动容器
[root@docker ~]# docker run -it --name bbox3 busybox
/ # ctrl+P,ctrl+q退出容器

[root@docker ~]# docker run -it --name bbox4 busybox
/ # ctrl+P,ctrl+q退出容器

# 尝试通过容器名通信(失败)
[root@docker ~]# docker exec -it bbox4 sh
/ # ping -c 3 bbox3
ping: bad address 'bbox3'

3. joined 容器

joined 容器共享一个网络栈(网卡、IP、路由等),可通过 127.0.0.1 直接通信,适用于需高效通信的场景(如 web 服务与应用服务)。

实践示例
步骤 1:启动基础容器(web1,httpd 服务)
复制代码
[root@docker ~]# docker run -d -it --name web1 httpd
f7641c43eb7021724e869f77bd4541e909ebf0968fc35e6a43e8e674d046ef3f

# 安装 iproute2 工具(查看网络)
[root@docker ~]# docker exec -it web1 bash
root@1d5181ce7a85:/usr/local/apache2# apt-get update
root@1d5181ce7a85:/usr/local/apache2# apt-get install iproute2 -y
root@1d5181ce7a85:/usr/local/apache2# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
18: eth0@if19: <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
步骤 2:创建 joined 容器(共享 web1 网络栈)
复制代码
[root@docker ~]# docker run -it --network container:web1 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
  • joined 容器与 web1 共享网卡(MAC 地址、IP 完全一致)。
步骤 3:通过 127.0.0.1 访问 web1 服务
复制代码
/ # wget 127.0.0.1
Connecting to 127.0.0.1 (127.0.0.1:80)
saving to 'index.html'
index.html 100% |***************************| 45 0:00:00 ETA
'index.html' saved

/ # cat index.html
<html><body><h1>It works!</h1></body></html>

joined 容器适用场景

  1. 高效通信:容器间通过 loopback 通信,无网络转发开销(如 web server 与 app server)。
  2. 网络监控:监控容器共享目标容器的网络栈,可捕获网络流量(如网络监控工具容器)。

5.6 容器如何访问外部世界

前面我们已经解决了容器间通信的问题,接下来讨论容器如何与外部世界通信。这里涉及两个方向:

  1. 容器访问外部世界
  2. 外部世界访问容器

容器访问外部世界

现象:容器默认可访问外网
复制代码
# 验证 host 可访问外网
[root@docker ~]# ping -c 3 www.baidu.com
PING www.baidu.com (223.109.82.6) 56(84) bytes of data.
64 bytes from 223.109.82.6 (223.109.82.6): icmp_seq=1 ttl=128 time=67.8 ms
64 bytes from 223.109.82.6 (223.109.82.6): icmp_seq=2 ttl=128 time=13.0 ms
64 bytes from 223.109.82.6 (223.109.82.6): icmp_seq=3 ttl=128 time=12.5 ms
--- www.baidu.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 12.454/31.074/67.754/25.937 ms

# 验证容器可访问外网
[root@docker ~]# docker run -it --name test1 busybox
/ # ping -c 3 www.baidu.com
PING www.baidu.com (223.109.82.6): 56 data bytes
64 bytes from 223.109.82.6: seq=0 ttl=127 time=21.240 ms
64 bytes from 223.109.82.6: seq=1 ttl=127 time=14.987 ms
64 bytes from 223.109.82.6: seq=2 ttl=127 time=14.651 ms
--- www.baidu.com ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 14.651/16.959/21.240 ms
原理:NAT 地址转换

容器位于私有网络(如 docker0 的 172.17.0.0/16),无法直接访问外网,Docker 通过 iptables NAT 规则实现地址转换:

复制代码
[root@docker ~]# iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P POSTROUTING ACCEPT
-P OUTPUT ACCEPT
-N DOCKER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.19.0.0/16 ! -o br-6936dc39839e -j MASQUERADE
-A POSTROUTING -s 172.22.16.0/24 ! -o br-c0fc7bdf8143 -j MASQUERADE
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A DOCKER -i docker0 -j RETURN
-A DOCKER -i br-6936dc39839e -j RETURN
-A DOCKER -i br-c0fc7bdf8143 -j RETURN

关键规则:-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

  • 含义:来自 172.17.0.0/16 网段(docker0 网络)的外出流量,若不是从 docker0 网卡发出(即访问外网),则通过 MASQUERADE 进行 NAT 转换(将源 IP 替换为 host 网卡 IP)。
验证:tcpdump 抓包
复制代码
# 安装 tcpdump
[root@docker ~]# yum install -y tcpdump

# 终端1:监控 docker0 网卡的 ICMP 流量
[root@docker ~]# tcpdump -i docker0 -n icmp
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes

# 终端2:监控 ens160 网卡的 ICMP 流量
[root@docker ~]# tcpdump -i ens160 -n icmp
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens160, link-type EN10MB (Ethernet), capture size 262144 bytes

# 终端3:启动容器 ping 外网
[root@docker ~]# docker run -it busybox
/ # ping -c 3 www.baidu.com
抓包结果分析
  • 终端 1(docker0):源 IP 为容器 IP(172.17.0.2)

    复制代码
    15:23:42.854951 IP 172.17.0.2 > 223.109.82.41: ICMP echo request, id 7, seq 0, length 64
    15:23:42.870473 IP 223.109.82.41 > 172.17.0.2: ICMP echo reply, id 7, seq 0, length 64
  • 终端 2(ens160):源 IP 为 host IP(192.168.108.30)

    复制代码
    15:25:41.886472 IP 192.168.108.30 > 223.109.82.41: ICMP echo request, id 8, seq 0, length 64
    15:25:41.906738 IP 223.109.82.41 > 192.168.108.30: ICMP echo reply, id 8, seq 0, length 64
通信流程
复制代码
Container (172.17.0.2) → docker0 → iptables NAT(替换源 IP 为 192.168.108.30) → ens160 → 外网(www.baidu.com)
外网响应 → ens160 → iptables NAT(替换目标 IP 为 172.17.0.2) → docker0 → Container

下一节我们讨论另一个方向的流量:外部世界如何访问容器。

5.7 外部世界如何访问容器

上节我们学习了容器如何访问外部网络,今天讨论另一个方向:外部网络如何访问到容器?

答案是:端口映射。

docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。容器启动时 通过 -p 参数映射端口:

复制代码
[root@docker ~]# docker run -d -p 8080:80 httpd
25b04cb7f6d6c012c609251a425475fcf7da03c93feb20c1d951eb45cb1ef79b

[root@docker ~]# docker ps
CONTAINER ID   IMAGE     COMMAND              CREATED         STATUS         PORTS                  NAMES
25b04cb7f6d6   httpd     "httpd-foreground"   5 seconds ago   Up 4 seconds   0.0.0.0:8080->80/tcp   gracious_ganguly
  • -p 8080:80:将容器的 80 端口映射到 host 的 8080 端口。

  • 查看端口映射:

    复制代码
    [root@docker ~]# docker port 25b04cb7f6d6
    80/tcp -> 0.0.0.0:8080
访问容器服务
复制代码
[root@docker ~]# curl 192.168.108.30:8080
<html><body><h1>It works!</h1></body></html>
动态端口映射(不指定 host 端口)
复制代码
[root@docker ~]# docker run -d -p 80 httpd
7a8c3e30bbdb9103d9a134212d0c1fde809d312120183a413d73592ee85b210c

[root@docker ~]# docker ps
CONTAINER ID   IMAGE     COMMAND              CREATED         STATUS         PORTS                     NAMES
7a8c3e30bbdb   httpd     "httpd-foreground"   11 seconds ago  Up 10 seconds  0.0.0.0:32768->80/tcp    my_http_server
  • Docker 自动分配 host 端口(32768 起),通过 docker ps 查看映射关系。

端口映射原理:docker-proxy

每个端口映射会启动一个 docker-proxy 进程,负责转发 host 端口的流量到容器端口:

复制代码
[root@docker ~]# ps aux | grep docker-proxy
root      1234  0.0  0.0 112720  2280 ?        Sl   10:00   0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8080 -container-ip 172.17.0.3 -container-port 80
通信流程
复制代码
外部(curl 192.168.108.30:8080) → host 8080 端口 → docker-proxy → 容器 172.17.0.3:80 → httpd 服务
httpd 响应 → 容器 80 端口 → docker-proxy → host 8080 端口 → 外部

实战:安装 Tomcat 并映射端口

步骤 1:搜索并拉取 Tomcat 镜像
复制代码
[root@docker ~]# docker search tomcat
NAME                     DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
tomcat                   Apache Tomcat is an open source implementati...   3751      [OK]       
bitnami/tomcat            Bitnami container image for Tomcat             52                   [OK]
...

[root@docker ~]# docker pull tomcat
Using default tag: latest
latest: Pulling from library/tomcat
...
Status: Downloaded newer image for tomcat:latest
docker.io/library/tomcat:latest

[root@docker ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
httpd        latest    90f191b9781e   2 weeks ago    148MB
tomcat       latest    9ca267cc83c7   3 weeks ago    468MB
hello-world  latest    74cc54e27dc4   6 months ago   10.1kB
步骤 2:启动 Tomcat 容器(端口映射)
复制代码
[root@docker ~]# docker run -itd -p 8080:8080 tomcat
92d78922526ba2a54ef63c48d2fec80b10997027de93457a4e2d56d7ea7448e2
步骤 3:访问 Tomcat 首页(解决 404 问题)

访问 http://192.168.108.30:8080 出现 404,原因是 Tomcat 官方镜像的 webapps 目录为空,默认页面在 webapps.dist 目录:

复制代码
# 进入容器
[root@docker ~]# docker exec -it 92d78922526b bash
root@92d78922526b:/usr/local/tomcat# ls webapps
root@92d78922526b:/usr/local/tomcat# ls webapps.dist/
docs  examples  host-manager  manager  ROOT

# 替换 webapps 目录
root@92d78922526b:/usr/local/tomcat# rm -r webapps
root@92d78922526b:/usr/local/tomcat# mv webapps.dist webapps
步骤 4:重新访问 Tomcat 首页

刷新 http://192.168.108.30:8080,成功显示 Tomcat 首页。

本章小结

  1. Docker 原生网络:none(隔离)、host(共享 host 网络)、bridge(默认,网桥转发)。

  2. 自定义网络:支持指定子网、网关,支持容器名 DNS 解析。

  3. 容器通信:同一网络直接通信,不同网络通过 docker network connect 跨网通信;支持 IP、DNS、joined 三种通信方式。

  4. 容器与外部通信:容器访问外网通过 NAT,外部访问容器通过端口映射(-p)。
    e2d56d7ea7448e2

    步骤 3:访问 Tomcat 首页(解决 404 问题)

    访问 http://192.168.108.30:8080 出现 404,原因是 Tomcat 官方镜像的 webapps 目录为空,默认页面在 webapps.dist 目录:

    [外链图片转存中...(img-sds3efk8-1769509891356)]

进入容器

root@docker \~\]# docker exec -it 92d78922526b bash root@92d78922526b:/usr/local/tomcat# ls webapps root@92d78922526b:/usr/local/tomcat# ls webapps.dist/ docs examples host-manager manager ROOT ## 替换 webapps 目录 root@92d78922526b:/usr/local/tomcat# rm -r webapps root@92d78922526b:/usr/local/tomcat# mv webapps.dist webapps #### 步骤 4:重新访问 Tomcat 首页 刷新 `http://192.168.108.30:8080`,成功显示 Tomcat 首页。 [外链图片转存中...(img-o5EbBnlz-1769509891356)] ### 本章小结 1. Docker 原生网络:none(隔离)、host(共享 host 网络)、bridge(默认,网桥转发)。 2. 自定义网络:支持指定子网、网关,支持容器名 DNS 解析。 3. 容器通信:同一网络直接通信,不同网络通过 `docker network connect` 跨网通信;支持 IP、DNS、joined 三种通信方式。 4. 容器与外部通信:容器访问外网通过 NAT,外部访问容器通过端口映射(`-p`)。

相关推荐
有风听风有雨看雨3 小时前
【Critical】docker unauthorized 2375
docker·容器·eureka
步步为营DotNet4 小时前
深度剖析.NET中IHostedService:后台服务管理的关键组件
服务器·网络·.net
Ares-Wang4 小时前
网络》》路由引入 、路由控制 》》路由策略 route-policy 、Filter-Policy(过滤策略)
网络·智能路由器
Jia ming5 小时前
虚拟地址与物理地址:64位VS48位
网络
Trank-Lw5 小时前
Docker Devcontainer 管理命令
运维·docker·容器
北辰当尹6 小时前
【小迪安全2023】day42 php应用&mysql架构&sql注入&跨库查询&文件读写&权限操作
mysql·安全·php
的卢马飞快6 小时前
【C语言进阶】给数据一个“家”:从零开始掌握文件操作
c语言·网络·数据库
科技观察6 小时前
告别镜像拉取困境:毫秒镜像以“正规军”姿态重塑国内Docker加速生态
运维·docker·容器
Yu_Lijing6 小时前
《图解HTTP》笔记与读后感(上)
网络·笔记·网络协议·http
czy87874756 小时前
connect() 的阻塞特性取决于它所关联的 socket 是否被设置为非阻塞模式,connect() 会等待 TCP 三次握手的超时时间
服务器·网络·tcp/ip