首页  /  云原生  /  正文

Docker 网络与容器互联

云原生 2026-10-02📖 26 分钟👁 —
🐳 云原生 · Docker第 9 / 9 页123456789📖 完整导航 →

一、docker0 是 Docker 网络的核心

先把环境弄干净,删掉所有容器和镜像再开始,开一台新机器也行:

docker rm -f `docker ps -aq`
docker rmi -f `docker images -aq`
# 这一步不知道为什么把docker的配置也删了,pull加速都拉不了了
# 只能重新配置一下了(更详细的在前面的笔记)
sudo tee /etc/docker/daemon.json <<EOF
{
"registry-mirrors": [
"https://hub-mirror.c.163.com",
"https://mirror.ccs.tencentyun.com",
"https://docker.m.daocloud.io"
]
}
EOF

清干净之后看网卡,一共三块:

ubuntu@VM-0-9-ubuntu:~$ ip addr
# 本机回环地址
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 noprefixroute 
       valid_lft forever preferred_lft forever
# 腾讯云内网地址
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 52:54:00:af:79:fb brd ff:ff:ff:ff:ff:ff
    altname enp2s1
    altname ens1
    inet 172.16.0.9/20 metric 100 brd 172.16.15.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:feaf:79fb/64 scope link 
       valid_lft forever preferred_lft forever
# docker的网卡
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 62:b1:8b:b3:a1:39 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::60b1:8bff:feb3:a139/64 scope link 
       valid_lft forever preferred_lft forever

lo 是本机回环,eth0 是云服务器的内网网卡,docker0 才是 Docker 自己的网卡 —— 地址是 172.17.0.1/16,它就是这个内网的网关。

二、容器 IP、veth-pair 和 docker0 转发

随便起一个容器:

docker run -d -P --name tomcat01 tomcat
# 查看容器内部网络地址,直接追加 ip addr 即可
# 之前写镜像就提到可以追加命令

三种查法:

# 方法1:查看 hosts 文件
docker exec tomcat01 cat /etc/hosts

# 方法2:来看内网 IP(缺少命令,看下面的方式安装)
docker exec -it tomcat01 ip addr

root@d84972560009:/usr/local/tomcat# ip addr
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
    # 容器启动时会得到一个 eth0@if140 地址IP,这是 docker 分配的
2: eth0@if140: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 92:d3:f5:3f:b3:d0 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

# 方法3:用 inspect 从外面看(最准),在network块
docker inspect tomcat01 | grep IPAddress

# 直接 inspect 看
        "NetworkSettings": {
            "SandboxID": "b0abb8543d11e213fb421d73f85134395b40d1f88c148002525d6a466739ce05",
            "SandboxKey": "/var/run/docker/netns/b0abb8543d11",
            "Ports": {
                "8080/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "32768"
                    },
                    {
                        "HostIp": "::",
                        "HostPort": "32768"
                    }
                ]
            },
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "DriverOpts": null,
                    "GwPriority": 0,
                    "NetworkID": "d18bbcfbe97438fc79c2ae24c7e92f0d72082b0a7d7e4c6b747e21a1d1d353c8",
                    "EndpointID": "a8570c4216ea15d1b53ca3b40617f6fb485b9d9cbe1b71ebbad3197efa58aebb",
                    "Gateway": "172.17.0.1",

                    # 容器内部地址
                    "IPAddress": "172.17.0.2",
                    "MacAddress": "92:d3:f5:3f:b3:d0",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "DNSNames": null
                }
            }
        },

注意 -P 是大写,表示随机映射端口,inspect 出来的 HostPort: 32768 就是随机分到的那个。

tomcat 这种精简镜像里默认没有 ip addr,得进去装。按发行版选对应的包管理器:

# 基于 Debian(apt 包管理器)的容器
docker exec -it tomcat01 bash
apt update
apt install iproute2 -y
ip addr

# 基于 CentOS/RHEL(yum 包管理器)的容器
docker exec -it tomcat01 bash
yum install -y iproute
ip addr

# 基于 Alpine(apk 包管理器)的容器
docker exec -it tomcat01 sh
apk add iproute2
ip addr

想 ping 也一样,apt install iputils-ping -y 装完才有:

# 发现 ping 命令没有,安装
apt install iputils-ping -y

# 发现能 ping 通
root@d84972560009:/usr/local/tomcat# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.021 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.034 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.032 ms

宿主机和容器是通的。那么容器之间能不能通?

每安装一个 docker 容器,docker 就会给容器分配 IP,而 docker 产生的网卡是桥接模式,使用 veth-pair 技术。

再看一次宿主机的网卡,容器的对端也冒出来了:

root@VM-0-9-ubuntu:~# ip addr
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 noprefixroute 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 52:54:00:af:79:fb brd ff:ff:ff:ff:ff:ff
    altname enp2s1
    altname ens1
    inet 172.16.0.9/20 metric 100 brd 172.16.15.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:feaf:79fb/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 62:b1:8b:b3:a1:39 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::60b1:8bff:feb3:a139/64 scope link 
       valid_lft forever preferred_lft forever
140: veth4543c38@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether ce:c1:e7:39:b2:2f brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::ccc1:e7ff:fe39:b22f/64 scope link 
       valid_lft forever preferred_lft forever

再起一个容器,宿主机上就又多一块:

ip addr
# 又多了一个
140: veth4543c38@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether ce:c1:e7:39:b2:2f brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::ccc1:e7ff:fe39:b22f/64 scope link 
       valid_lft forever preferred_lft forever
141: vethcc477c1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 6e:7b:eb:15:4a:97 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::6c7b:ebff:fe15:4a97/64 scope link 
       valid_lft forever preferred_lft forever

# 看里面
apt update && apt install -y iproute2

root@1fa8ebedc8fb:/usr/local/tomcat# ip addr
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
       # 这个数字对应容器外面的 141 那块网卡
2: eth0@if141: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 52:bb:99:c3:57:4f brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

# 测试两个tomcat互通
apt install iputils-ping -y
# 用02 ping 01
root@1fa8ebedc8fb:/usr/local/tomcat# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.062 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.044 ms

通。docker 给容器分配的 IP 都是同网段的,所以容器之间默认互联不用额外配置。

这里的关键是 veth-pair:

两个网络命名空间之间由一对虚拟接口连起来,这就是 veth-pair

OpenStack、Docker 容器之间连接、OVS 之间,用的都是 veth-pair 技术。

想找到配对的另一头也简单:进容器看 eth0@if141 里 if 后面的数字,对应宿主机上的那块 141: vethcc477c1@if2。

docker0 就像网关、路由器,容器之间不是直连,是 docker0 转发连接的。docker0 是 docker 的网卡,docker 的内网和 Linux 服务器对应的公网是一回事。

Tomcat01 的 eth0@if262 对应宿主机上 262 号 veth,Tomcat02 同理,中间由 docker0(172.17.0.1)当网关转发

分配的 IP 直接看 CIDR 表示法就知道上限了:

172.17.0.3/16

172.17.0.0---172.17.255.255

可分配IP:2^16-2(广播地址和回环地址)

一个网段能塞 6 万多个容器,不用深究,知道大概就行。

往外走的时候靠 NAT:

# NAT 核心作用就是:让多个设备共享一个公网 IP 上网
# 实现:把内网 IP(私有地址)替换成外网 IP(公网地址)
容器A(172.17.0.2)
        │
        ▼
    ┌───────┐
    │docker0│  ← 网桥(只负责内部连接)
    └───┬───┘
        │
        ▼
   Linux内核路由 + NAT  ← 这里做地址转换
        │
        ▼
   物理网卡(eth0)  ← 带着公网 IP 发出数据

删除容器的时候,一对 veth-pair 会一起消失:

# 删除容器一对veth-pair都会消失
# 查看删除前 tomcat02 的信息
2: eth0@if141: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 52:bb:99:c3:57:4f brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
141: vethcc477c1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 6e:7b:eb:15:4a:97 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::6c7b:ebff:fe15:4a97/64 scope link 
       valid_lft forever preferred_lft forever

docker rm -f tomcat02
# 此时少了 02 的一对网卡,只剩 01 的 140 那块

一个小结:

容器 A、B、C 各自一对 Veth 挂在 Docker0 网桥上,再经 NAT 从物理网卡出去

顺带一个实际的问题:写微服务的时候数据库地址写成 数据库url=ip:端口,IP 一变地址就失效了(docker 每次重启会重新分配 IP)。Spring Cloud 里用服务名(feign 那种写法)就能绕开这个坑,只看服务名不看 IP,数据库 IP 换了也不影响访问。这就是为什么后面要用自定义网络。

三、四种网络模式

docker0 和 veth-pair 是底层原理,实际用的时候是在启动容器时选一种网络模式。它要解决的就是这几件事:容器之间互联通信、宿主机外面能访问进来(端口映射),以及容器 IP 变动的时候还能用服务名找到对方:

容器网络的作用:容器间的互联和通信、端口映射,IP 变动时用服务名通信不受影响

1. 三大主流模式

先认模式,日常用得最多的就是 bridge:

三大网络模式:bridge 默认走 docker0 虚拟网桥、host 用宿主机 IP 和端口、none 不配置网络

指定方式就是在 docker run 的时候加 --network:

三种模式的指定方式:--network bridge、--network host、--network none

bridge 是默认模式,前面那张 docker0 网桥就是它。名字不用刻意记,docker network inspect bridge 里的 com.docker.network.bridge.name 就写着 docker0,宿主机上 ifconfig 也能查到这块网卡:

docker network inspect bridge 里查到网桥名叫 docker0,宿主机 ifconfig 里也是 docker0

容器进出的流量都从这张网桥过:容器里是一块 eth0,宿主机上是一对 veth,最后接到宿主机的物理网卡:

docker0 网桥:容器里是 eth0,宿主机上是一对 veth,再往后才是宿主机的物理网卡

原理不复杂:容器网卡和宿主机上的 veth 成对出现、一对一匹配,同一个网桥下面的容器互相可达:

docker0 的原理:容器网卡和宿主机 veth 成对出现、一对一匹配,同一个网桥下互相可达

想验证的话,起两个 tomcat 看网卡最直观:

# 启动两个web服务,这个镜像是安装了ip命令的
docker run -d -p 8082:8080 --name tomcat82 tomcat:with-ip
docker run -d -p 8081:8080 --name tomcat81 tomcat:with-ip

# 宿主机上 ip addr 能看到两块 veth
117: veth01fed78@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
    link/ether 22:7f:88:72:5c:15 brd ff:ff:ff:ff:ff:ff link-netnsid 0
118: vethc9b94bd@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
    link/ether e6:ec:df:1e:b0:82 brd ff:ff:ff:ff:ff:ff link-netnsid 1

宿主机上的 veth 和容器里的 eth0 是一对一对上的:

同一对 veth 和 eth0:两个容器里的网卡编号和宿主机的 veth 编号一一对应

容器里一般没有 ip addr 这类命令,装完再打包成镜像就顺手了:

docker commit -a "zzz" -m "添加 iproute2" tomcat81 tomcat:with-ip

默认的 bridge 网络里,容器之间直接用 IP 是通的,问题是 IP 是动态的:

默认网络下,tomcat82 里直接 ping tomcat81 的 IP 172.17.0.3 是通的

通是通,但下次重建容器 IP 就变了,所以还是得靠名字 —— 这就是后面要用自定义网络的原因。

host 模式不虚拟自己的网卡,直接用宿主机的 IP 和端口,和宿主机共用同一个 Network Namespace:

host 模式的容器和宿主机共用 Network Namespace,不走 docker0 转发
docker run -d -p 8083:8080 --network host --name tomcat83 tomcat:with-ip

# 启动时有一个警告:启动发布的端口是不被推荐的
# 因为端口号会以主机端口为主,指定了端口映射语法没问题但是不生效
# 所以一般不写 -p
WARNING: Published ports are discarded when using host network mode

那句 WARNING: Published ports are discarded when using host network mode 就是答案:端口以宿主机为准,-p 写了也不生效,所以干脆不写,浏览器直接访问 IP:8080。进容器看 ip addr,和宿主机是一模一样的(Gateway、IPAddress 都是空的)。

none 模式的容器只有一块 lo,docker 不给它做任何网络配置,得自己配,日常基本用不上:

none 模式的启动方式:docker run -d -p 8084:8080 --network none --name tomcat84 billygoo/tomcat8-jdk8

2. container 模式(了解)

除了三大模式,还有一种是借用别人的网络:新容器不建自己的网卡、不配自己的 IP,直接和指定容器共享 IP 和端口范围:

四种网络模式对比,最下面一行就是 container 模式:和指定容器共享 IP 和端口范围
container 模式的拓扑:两个容器共用同一个 eth0,只占一份网络资源
docker run -d -p 8085:8080 --name tomcat85 billygoo/tomcat8-jdk8
docker run -d -p 8086:8080 --network container:tomcat85 --name tomcat86 billygoo/tomcat8-jdk8
用 --network container:tomcat85 让 tomcat86 共享 tomcat85 的网络

这条命令会直接失败:

报错 conflicting options: port publishing and the container type network mode

原因就是端口冲突:既然网络是跟别人共用的,那端口也是同一套,再写 -p 就等于和别人抢端口,docker 直接拒绝。所以容器的两个 8080 是没法共存的,tomcat 不适合这么玩。

换个干净的镜像试就清楚了:

docker run -it --name alpine1 alpine /bin/sh
docker run -it --network container:alpine1 --name alpine2 alpine /bin/sh
alpine1 和 alpine2 的启动方式,第二个用 --network container:alpine1

两个容器里的 eth0@if16、IP、MAC 完全一样:

alpine1 和 alpine2 里的 eth0@if16 和 IP 完全相同,蓝圈标出的就是同一个网卡

那故障也就好理解了:共享源一停,借用方就只剩一块 lo 网卡,网络全没了。


这是比较老的技术了,但可以学一下,知道它为什么被淘汰。

# 直接按名字 ping 是不通的
# (原笔记这里把容器名写成了 tomcat 02,中间多一个空格,报的是 No such container,跟网络无关)
docker exec -it tomcat03 ping tomcat02
# 让两个网络连接就可以了
# 启动三个tomcat
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND             CREATED              STATUS              PORTS                                           NAMES
b9594c12916a   tomcat    "catalina.sh run"   About a minute ago   Up About a minute   0.0.0.0:32770->8080/tcp, [::]:32770->8080/tcp   tomcat02
d84972560009   tomcat    "catalina.sh run"   2 days ago           Up 2 days           0.0.0.0:32768->8080/tcp, [::]:32768->8080/tcp   tomcat01
# 想让03 ping 通02
# 使用 --link 启动
root@VM-0-9-ubuntu:~# docker run -P -d --name tomcat03 --link tomcat02 tomcat
WARNING: Links on the default bridge network are deprecated and will be removed in a future release. Use a custom network instead.
f4d53196405c4f745e9c4b04737c4091256837550ad67cd41ddeca2d63d54eae

# 此时再 ping(ping 命令在 Tomcat 容器里默认没有安装进去安装就行)
# docker exec 是在一个已经运行的容器里执行一条命令
# docker exec [选项] 容器名 命令 [参数...]
root@VM-0-9-ubuntu:~# docker exec -it tomcat03 ping tomcat02
OCI runtime exec failed: exec failed: unable to start container process: exec: "ping": executable file not found in $PATH
# 安装 ping
apt update && apt install iputils-ping -y

# 再测试
root@VM-0-9-ubuntu:~# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.062 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.040 ms

docker 自己也给了警告:--link 已经过时了,以后会被移除,建议用自定义网络代替。

--link 不方便的地方在于它是单向的:上面 03 到 02 可以通,02 到 03 就不行,因为 02 启动时没配 link。看配置文件就明白了:

# 也可以看容器的本地绑定文件
root@VM-0-9-ubuntu:~# docker exec -it tomcat03 cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::	ip6-localnet
ff00::	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
# 直接写死了请求02的地址,ping容器名和ID都可以
172.17.0.3	tomcat02 b9594c12916a
# 下面是03的IP
172.17.0.4	f4d53196405c

# 02是没有的
root@VM-0-9-ubuntu:~# docker exec -it tomcat02 cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::	ip6-localnet
ff00::	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.3	b9594c12916a

--link 本质上就是往对方的 /etc/hosts 里写死一条 IP 记录。既然是写死的,重启 docker 让 IP 变了就全乱套了,所以现在不用它了。(想看 link 配置就 docker inspect tomcat03 | grep Link,02 那边是 null。)

五、自定义网络

先看现在有哪些网络:

# 看所有网卡
# 平时都看桥接网卡的东西,不看别的
root@VM-0-9-ubuntu:~# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
d18bbcfbe974   bridge    bridge    local
5f8c65dbe6a5   host      host      local
0ace46ca89cf   none      null      local

四种网络模式前面已经一种一种过了一遍,这里直接看自定义网络。

看具体某个网络,docker network inspect 后面跟的是网络名或网络 ID,不是容器 ID:

docker network inspect d18bbcfbe974
root@VM-0-9-ubuntu:~# docker network inspect d18bbcfbe974
[
    {
        "Name": "bridge",
        "Id": "d18bbcfbe97438fc79c2ae24c7e92f0d72082b0a7d7e4c6b747e21a1d1d353c8",
        "Created": "2026-08-02T16:40:39.066550227+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv4": true,
        "EnableIPv6": false,
        "IPAM": {
        # 这里是docker0
            "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,
        "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": {},
        # 容器部分
        "Containers": {
            "b9594c12916a7613ad93945068d8cf066af6e36a84021ebde7d265dc3e5031ec": {
                "Name": "tomcat02",
                "EndpointID": "7e9e2f499ea32e6366d20013e6b5566b14dc938d436c5fbf031fa43c1025172e",
                "MacAddress": "4e:9e:25:32:29:7c",
                # 下面是 docker 随机分配的IP
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "d8497256000951bb607f74a7e8da25ddf2a2634b66cbd3370e7eb7b9031644d5": {
                "Name": "tomcat01",
                "EndpointID": "a8570c4216ea15d1b53ca3b40617f6fb485b9d9cbe1b71ebbad3197efa58aebb",
                "MacAddress": "92:d3:f5:3f:b3:d0",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            },
            "f4d53196405c4f745e9c4b04737c4091256837550ad67cd41ddeca2d63d54eae": {
                "Name": "tomcat03",
                "EndpointID": "58030c137ffd0ccf1024cbe22db4bc6d1ffc6f7a40fe19061f6e318d24c8073b",
                "MacAddress": "02:f7:af:47:b7:3b",
                "IPv4Address": "172.17.0.4/16",
                "IPv6Address": ""
            }
        },
        "Status": {
            "IPAM": {
                "Subnets": {
                    "172.17.0.0/16": {
                        "IPsInUse": 6,
                        "DynamicIPsAvailable": 65530
                    }
                }
            }
        }
    }
]

创建自定义网络先看参数,不知道就 --help:

root@VM-0-9-ubuntu:~# docker network create --help
Usage:  docker network create [OPTIONS] NETWORK

Create a network

Options:
      --attachable            Enable manual container attachment
      --aux-address map       Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])
      --config-from string    The network from which to copy the configuration
      --config-only           Create a configuration only network

      # 可以看到默认是桥接模式
  -d, --driver string         Driver to manage the Network (default "bridge")
      --gateway ipSlice       IPv4 or IPv6 Gateway for the master subnet (default [])
      --ingress               Create swarm routing-mesh network
      --internal              Restrict external access to the network
      --ip-range ipNetSlice   Allocate container ip from a sub-range (default [])
      --ipam-driver string    IP Address Management Driver (default "default")
      --ipam-opt map          Set IPAM driver specific options (default map[])
      --ipv4                  Enable or disable IPv4 address assignment (default true)
      --ipv6                  Enable or disable IPv6 address assignment
      --label list            Set metadata on a network
  -o, --opt map               Set driver specific options (default map[])
      --scope string          Control the network's scope
      
      # 子网配置
      --subnet strings        Subnet in CIDR format that represents a network segment
      

这里有个容易混的地方:--net bridge 是启动容器时指定的模式,--driver bridge 是创建网络时用的参数。

下面自己建一个,参数依次是驱动模式、子网(CIDR 表示法)、网关:

docker network create \
  --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 \
  mynet			# 子网的名字

# 查看网络
root@VM-0-9-ubuntu:~# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
d18bbcfbe974   bridge    bridge    local
5f8c65dbe6a5   host      host      local
36ca4c8c9a09   mynet     bridge    local
0ace46ca89cf   none      null      local

# docker inspect [网络名]
root@VM-0-9-ubuntu:~# docker inspect mynet
[
    {
        "Name": "mynet",
        "Id": "36ca4c8c9a09eeb1e2d4b641a9b51ae9c69e9a057d4326f3b37875f1b099a296",
        "Created": "2026-08-05T09:28:43.060157867+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv4": true,
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.0.0/16",
                    "Gateway": "192.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Options": {},
        "Labels": {},
        "Containers": {},
        "Status": {
            "IPAM": {
                "Subnets": {
                    "192.168.0.0/16": {
                        "IPsInUse": 3,
                        "DynamicIPsAvailable": 65533
                    }
                }
            }
        }
    }
]

到这儿自己的网络就建好了。往里跑两个容器:

docker run -d -P --name tomcat01 --net mynet tomcat
docker run -d -P --name tomcat02 --net mynet tomcat

# 查看inspect的容器部分
# 用到了自己创建的子网IP,2和3,1是网关
        "Containers": {
            "3c0f5e37a82f11fea4ade117d465627574c6e2bfb816bc7d2d28ba91eaa63100": {
                "Name": "tomcat01",
                "EndpointID": "f26efa1880b0de9f1609f1b114f1dae6da7a018355f927c2a303d9b82576005f",
                "MacAddress": "82:96:fd:c3:ba:66",
                "IPv4Address": "192.168.0.2/16",
                "IPv6Address": ""
            },
            "6f0a0cee653c0eb9d0a7669af37fb7245cdc7863510729701937f4a1f8a89dbe": {
                "Name": "tomcat02",
                "EndpointID": "35ba3cda62ffbe3b7eba0d306d182e74099b780d59eb7b83798b3d3a9dd16b00",
                "MacAddress": "d2:eb:e7:cd:10:8c",
                "IPv4Address": "192.168.0.3/16",
                "IPv6Address": ""
            }
        },

测互通 —— 不用 --link 就能通,这是自定义网络最香的地方:

apt update && apt install -y iputils-ping

root@VM-0-9-ubuntu:~# docker exec -it tomcat01 bash
root@3c0f5e37a82f:/usr/local/tomcat# ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.074 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.062 ms

(原笔记这一段两次 ping 的输出对不上,一次 ping .3 却回了 .2 的结果,属于贴错;以能 ping 通 .3 的这次为准。)

自定义的网络 docker 已经帮我们维护好了对应的关系,平时大都使用自定义网络而不是 docker0。 不同的集群用不同的网络,可以保证集群彼此隔离、互不打扰,比如 redis 一套、mysql 一套;当然需要的时候也能打通。

redis 和 mysql 各占一个网段,两边互不相通,需要的时候再打通

换到自定义网络之后,容器里装个 ping 就能直接用服务名互 ping:

在 tomcat81 里装上 iputils-ping,ping 主机名 tomcat81 能解析成 172.19.0.2 并正常返回

原因就是自定义网络本身就维护好了主机名和 IP 的对应关系,容器 IP 变了也不影响调用:

Docker 架构图,红框圈的是 networkdriver,bridge、IP、port 都在这一层管

六、跨网段连通

现在手上有两个网络了:docker0(172.17 网段)和 mynet(192.168 网段),各跑着容器。同一网段内畅通没问题,不同网段按计网的规矩就是 ping 不通:

左边是在 Docker0 网段的容器,右边是在 mynet 网段的容器,中间是不通的

先在 docker0 网段里起两个:

# 在docker0网段创建
root@VM-0-9-ubuntu:~# docker run -d -P --name tomcat01-d0 tomcat
9227203fcc9c1e05874024db920e7ca3f4734ad80634b95d0ae907b2ee136e60
root@VM-0-9-ubuntu:~# docker run -d -P --name tomcat02-d0 tomcat
3dd00dc3dc9dc0e3f0593446214efc00d4d11ad28c3e5a436cb31e4caccbf1e6
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND             CREATED          STATUS          PORTS                                           NAMES
3dd00dc3dc9d   tomcat    "catalina.sh run"   2 seconds ago    Up 1 second     0.0.0.0:32777->8080/tcp, [::]:32777->8080/tcp   tomcat02-d0
9227203fcc9c   tomcat    "catalina.sh run"   5 seconds ago    Up 5 seconds    0.0.0.0:32776->8080/tcp, [::]:32776->8080/tcp   tomcat01-d0
6f0a0cee653c   tomcat    "catalina.sh run"   28 minutes ago   Up 28 minutes   0.0.0.0:32775->8080/tcp, [::]:32775->8080/tcp   tomcat02
3c0f5e37a82f   tomcat    "catalina.sh run"   29 minutes ago   Up 29 minutes   0.0.0.0:32774->8080/tcp, [::]:32774->8080/tcp   tomcat01

# 这里卡住了,并不通
root@VM-0-9-ubuntu:~# docker exec -it tomcat01-d0 ping 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.

# 看网段(docker0的默认网段名字叫bridge)
docker inspect bridge
# 容器部分里 172 网段和 192 网段不同,所以 ping 失败

打通的办法是用 docker network connect,相当于给这个容器在另一个网段里再加一个 IP:

# docker network connect [网络名] [容器名]
# 如同给网段新增容器
docker network connect mynet tomcat01-d0

连上之后再看 mynet 的容器列表,tomcat01-d0 就多了一个 192.168.0.4 的地址:

        "Containers": {
            # 直接加入了容器,实现一个容器两个地址,也算是暴力打通了
            "9227203fcc9c1e05874024db920e7ca3f4734ad80634b95d0ae907b2ee136e60": {
                "Name": "tomcat01-d0",
                "EndpointID": "4dc6ac32e8d5b1c2a48c4ccd8aabfff17da0789758118a4d5836615544e27ddc",
                "MacAddress": "e2:5f:63:b5:c0:8d",
                "IPv4Address": "192.168.0.4/16",
                "IPv6Address": ""
            }
        },

再测就通了,而且因为是自定义网络,直接用容器名就能 ping 到:

# 测试连通性
root@VM-0-9-ubuntu:~# docker exec -it tomcat01-d0 ping tomcat02
PING tomcat02 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat02.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.062 ms
64 bytes from tomcat02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.057 ms
^C
--- tomcat02 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1023ms
rtt min/avg/max/mdev = 0.057/0.059/0.062/0.002 ms

跨网络操作就用 docker network connect。

tomcat-01 同时接在 Docker0 和 mynet 上,一个容器两个地址,两个网段就打通了

docker network 这套子命令不长,常用的就这几个:

docker network --help
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

它和系统自带的 netstat 容易混,放一张表对比:

对比netstatdocker network
来源Linux 系统自带Docker 提供
作用对象宿主机网卡、端口、连接Docker 网桥、容器网络
能看到什么哪个端口被占用、TCP 连接有哪些 Docker 网络、哪个容器连到哪个网桥
示例输出netstat -tlnp 看 80 端口被谁占docker network ls 看网络列表

七、关于跨主机通信

上面聊的都是单机上的网络:docker0、自定义网桥、docker network connect,全在同一台宿主机里。跨主机要另外的驱动,比如 overlay(Swarm/K8s 那套)或者 macvlan,我这边的笔记还没做到,先留个位置。

最后两条收尾的: