文章目录
- [第 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 如何自定义容器网络?)
- [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 容器如何访问外部世界)
-
- 容器访问外部世界
-
- 现象:容器默认可访问外网
- [原理:NAT 地址转换](#原理:NAT 地址转换)
- [验证:tcpdump 抓包](#验证:tcpdump 抓包)
- 抓包结果分析
- 通信流程
- [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 网络适用场景
- 性能优先:直接使用 Docker host 的网络,无需网络转发,性能最高,适合对网络传输效率有高要求的场景。
- 配置 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 容器适用场景
- 高效通信:容器间通过 loopback 通信,无网络转发开销(如 web server 与 app server)。
- 网络监控:监控容器共享目标容器的网络栈,可捕获网络流量(如网络监控工具容器)。
5.6 容器如何访问外部世界
前面我们已经解决了容器间通信的问题,接下来讨论容器如何与外部世界通信。这里涉及两个方向:
- 容器访问外部世界
- 外部世界访问容器
容器访问外部世界
现象:容器默认可访问外网
# 验证 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 首页。

本章小结
-
Docker 原生网络:none(隔离)、host(共享 host 网络)、bridge(默认,网桥转发)。
-
自定义网络:支持指定子网、网关,支持容器名 DNS 解析。
-
容器通信:同一网络直接通信,不同网络通过
docker network connect跨网通信;支持 IP、DNS、joined 三种通信方式。 -
容器与外部通信:容器访问外网通过 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`)。