从零拆解 Docker 网络:bridge/host/none、自定义网络与 DNS 通信实战

第5章 网络

摘要:本章全面介绍了Docker网络的核心概念与实践。首先讲解了Docker的三种原生网络(none、host、bridge)及其适用场景:none网络提供完全隔离,host网络实现高性能共享,bridge网络是默认的容器通信方式。接着详细演示了如何创建自定义bridge网络、指定子网和静态IP。重点探讨了容器间通信的三种方式:IP通信、Docker DNS Server(仅限用户自定义网络)和joined容器(共享网络栈)。最后分析了容器与外部世界的双向通信机制:容器通过NAT访问外网,外部通过端口映射访问容器服务。通过大量实操命令和网络拓扑图,帮助读者深入理解Docker网络的工作原理。

续上篇博客 Docker run/exec/attach 命令区别 + 容器资源限制实战

031 none和host网络的适用场景

本章开始讨论 Docker 网络。

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

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

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

bash 复制代码
[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 网络。

bash 复制代码
[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 网络中避免密码被窃取。

当然大部分容器是需要网络的,我们接着看 host 网络。

host 网络

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

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

bash 复制代码
[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
/ #
/ # hostname                         #容器主机与宿主机相同      
docker
/ #

在容器中可以看到 host 的所有网卡,并且连 hostname 也是 host 的。host 网络的使用场景又是什么呢?

直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host 上已经使用的 端口就不能再用了。

Docker host 的另一个用途是让容器可以直接配置 host 网路。比如某些跨 host 的网络解决方案,其本 身也是以容器方式运行的,这些方案需要对网络进行配置,比如管理 iptables。

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

032 学容器必须懂brige网络

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

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

Docker 就创建了在主机和所有容器之间一个虚拟共享网络 当创建一个 Docker 容器的时候,同时会创建 了一对 veth pair 接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包), 这对接 口

  • 一端在容器内即 eth0;
  • 另一端在本地并被挂载到 docker0 网桥,名称以 veth 开头(例如 vethAQI2QT)

通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。

下面演示:

先配置yum源用于安装软件

bash 复制代码
[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 上没有任何其他网络设备,我们创建一个容器看看有什么变化。

bash 复制代码
[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

一个新的网络接口 vethddb2744被挂到了docker0上, vethddb2744就是新创建容器的虚拟网卡。

下面看一下容器的网络配置

bash 复制代码
[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,对面是25号网卡
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 。大家可能会问了,为什么不是 vethddb2744 呢?

实际上 eth0@if25 vethddb2744 是一对 veth pair。veth pair 是一种成对出现的特殊网络设备,可以把它们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头( eth0@if25 )在容器中,另一头(vethddb2744 )挂在网桥 docker0 上,其效果就是将 eth0@if25 也挂在了 docker0 上。

在宿主机上查看IP地址,可以证明Docker0上的 vethddb2744 和容器中的 eth0@if25 是一对

在宿主机看到有块网卡: 25: vethddb2744@if24

宿主机的25号网卡:25: vethddb2744@if24 含义就是宿主机25号网卡对面连接了一块24号网卡,25 号网卡的名字叫vethddb2744

证明了容器busybox1里的eth0连接到了docker0网桥的vethddb2744

我们还看到eth0@if25 已经配置了 IP 172.17.0.2,为什么是这个网段呢?让我们通过 docker network inspect bridge 看一下 bridge 网络的配置信息:

bash 复制代码
[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",             `#这是bridge网络分配的网段`
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "5225d246f751dbfc4cdf745675d9c79d3de1b91dd4174268c35e671b9f1b0c80": {
                "Name": "busybox1",                 `#容器busybox1`
                "EndpointID": 
"ecc590ff010723831b97c0439ef9a9ca13fda695185d846d25662caecb066b7f",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",         `分配给busybox1的172.17.0.2`
                "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 网络配置的 subnet 就是 172.17.0.0/16,并且网关是 172.17.0.1。这个网关在哪儿呢?大概 你已经猜出来了,就是 docker0。

bash 复制代码
[root@docker ~]# ip a | grep docker0
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
group default
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0

当前容器网络拓扑结构如图所示:

容器创建时,docker 会自动从 172.17.0.0/16 中分配一个 IP,这里 16 位的掩码保证有足够多的 IP 可以 供容器使用。

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

033 如何自定义容器网络?

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

Docker 提供三种 user-defined 网络驱动:bridge, overlay 和 macvlan。overlay 和 macvlan 用于创建跨主机的网络。

我们可通过 bridge 驱动创建类似前面默认的 bridge 网络,例如:

bash 复制代码
[root@docker ~]# docker network create --driver bridge my_net
89f7bc11b602e84452ae01786113ac196a535f7296b9989ad941b3a48a5e6d04

查看一下当前 host 的网络结构变化:

bash 复制代码
[root@docker ~]# brctl show
bridge name              bridge id               STP enabled     interfaces
br-89f7bc11b602         8000.0242194d039e         no          #my_net,看bridge  name和刚才创建的my_net id一样
docker0                 8000.02420be905a5         no             vethddb2744     

新增了一个网桥 br-89f7bc11b602,这里 br-89f7bc11b602 正好是新建 bridge 网络 my_net 的短 id。执行 docker network inspect 查看一下 my_net 的配置信息:

bash 复制代码
[root@docker ~]# docker network ls
[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",      `#这里 172.18.0.0/16 是 Docker 自动分配的 IP 网段。``
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {}
        "Labels": {}
    }
]        

这里 172.18.0.0/16 是 Docker 自动分配的 IP 网段。

我们可以自己指定 IP 网段吗?

答案是:可以。

只需在创建网段时指定 --subnet--gateway参数:

bash 复制代码
[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                     #my_net
br-ec761bc51778         8000.02427fa54b88       no                     #my_net2
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": {}
    }
]

这里我们创建了新的 bridge 网络my_net2,网段为 172.22.16.0/24,网关为 172.22.16.1。与前面一样,网关在my_net2对应的网桥br-ec761bc51778上:

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

#同时宿主机上出现了与网桥my_net,my_net2同名的网卡
#查看my_net2网卡
[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

容器要使用新的网络,需要在启动时通过 --network 指定:

bash 复制代码
[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              #my_net
br-ec761bc51778          8000.02427fa54b88       no     veth02dd704         #my_net2上新增的接口veth02dd704连接容器busybox2
docker0         8000.02420be905a5       no              vethddb2744

容器分配到的 IP 为 172.22.16.2。

到目前为止,容器的 IP 都是 docker 自动从 subnet 中分配,我们能否指定一个静态 IP 呢?

答案是:可以,通过--ip 指定。

bash 复制代码
[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
28: eth0@if29: <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    #确实是指定的IP
       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              #my_net
br-ec761bc51778          8000.02427fa54b88       no     veth02dd704         #my_net2
                                                        veth5fbb7f6
#veth5fbb7f6是连接busybox3
docker0         8000.02420be905a5       no              vethddb2744

注:只有使用--subent创建的网络才能指定静态 IP。

my_net创建时没有指定--subent,如果指定静态 IP 报错如下:

bash 复制代码
[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 的网络拓扑结构。

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

034 理解容器之间的连通性

通过前面小节的实践,当前 docker host 的网络拓扑结构如下图所示,今天我们将讨论这几个容器之间 的连通性。

busybox2、busybox3 容器都挂在 my_net2 上,应该能够互通,我们验证一下:

bash 复制代码
# 登陆busybox2 ping busybox3
[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 -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网关地址
/ # 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

可见同一网络中的容器、网关之间都是可以通信的。

my_net2 与默认 bridge 网络(docker0)能通信吗?

从拓扑图可知,两个网络属于不同的网桥,应该不能通信,我们通过实验验证一下,让 busybox2 容器 ping buxybox1 容器:

bash 复制代码
/ # 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
/ #

确实 ping 不通,符合预期。

"等等!不同的网络如果加上路由应该就可以通信了吧?"

这是一个非常非常好的想法。 确实,如果 host 上对每个网络的都有一条路由,同时操作系统上打开了 ip forwarding,host 就成了一 个路由器,挂接在不同网桥上的网络就能够相互通信。下面我们来看看 docker host 满不满足这些条件 呢?

ip r 查看 host 上的路由表:

bash 复制代码
[root@docker ~]# ip r
default via 192.168.108.2 dev ens160 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.18.0.0/16 dev br-a6eff42dc3c6 proto kernel scope link src 172.18.0.1 linkdown
172.22.16.0/24 dev br-c0fc7bdf8143 proto kernel scope link src 172.22.16.1
192.168.108.0/24 dev ens160 proto kernel scope link src 192.168.108.30 metric 100
[root@docker ~]#

172.17.0.0/16 和 172.22.16.0/24 两个网络的路由都定义好了。再看看 ip forwarding:

bash 复制代码
[root@docker ~]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

ip forwarding 也已经启用了。

条件都满足,为什么不能通行呢?

我们还得看看 iptables:

bash 复制代码
[root@docker ~]# iptables-save
# Generated by iptables-save v1.8.5 on Thu Jul 31 17:09:34 2025
*filter
:INPUT ACCEPT [69184:9702316]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [113709:49882234]
:DOCKER - [0:0]
:DOCKER-ISOLATION-STAGE-1 - [0:0]
:DOCKER-ISOLATION-STAGE-2 - [0:0]
:DOCKER-USER - [0:0]-A FORWARD -j DOCKER-USER-A FORWARD -j DOCKER-ISOLATION-STAGE-1-A FORWARD -o br-ec761bc51778 -m conntrack --ctstate RELATED,ESTABLISHED -j 
ACCEPT
-A FORWARD -o br-ec761bc51778 -j DOCKER
-A FORWARD -i br-ec761bc51778 ! -o br-ec761bc51778 -j ACCEPT
-A FORWARD -i br-ec761bc51778 -o br-ec761bc51778 -j ACCEPT
-A FORWARD -o br-89f7bc11b602 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-89f7bc11b602 -j DOCKER-A FORWARD -i br-89f7bc11b602 ! -o br-89f7bc11b602 -j ACCEPT
-A FORWARD -i br-89f7bc11b602 -o br-89f7bc11b602 -j ACCEPT
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-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-A DOCKER-USER -j RETURN
COMMIT
# Completed on Thu Jul 31 17:09:34 2025
# Generated by iptables-save v1.8.5 on Thu Jul 31 17:09:34 2025
*security
:INPUT ACCEPT [69166:9698194]
:FORWARD ACCEPT [34:2856]
:OUTPUT ACCEPT [113709:49882234]
COMMIT
# Completed on Thu Jul 31 17:09:34 2025
# Generated by iptables-save v1.8.5 on Thu Jul 31 17:09:34 2025
*raw
:PREROUTING ACCEPT [69554:9733396]
:OUTPUT ACCEPT [113709:49882234]
COMMIT
# Completed on Thu Jul 31 17:09:34 2025
# Generated by iptables-save v1.8.5 on Thu Jul 31 17:09:34 2025
*mangle
:PREROUTING ACCEPT [69554:9733396]
:INPUT ACCEPT [69184:9702316]
:FORWARD ACCEPT [370:31080]
:OUTPUT ACCEPT [113709:49882234]
:POSTROUTING ACCEPT [113743:49885090]
COMMIT
# Completed on Thu Jul 31 17:09:34 2025
# Generated by iptables-save v1.8.5 on Thu Jul 31 17:09:34 2025
*nat
:PREROUTING ACCEPT [367:33182]
:INPUT ACCEPT [9:500]
:POSTROUTING ACCEPT [294:23341]
:OUTPUT ACCEPT [291:23089]
:DOCKER - [0:0]-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.22.16.0/24 ! -o br-ec761bc51778 -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-89f7bc11b602 -j MASQUERADE
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A DOCKER -i br-ec761bc51778 -j RETURN
-A DOCKER -i br-89f7bc11b602 -j RETURN
-A DOCKER -i docker0 -j RETURN
COMMIT
# Completed on Thu Jul 31 17:09:34 2025

原因就在这里了:iptables DROP 掉了网桥 docker0 与 br-ec761bc51778(my_net2) 之间双向的流 量。

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

从规则的命名 DOCKER-ISOLATION可知 docker 在设计上就是要隔离不同的 netwrok。

那么接下来的问题是:怎样才能让 busybox1与busybox2 通信呢?

答案是:为 busybox1 容器添加一块 my_net2 的网卡。这个可以通过 docker network connect 命令实现。

bash 复制代码
[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
           
[root@docker ~]# docker network connect my_net2 busybox1

我们在 httpd 容器中查看一下网络配置:

bash 复制代码
[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
       
[root@docker ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
br-89f7bc11b602          8000.0242194d039e       no
br-ec761bc51778          8000.02427fa54b88       no     veth02dd704 #busybox2
                                                        veth5fbb7f6 #busybox3
                                                        veth6642cc9 #busybox1
docker0         8000.02420be905a5       no              vethddb2744

容器中增加了一个网卡 eth1,分配了 my_net2 的 IP 172.22.16.3。现在 busybox2 应该能够访问 busybox1 了,验证一下:

bash 复制代码
[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
/ #

busybox 能够 ping 到 busybox2。当前网络结构如图所示:

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

035 容器通信的三种方式

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

IP 通信

从上一节的例子可以得出这样一个结论:两个容器要能通信,必须要有属于同一个网络的网卡。

满足这个条件后,容器就可以通过 IP 交互了。具体做法是在容器创建时通过--network 指定相应的网络,或者通过 docker network connect 将现有容器加入到指定网络。可参考上一节 busybox 的例子,这里不再赘述。

Docker DNS Server

通过 IP 访问容器虽然满足了通信的需求,但还是不够灵活。因为我们在部署应用之前可能无法确定 IP, 部署之后再指定要访问的 IP 会比较麻烦。对于这个问题,可以通过 docker 自带的 DNS 服务解决。

从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过"容器 名"通信。方法很简单,只要在启动时用 --name 为容器命名就可以了。

下面启动两个容器 bbox1 和 bbox2:

bash 复制代码
[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退出容器

然后,bbox2 就可以直接 ping 到 bbox1 了:

bash 复制代码
[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 有个限制:**只能在 user-defined 网络中使用。**也就是说,默认的 bridge 网络是无法 使用 DNS 的。下面验证一下:

创建 bbox3 和 bbox4,均连接到 bridge 网络。

bash 复制代码
[root@docker ~]# docker run -it --name bbox3 busybox
/ # 
ctrl_p,ctrl_q退出容器

[root@docker ~]# docker run -it --name bbox4 busybox
/ # 
ctrl_p,ctrl_q退出容器

bbox4 无法 ping 到 bbox3。

bash 复制代码
[root@docker ~]# docker exec -it bbox4 sh
/ # ping -c 3 bbox3
ping: bad address 'bbox3'
/ #

joined 容器

joined 容器是另一种实现容器间通信的方式。

joined 容器非常特别,它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之 间可以通过 127.0.0.1 直接通信。请看下面的例子:

先创建一个 httpd 容器,名字为 web1。

bash 复制代码
[root@docker ~]# docker run -d -it --name web1 httpd
f7641c43eb7021724e869f77bd4541e909ebf0968fc35e6a43e8e674d046ef3f

下面我们查看一下 web1 的网络:

bash 复制代码
[root@docker ~]# docker exec -it web1 bash

root@1d5181ce7a85:/usr/local/apache2# hostname -I
172.17.0.4
# 也可以直接通过hostname -I观察IP地址

然后创建 busybox 容器并通过 --network=container:web1 指定 jointed 容器为 web1:

请注意 busybox 容器中的网络配置信息

bash 复制代码
[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
/ #

看!busybox 和 web1 的网卡 mac 地址与 IP 完全一样,它们共享了相同的网络栈。busybox 可以直接 用 127.0.0.1 访问 web1 的 http 服务。

bash 复制代码
/ # 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. 希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序。

容器之间的通信我们已经搞清楚了,接下来要考虑的是容器如何与外部世界通信?这将是下一节的主题。

036 容器如何访问外部世界

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

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

容器访问外部世界

在我们当前的实验环境下,docker host 是可以访问外网的。

bash 复制代码
[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

我们看一下容器是否也能访问外网呢?

bash 复制代码
[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

可见,容器默认就能访问外网

请注意:这里外网指的是容器网络以外的网络环境,并非特指 internet。

现象很简单,但更重要的:我们应该理解现象下的本质。

在上面的例子中,busybox 位于 docker0 这个私有 bridge 网络中(172.17.0.0/16),当 busybox 从 容器向外 ping 时,数据包是怎样到达 www.baidu.com 的呢?

这里的关键就是 NAT。我们查看一下 docker host 上的 iptables 规则:

bash 复制代码
[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
[root@docker ~]#

在 NAT 表中,有这么一条规则:

-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

其含义是:如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理。 而 MASQUERADE 的处理方式是将包的源地址替换成 host 的地址发送出去,**即做了一次网络地址转换 (NAT)。 **

下面我们通过 tcpdump 查看地址是如何转换的。先查看 docker host 的路由表:

bash 复制代码
[root@docker ~]# ip r
default via 192.168.108.2 dev ens160 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.19.0.0/16 dev br-6936dc39839e proto kernel scope link src 172.19.0.1 linkdown
172.22.16.0/24 dev br-c0fc7bdf8143 proto kernel scope link src 172.22.16.1 linkdown
192.168.108.0/24 dev ens160 proto kernel scope link src 192.168.108.30 metric 100

默认路由通过 ens160 发出去,所以我们要同时监控 ens160 和 docker0 上的 icmp(ping)数据包。

使用tcpdump观察现象

bash 复制代码
[root@docker ~]# yum install -y tcpdump
[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

#再开一个窗口
[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

再开一个窗口, busybox ping www.baidu.com

bash 复制代码
[root@docker ~]#  docker run -it busybox
/ #
/ # ping -c 3 www.baidu.com
PING www.baidu.com (223.109.82.41): 56 data bytes
64 bytes from 223.109.82.41: seq=0 ttl=127 time=12.503 ms
64 bytes from 223.109.82.41: seq=1 ttl=127 time=13.380 ms
64 bytes from 223.109.82.41: seq=2 ttl=127 time=11.507 ms

--- www.baidu.com ping statistics --
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 11.507/12.463/13.380 ms
/ #

tcpdump 输出如下:

查看docker0抓包窗口

bash 复制代码
[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
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
15:23:43.857551 IP 172.17.0.2 > 223.109.82.41: ICMP echo request, id 7, seq 1, length 64
15:23:43.880065 IP 223.109.82.41 > 172.17.0.2: ICMP echo reply, id 7, seq 1, length 64
15:23:44.869116 IP 172.17.0.2 > 223.109.82.41: ICMP echo request, id 7, seq 2, length 64
15:23:44.883777 IP 223.109.82.41 > 172.17.0.2: ICMP echo reply, id 7, seq 2, length 64

docker0 收到 busybox 的 ping 包,源地址为容器 IP 172.17.0.2,这没问题,交给 MASQUERADE 处 理。这时,在 ens160 上我们看到了变化:

bash 复制代码
[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
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
15:25:42.887752 IP 192.168.108.30 > 223.109.82.41: ICMP echo request, id 8, seq 1, length 64
15:25:42.899613 IP 223.109.82.41 > 192.168.108.30: ICMP echo reply, id 8, seq 1, length 64
15:25:43.889467 IP 192.168.108.30 > 223.109.82.41: ICMP echo request, id 8, seq 2, length 64
15:25:43.904277 IP 223.109.82.41 > 192.168.108.30: ICMP echo reply, id 8, seq 2, length 64

ping 包的源地址变成了 ens160 的 IP 192.168.108.130

这就是 iptable NAT 规则处理的结果,从而保证数据包能够到达外网。下面用一张图来说明这个过程:

  1. busybox 发送 ping 包:172.17.0.2 > www.baidu.com
  2. docker0 收到包,发现是发送到外网的,交给 NAT 处理。
  3. NAT 将源地址换成 ens160的 IP:192.168.108.30 > www.baidu.com
  4. ping 包从 ens160 发送出去,到达 www.baidu.com

通过 NAT,docker 实现了容器对外网的访问。

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

037 外部世界如何访问容器

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

答案是:端口映射。

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

通过-p参数映射端口:

容器启动后,可通过 docker ps 或者 docker port 查看到 host 映射的端口。在上面的例子中,httpd 容器的 80 端口被映射到 host 32768 上,这样就可以通过 <host ip>:<32768> 访问容器的 web 服务 了。

bash 复制代码
[root@docker ~]# curl 192.168.108.30:32768
<html><body><h1>It works!</h1></body></html>

除了映射动态端口,也可在 -p 中指定映射到 host 某个特定端口,例如可将 80 端口映射到 host 的 8080 端口:

bash 复制代码
[root@docker ~]# docker run -d -p 8080:80 httpd
25b04cb7f6d6c012c609251a425475fcf7da03c93feb20c1d951eb45cb1ef79b
[root@docker ~]#
[root@docker ~]# curl 192.168.108.30:8080
<html><body><h1>It works!</h1></body></html>

每一个映射的端口,host 都会启动一个 docker-proxy 进程来处理访问容器的流量:

以 0.0.0.0:8080->80/tcp 为例分析整个过程:

  1. docker-proxy 监听 host 的 8080 端口。
  2. 当 curl 访问 192.168.108.30:8080时,docker-proxy 转发给容器 172.17.0.3:80。
  3. httpd 容器响应请求并返回结果。

实战:安装tomcat

docker hub上面查找tomcat镜像

bash 复制代码
[root@docker ~]# docker search tomcat
NAME                   DESCRIPTION                                     STARS     OFFICIAL
tomcat                 Apache Tomcat is an open source implementati...   3751      [OK]
bitnami/tomcat         Bitnami container image for Tomcat              52
bitnamicharts/tomcat   Bitnami Helm chart for Apache Tomcat            0
rootpublic/tomcat                                                      0
chainguard/tomcat      Build, ship and run secure software with Cha...   0
islandora/tomcat       Base tomcat image used for java services.       0
vulhub/tomcat                                                          0
openeuler/tomcat                                                       0
tomcat/tomcat01                                                        0
jelastic/tomcat        An image of the Tomcat Java application serv...   4
rightctrl/tomcat       CentOS , Oracle Java, tomcat application ssl...   7
amd64/tomcat           Apache Tomcat is an open source implementati...   10
softwareplant/tomcat   Tomcat images for jira-cloud testing            0
arm64v8/tomcat         Apache Tomcat is an open source implementati...   13
appsvc/tomcat                                                          1
s390x/tomcat           Apache Tomcat is an open source implementati...   0
ppc64le/tomcat         Apache Tomcat is an open source implementati...   1
arm32v7/tomcat         Apache Tomcat is an open source implementati...   12
awscory/tomcat         tomcat                                          0
devbeta/tomcat                                                         0
tutum/tomcat           Base docker image to run a Tomcat applicatio...   11
qasymphony/tomcat      Tomcat images                                   1
hegand/tomcat          docker-tomcat                                   0
techangels/tomcat                                                      0
cloudesire/tomcat      Tomcat server, 6/7/8                            14

从docker hub上拉取tomcat镜像到本地

bash 复制代码
[root@docker ~]# docker pull tomcat

docker images查看是否有拉取到的tomcat

bash 复制代码
[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

使用tomcat镜像创建容器实例(也叫运行镜像)

bash 复制代码
[root@docker ~]# docker run -itd -p 8080:8080 tomcat
92d78922526ba2a54ef63c48d2fec80b10997027de93457a4e2d56d7ea7448e2

访问tomcat首页

把webapps.dist目录换成webapps

bash 复制代码
#查看刚才创建的tomcat容器ID
[root@docker ~]# docker ps
CONTAINER ID   IMAGE     COMMAND             CREATED         STATUS                     PORTS      NAMES
92d78922526b   tomcat    "catalina.sh run"   2 minutes       up 2 minutes
0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   fervent_brahmagupta

#进入tomcat容器
[root@docker ~]# docker exec -it 92d bash

#查看webapps文件夹为空
root@92d78922526b:/usr/local/tomcat# ls webapps
root@92d78922526b:/usr/local/tomcat#
 
#文件在webapps.dist
root@92d78922526b:/usr/local/tomcat# ls webapps.dist/
docs  examples  host-manager  manager  ROOT

#用webapps.dist替换webapps
root@92d78922526b:/usr/local/tomcat# mv webapps.dist/* webapps

再次访问tomcat

思考:我想要基于tomcat镜像做出一个能够直接访问的tomcat镜像该如何做?

思路:

  • 先启动原始 tomcat 容器
  • 进入容器修复目录
  • 将修改后的容器提交为新镜像
  • 后续直接使用新镜像启动即可

本章小结

在这一章我们首先学习了 Docker 的三种网络:none, host 和 bridge 并讨论了它们的不同使用场景;然 后我们实践了创建自定义网络;最后详细讨论了如何实现容器与容器之间,容器与外部网络之间的通信。

本章重点关注的是单个主机内的容器网络,下一节开始学习 Docker 存储。

相关推荐
联众合科技24 分钟前
AF 防火墙与传统防火墙核心差异及新型威胁防御实测
网络·安全
三言老师28 分钟前
free查看内存与缓存状态实操
运维·服务器·网络·centos
奥莱维31 分钟前
酒店客控系统多少钱_2026不同规模报价区间透明解析
大数据·网络
无锡耐特森34 分钟前
通过CAN转Profinet网关与PLC的配合引领智慧物流的未来
运维·服务器·网络
羽翼安全1 小时前
严防屏幕泄密:企业防拍摄系统的技术原理与落地实践
网络·安全·电脑
有续技术1 小时前
三菱 (Mitsubishi) 机床 设置查看 IP 地址
linux·服务器·网络
2401_873479401 小时前
IPv6查出来的地理位置是错的?用双栈IP库解决定位偏差问题
网络·数据库·tcp/ip·ip
花生了什么事o1 小时前
WebSocket 与 SSE:实时通信方案的选择
网络·websocket·网络协议
yagami_gagami1 小时前
第四届黄河流域公安院校电子物证个人赛服务器取证
linux·服务器·网络·mysql·安全·docker·llama
九硕智慧建筑一体化厂家1 小时前
直流照明|档案库房专用照明,防紫外线、防纸张老化、适配24小时值守
运维·网络·笔记·智慧城市