目录

Docker - 网络网桥

# 网桥原理

当 Docker 启动时,会自动在主机上创建一个 docker0 虚拟网桥,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机。它会在挂载到它的网口之间进行转发。

同时,Docker 随机分配一个本地未占用的私有网段(在 RFC1918 (opens new window)中定义)中的一个地址给 bridge 接口。比如典型的 172.17.42.1,掩码为 255.255.0.0。此后启动的容器内的网口也会自动分配一个同一网段(172.17.0.0/16)的地址。

当创建一个 Docker 容器的时候,同时会创建了一对 veth pair 接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包,它常常充当着一个桥梁。)。这对接口一端在容器内,即 eth0;另一端在本地并被挂载到 docker0 网桥,名称以 veth 开头(例如 vethAQI2QT)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络。

img

img

# 理解网桥

首先我们可以查看操作系统的 IP,会发现 Docker 的 IP

执行命令:ip address 或者 ip a

image-20211120185120699

每次启动一个容器,都会增加一个 IP。

Docker 保证了容器的隔离性,但是容器之间也是可以通信的,当部署了很多项目,或者很多软件,如 MySQL,Redis,ElasticSearch。在实际生产环境上,我们不可能只是单独使用其中一个,我们需要数据的交互,如 MySQL 数据缓存到 Redis 里,那么它们就需要进行连接,容器之间是隔离的,而想要连接,就是需要通过网络。

上文提到,按照 Docker 服务默认会创建一个 docker0 网桥,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。

形象化理解:两个地方需要传递消息,首先需要中转站,首先两个地方要有消息站,两个地方的消息站叫做 eth0,中转站叫做 veth。消息站之间通信的方式是信号,两个地方信号是 127.17.xx.xx,中转站信号是 127.17.0.1,所以通信就非常简单了,一个地方先通过信号发给中转站,中转站再转发给另一个地方,实现联系。

image-20210615232726561

可以通过 docker network ls 命令查看网桥:


 







# 执行命令
docker network ls

# 返回结果
NETWORK ID     NAME      DRIVER    SCOPE
a4233d5cfcdb   bridge    bridge    local  
5eeb9eeac070   host      host      local
7cbed644a605   none      null      local
1
2
3
4
5
6
7
8

笔记

网桥名 bridge 就是默认网桥 docker0。

习惯了叫 docker0。

2021-11-20 @Young Kbt

Docker 安装时会自动在 host 上创建三个网络:none,host,和 bridge。我们看下 docker0 网桥:(brctl 可以通过 yum install bridge-utils 安装)


 







# 执行命令
brctl show

# 返沪结果
bridge name	bridge id	STP   enabled	interfaces
docker0		8000.02425dfadde1	no		veth02edcbb
										vethf548dd3
virbr0		8000.5254007ac9df	yes		virbr0-nic
1
2
3
4
5
6
7
8

# 操作网桥

笔记

网桥和镜像、容器一样,也有两个标识:id 和 名字。

使用任意一个标识都可以操作网桥,本内容使用网桥的名字(name)

2021-11-20 @Young Kbt

# 为什么不使用默认的网桥

首先要知道,启动一个容器,默认是使用网桥 docker0,但是实际生产环境,我们不可能把 N 多个项目或者环境都放在一个网桥上。网桥是通信的媒介,我们应该自己创建网桥,把关联的项目放在一个网桥上,不关联的项目放到另一个网桥上,防止毫不相干的项目因为争夺资源等因素影响其他项目的运行,因为不同的网桥是隔离的。

疑惑

一个容器只能连接一个网桥吗?或者说不同网桥上的容器之间可以连接吗?

答案在 连接多个网桥

2021-11-20 @Young Kbt

image-20211120193020951

# 网桥命令列表


 















# 执行命令
docker network --help

# 返回结果
docker network
Usage:  docker network COMMAND
Manage networks
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  # 删除指定的网桥
Run 'docker network COMMAND --help' for more information on a command. 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 查看网桥列表

# 执行命令
docker network ls

# 返回结果
docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a4233d5cfcdb   bridge    bridge    local
5eeb9eeac070   host      host      local
7cbed644a605   none      null      local
1
2
3
4
5
6
7
8
9

# 创建自定义网桥

创建自定义网桥命令格式:docker network create [options] <网桥名>

docker network create [options] <网桥名>

# 指定 DRIVER,默认是 bridge
docker network create -d xxx <网桥名>

# 指定 子网掩码
docker network create --subnet <子网掩码 IP> <网桥名>

# 指定 网关
docker network create --gateway  <网关 IP> <网桥名>

# 完整
docker network create -d xxx --subnet <子网掩码 IP> --gateway <网关 IP> xxx <网桥名>
1
2
3
4
5
6
7
8
9
10
11
12
13

使用 -d,配置网络的类型(DRIVER),默认是 bridge

例子:创建 kele 网桥














 


# 执行命令
docker network create kele

# 返回结果
922ea0d2e922b48bb5cd3adc2c63fc02079927a6971c491fc5a1a2e4a45e2073

# 执行命令
docker network ls

# 返回结果
NETWORK ID     NAME      DRIVER    SCOPE
a4233d5cfcdb   bridge    bridge    local
5eeb9eeac070   host      host      local
922ea0d2e922   kele      bridge    local
7cbed644a605   none      null      local
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

例子:创建 bing 网桥,指定该网桥的网关和子网掩码












 





# 执行命令
docker network create -d bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 bing

# 返回结果
7e9d2f900b52563588e10e8c3071219c629203356c5fe0f3fec25fec477c0757

# 执行命令
docker network ls

# 返回结果
NETWORK ID     NAME      DRIVER    SCOPE
7e9d2f900b52   bing      bridge    local
a4233d5cfcdb   bridge    bridge    local
5eeb9eeac070   host      host      local
922ea0d2e922   kele      bridge    local
7cbed644a605   none      null      local
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 查看网桥详细信息

docker network inspect <网桥id | 网桥名>
1

例子:查看 bridge 的详细信息




















 
 












 






 


















# 执行命令
docker network inspect a4233d5cfcdb
# 或者
docker network inspect bridge

# 返回结果
[
    {
        "Name": "bridge",
        "Id": "a4233d5cfcdbf99321aa1893c1846278497008f60e10787bf2341f45a830beee",
        "Created": "2021-11-20T11:30:23.135345272+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": {
            "1365f332be6b57943dfbfc1ececddac3a2b3c6ad12a17313a427041e27a2c044": {
                "Name": "tomcat01",
                "EndpointID": "8f96af6293e77ee301c83f8325f1f78c3cc027641a5e95ea93160bfba7315ad3",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            },
            "e2bb571eb1687719f346bce4cec6bd5dff5240b8bf090b451ef05c2cb6bee167": {
                "Name": "tomcat02",
                "EndpointID": "6e397e04b57a5ecbbd04d506c0170391f5fbdca5270bb826b1d7daf48e1de92e",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/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": {}
    }
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

20 - 21 行代码可以看出默认网桥的网关就是 172.17.0.1

34 和 41 行代码可以看出该网桥上的容器是 tomcat01 和 tomcat02。

# 删除网桥

删除某个网桥命令格式:docker network rm <网桥名称 | 网桥 id>

删除全部未使用的网桥命令格式:docker network prune

# 删除某个网桥
docker network rm <网桥名称 | 网桥 id>

# 删除全部网桥
docker network prune
1
2
3
4
5

例子:删除 kele 网桥












 

 

# 执行命令
docker network ls

# 返回结果
NETWORK ID     NAME      DRIVER    SCOPE
a4233d5cfcdb   bridge    bridge    local
5eeb9eeac070   host      host      local
922ea0d2e922   kele      bridge    local
7cbed644a605   none      null      local

# 删除网桥
docker network rm 922ea0d2e922
# 或者
docker network rm kele
1
2
3
4
5
6
7
8
9
10
11
12
13
14

例子 2:删除所有网桥

docker network prune
1

警告

这个命令谨慎操作!

2021-11-20 @Young Kbt

# 容器使用网桥

启动一个容器默认使用 bridge 网桥,我们在启动的时候指定某一个网桥。

一旦在启动容器时指定了网桥之后,日后可以在任何这个网桥关联的容器中使用 容器名字 进行与其他容器通信。

格式:docker run -d -p 宿主机端口:容器端口 --name <自定义名> --network <网桥名> <镜像>:<TAG | ID>

docker run -d -p 宿主机端口:容器端口 --name <自定义名> --network <网桥名> <镜像>:<TAG | ID>

# 或者
docker run -d -p 宿主机端口:容器端口 --network <网桥名> --network-alias <网桥别名> <镜像>:<TAG | ID>
1
2
3
4

如果不指定 --network,创建的容器默认都会挂到 docker0 上,使用本地主机上 docker0 接口的 IP 作为所有容器的默认网关。

Docker 在创建一个容器的时候,会执行如下操作:

  • 创建一对虚拟接口/网卡,也就是 veth pair,分别放到本地主机和新容器中;
  • 本地主机一端桥接到默认的 docker0 或指定网桥上,并具有一个唯一的名字,如 vethxxxxx;
  • 容器一端放到新容器中,并修改名字作为 eth0,这个网卡/接口只在容器的名字空间可见;
  • 从网桥可用地址段中(也就是与该 bridge 对应的 network)获取一个空闲地址分配给容器的 eth0,并配置默认路由到桥接网卡 vethxxxx。

例子:启动两个 Tomcat 容器,绑定 kele 网桥,并验证互相连接

启动容器,绑定网桥









 


 









# 执行命令
docker images tomcat

# 返回结果
REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
tomcat        8.5.73    7ec084df520c   47 hours ago   249MB

# 启动一个容器,绑定 kele 网桥,名字叫做 tomcat02
docker run -d -p 8081:8080 --name tomcat02 --network kele tomcat:8.5.73

# 启动一个容器,绑定 kele 网桥,名字叫做 tomcat03
docker run -d -p 8082:8080 --name tomcat03 --network kele tomcat:8.5.73

# 执行命令
docker ps

# 返回结果
CONTAINER ID   IMAGE           COMMAND             CREATED         STATUS         PORTS                                       NAMES
b8151edb05f3   tomcat:8.5.73   "catalina.sh run"   8 seconds ago   Up 7 seconds   0.0.0.0:8082->8080/tcp, :::8082->8080/tcp   tomcat03
58224b3d8a32   tomcat:8.5.73   "catalina.sh run"   2 minutes ago   Up 2 minutes   0.0.0.0:8081->8080/tcp, :::8081->8080/tcp   tomcat02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

进入其中一个容器,验证是否能连接另一个容器,这里进入 tomcat02 容器

首先知道 tomcat03 的 IP 地址














 









docker inspect tomcat03

# 返回结果(部分)
"Networks": {
    "kele": {
        "IPAMConfig": null,
        "Links": null,
        "Aliases": [
        	"b8151edb05f3"
    	],
        "NetworkID": "922ea0d2e922b48bb5cd3adc2c63fc02079927a6971c491fc5a1a2e4a45e2073",
        "EndpointID": "71cbf2b0317d5776931d51979e77100d00b91433b76770651c5f1a8dd88d21e0",
        "Gateway": "172.18.0.1",
        "IPAddress": "172.18.0.3",
        "IPPrefixLen": 16,
        "IPv6Gateway": "",
        "GlobalIPv6Address": "",
        "GlobalIPv6PrefixLen": 0,
        "MacAddress": "02:42:ac:12:00:03",
        "DriverOpts": null
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

可以知道 tomcat03 的 IP 是 172.18.0.3


 







# 进入 tomcat02
docker exec -it tomcat02 bash

# curl 拉取资源
root@58224b3d8a32:/usr/local/tomcat# curl http://172.18.0.2:8080

# 返回结果(部分)
!doctype html><html lang="en"><head><title>HTTP Status 404 – Not Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404 – Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr class="line" /><h3>Apache Tomcat/8.5
1
2
3
4
5
6
7
8

只要返回结果不报错,代表容器之间可以通信,报错案例:

curl: (7) Failed to connect to 172.18.0.3 port 8082: Connection refused
1

此时应该意识到一个问题:通过 IP 访问 其他容器非常麻烦,如果其他容器 IP 被修改,那么将无法访问,那如何解决呢?

别忘了容器的 name(姓名),Docker 在启动容器的时候,自动将 name(姓名)与 IP 进行绑定,所以访问其他容器 name(姓名)也能访问其他容器


 




# curl 拉取资源
root@58224b3d8a32:/usr/local/tomcat# curl http://tomcat03:8080

# 返回结果(部分)
!doctype html><html lang="en"><head><title>HTTP Status 404 – Not Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404 – Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr class="line" /><h3>Apache Tomcat/8.5
1
2
3
4
5

如果不喜欢使用 name(姓名)进行访问,也可以在启动时给网桥单独去别名,只属于自己的网桥别名,通过该别名也能访问

# 启动一个容器,给所在的网桥取个别名
docker run -d --name tomcat04 --network kele --network-alias kele_tomcat tomcat:8.5.7
1
2

这样,也可以通过网桥的别名访问该容器:

# curl 拉取资源
root@58224b3d8a32:/usr/local/tomcat# curl http://kele_tomcat:8080
1
2

当然,建议网桥的别名和容器的 name(姓名)保持一致。

那么,处于 kele 网桥的容器能访问其他网桥容器吗?

启动新的 Tomcat 容器,让他处于默认网桥 docker0,名字叫做 tomcat01


 










# 如果不加 --network xx,会自动加上,并且 xx 是 bridge
docker run -d -p 8081:8080 --name tomcat01--network bridge tomcat:8.5.73

# 执行命令
docker ps

# 返回结果
CONTAINER ID   IMAGE           COMMAND             CREATED        STATUS       PORTS                                       NAMES
b8151edb05f3   tomcat:8.5.73   "catalina.sh run"   2 hours ago    Up 2 hours   0.0.0.0:8082->8080/tcp, :::8082->8080/tcp   tomcat03
58224b3d8a32   tomcat:8.5.73   "catalina.sh run"   2 hours ago    Up 2 hours   0.0.0.0:8081->8080/tcp, :::8081->8080/tcp   tomcat02
1365f332be6b   tomcat:8.5.73   "catalina.sh run"   22 hours ago   Up 9 hours   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   tomcat01
1
2
3
4
5
6
7
8
9
10
11

进入 tomcat01 容器,访问 tomcat02 或者 tomcat03 容器





 




# 进入容器
docker exec -it tomcat01 bash

# 访问 tomcat02
root@1365f332be6b:/usr/local/tomcat# curl http://tomcat02:8080

# 返回结果,失败
curl: (6) Could not resolve host: tomcat02
1
2
3
4
5
6
7
8

明显是失败的,说明不同网桥的容器无法互相访问,这就解决了容器之间互相争夺资源(CPU、内存)的清空。

所以项目比较多时,建议创建自定义网桥,不要全部堆积在默认网桥。

# Link连接容器

这是一个过时的命令,我们不推荐使用! 我们建议使用 容器使用网桥,但是这里还是简单演示该操作。

该命令是在启动容器时使用,格式:docker run -d -p 宿主机端口:容器端口 --name <自定义名> --link <其他容器名 | 容器 id> <镜像>:<TAG | ID>

它连接的其他容器必须处于默认网桥 docker0 上。

docker run -d -p 宿主机端口:容器端口 --name <自定义名> --link <其他容器名 | 容器 id> <镜像>:<TAG | ID>
1

--link 其他容器,就是连接其他容器的网桥。

例子 1:启动 tomcat04 容器时,连接 tomcat 03 容器

使用 -P(大写)容器端口会随机映射宿主机端口,如果不打算外部访问,只是 Docker 内部使用,则可以使用该命令


 




# 执行命令
docker run -d -P --name tomcat04 --link tomcat03 tomcat:8.5.73

# 返回结果,报错了
docker: Error response from daemon: Cannot link to /tomcat03, as it does not belong to the default network.
1
2
3
4
5

从返回结果得出,--link 只能连接处于默认网桥 docker0 的容器

例子 2:启动 tomcat04 容器时,连接 tomcat 01 容器

首先删除 tomcat04,虽然例子 1 报错了,但是还是成功创建了 tomcat04,并且 tomcat04 绑定的时默认网桥

docker rm tomcat04
1

然后开始连接 tomcat01 容器


 






















# 执行命令
docker run -d -p 8084:8080 --name tomcat04 --link tomcat01 tomcat:8.5.73

# 返回结果,报错了
docker: Error response from daemon: Cannot link to /tomcat03, as it does not belong to the default network.

# 进入 tomcat04 容器
docker exec -it tomcat04 bash
# 访问 tomcat01
root@c175286f1fa4:/usr/local/tomcat# curl http://tomcat01:8080

# 返回结果(部分)
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Apache Tomcat/8.5.73</title>
        <link href="favicon.ico" rel="icon" type="image/x-icon" />
        <link href="tomcat.css" rel="stylesheet" type="text/css" />
    </head>
    ......
    ......
 </htm>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

说明连接上了 tomcat01 容器的网桥。

# 连接多个网桥

回答

容器启动时指定连接了一个网桥,那么还可以连接其他网桥吗?答案是可以的。

2021-11-20 @Young Kbt

连接多个网桥命令格式:docker network connect <容器名 | 容器 id> <网桥名 | 网桥 id>

docker network connect <容器名 | 容器 id> <网桥名 | 网桥 id>
1

例子:tomcat 04 连接 kele 桥段

tomcat04 已经连接了 bridge 网桥,我们也可以让它连接上其他的网桥如 kele 网桥

# 执行命令
docker network connect kele tomcat04
1
2

如何查看是否连接上了呢?

docker inspect tomcat04
1

返回结果:

image-20211120222149906

可以知道 tomcat04 既连接上了 bridge 网桥,也连接上了 kele 网桥。

结论:如果要跨网络操作别人,就需要使用 docker network connect <容器名 | 容器 id> <网桥名 | 网桥 id> 进行连接。

# 取消连接网桥

笔记

当一个容器连接了多个网桥,比如上面的 tomcat04 连接了 bridgekele 网桥,如何取消连接某个网桥呢?

2021-11-20 @Young Kbt

取消连接网桥命令格式:docker network disconnect <容器名 | 容器 id> <网桥名 | 网桥 id>

docker network disconnect <容器名 | 容器 id> <网桥名 | 网桥 id>
1

例子:tomcat04 取消连接 kele 网桥

docker network disconnect kele tomcat04
1

如何查看是否取消连接了呢?

docker inspect tomcat04
1

返回结果:只有 bridge 网桥

image-20211120222746978

更新时间: 2024/01/17, 05:48:13
最近更新
01
JVM调优
12-10
02
jenkins
12-10
03
Arthas
12-10
更多文章>