Bridge模式

bridge模式是Docker默认的网络模式,此模式会为每一个容器分配独立的Network Namespace。

Bridge模式示意图
Bridge模式示意图

实现原理

  • 我们只要在宿主机上安装了docker,就会创建一个虚拟网桥docker0
  • 我们每启动一个docker容器,docker就会给容器分配一个docker0的子网的ip,同时会创建了一对 veth pair 接口,一端连接容器内部,一端连接docker0网桥
  • 通过这种方式,主机可以跟容器通信,容器之间也可以相互通信
查看docker0与veth pair
运行容器前系统网络信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#查看系统网络信息,安装了docker就会有docker0
[root@localhost ~]# 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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:16:3e:16:fa:95 brd ff:ff:ff:ff:ff:ff
inet 172.27.45.106/20 brd 172.27.47.255 scope global dynamic noprefixroute eth0
valid_lft 315324622sec preferred_lft 315324622sec
inet6 fe80::216:3eff:fe16:fa95/64 scope link
valid_lft forever preferred_lft forever
6: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:84:89:96:a4 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::42:84ff:fe89:96a4/64 scope link
valid_lft forever preferred_lft forever
容器运行
1
2
3
4
5
#运行两个网络模式是bridge的容器(默认就是bridge)
[root@localhost ~]# docker run -d --name nginx_c_b1 -p 8561:80 nginx
c19004406443ed36417ece5a29f855feeea1e1fa745d61c1df356cba1874b58f
[root@localhost ~]# docker run -d --name nginx_c_b2 -p 8562:80 nginx
b01797d4cc20969f10c2310edf031d6ca50448205715ffc6a050339eb1d0fc6f
容器运行后系统信息
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
[root@localhost ~]# 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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:16:3e:16:fa:95 brd ff:ff:ff:ff:ff:ff
inet 172.27.45.106/20 brd 172.27.47.255 scope global dynamic noprefixroute eth0
valid_lft 315324281sec preferred_lft 315324281sec
inet6 fe80::216:3eff:fe16:fa95/64 scope link
valid_lft forever preferred_lft forever
6: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:84:89:96:a4 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::42:84ff:fe89:96a4/64 scope link
valid_lft forever preferred_lft forever
99: vethb22303b@if98: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether f2:65:40:31:c5:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::f065:40ff:fe31:c503/64 scope link
valid_lft forever preferred_lft forever
101: veth770c276@if100: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 72:aa:84:01:7e:c8 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::70aa:84ff:fe01:7ec8/64 scope link
valid_lft forever preferred_lft forever
再查看bridge信息,容器已经加入到bridge里面,ip地址就是子网的ip
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@localhost ~]# docker network inspect bridge
[
{
...
"Containers": {
"62766e60bf80b0d60090fa857313645fc5e9817c50cc65358bd82052a20e0d14": {
"Name": "nginx_c_b2",
"EndpointID": "cdd96b9b6c643c25ee85c17b65d70ecaa2eb663651f1130bd92a2c1957d11a51",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"88fe91f8d2a51f357a1bbc7c00e7025f02deea2fa3dfa4884d1d98b47d8b1b7e": {
"Name": "nginx_c_b1",
"EndpointID": "501826389e3cc55943cc9abcce93b4529b496e98f342a9e920b6b045611dcdba",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
}
...
}
]

宿主机访问容器,容器之间访问

宿主机请求容器成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@localhost ~]# curl 172.17.0.3
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
进入一个容器,请求另外的容器成功
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
[root@localhost ~]# docker exec -it nginx_c_b1 /bin/bash
root@c19004406443:/# curl 172.17.0.3
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

容器访问外部网络的过程

1
2
3
4
5
6
#容器内访问外网地址成功
[root@localhost ~]# docker exec -it nginx_c_b1 /bin/bash
root@41abcb9be1b3:/# curl www.baidu.com
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div
...

主机有一块网卡为eth0,从主机上一个IP为172.17.0.2的容器中访问百度。

IP包首先判断要访问的地址非本子网,从容器发往自己的默认网关docker0,然后会查询主机的路由表,发现包应该从主机的eth0发往主机的网关。IP包就会转发给eth0,并从eth0发出去。

1
2
3
4
5
6
7
8
9
10
#查看系统路由表
[root@localhost ~]# ip route
default via 172.27.47.253 dev eth0 proto dhcp metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.27.32.0/20 dev eth0 proto kernel scope link src 172.27.45.106 metric 100

# 容器要想访问外部网络,需要本地系统的转发支持。
# 在Linux 系统中,检查转发是否打开。如果为 0,说明没有开启转发,则需要手动打开。
[root@localhost ~]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

外部访问容器的过程

在创建完两个带暴露端口的容器后,查看Iptable规则的变化,发现多了两个网址规则,这些规则就是对主机eth0收到的目的端口为8563/8564的tcp流量进行DNAT转换,将流量发往172.17.0.2:80/172.17.0.3:80,也就是我们创建的Docker容器。所以,外界只需访问宿主机地址:8561/8562就可以访问到容器中的服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#查看系统iptables,新增加两个地址匹配规则
[root@localhost ~]# iptables-save|grep -i docker0
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8561 -j DNAT --to-destination 172.17.0.2:80
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8562 -j DNAT --to-destination 172.17.0.3:80
-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 -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
-A DOCKER -d 172.17.0.3/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP

--link实现容器名访问

IP地址可以实现互联互通,但是直接能用名称访问是更好的方式。

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
# 直接名称访问是不行的
[root@localhost ~]# docker exec -it nginx_c_b1 curl nginx_c_b2
curl: (6) Could not resolve host: nginx_c_b2

# 容器运行的时候给容器创建连接
[root@localhost ~]# docker run -d --name nginx_c_b3 --link nginx_c_b2 nginx
c234f762227516399a081707bce0f17710f1efee0d36d0cbc949ddb1f5f4e292

# 重新用名称访问成功
[root@localhost ~]# docker exec -it nginx_c_b3 curl nginx_c_b2
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

# 反过来用名称访问也是不行的
[root@localhost ~]# docker exec -it nginx_c_b2 curl nginx_c_b3
curl: (6) Could not resolve host: nginx_c_b3
原理探究:--link本质就是在hosts配置中添加映射
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
# 查看容器详情,有一个连接记录
[root@localhost ~]# docker inspect nginx_c_b3
[
{
"Links": [
"/nginx_c_b2:/nginx_c_b3/nginx_c_b2"
],
}
]

# 查看容器内部IP映射文件,link就是加了一条映射 ID和容器名称都是可以直接访问的
[root@localhost ~]# docker exec -it nginx_c_b3 cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 nginx_c_b2 edb46469326c
172.17.0.4 c234f7622275

[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c234f7622275 nginx "/docker-entrypoint.…" 4 minutes ago Up 4 minutes 80/tcp nginx_c_b3
edb46469326c nginx "/docker-entrypoint.…" 35 minutes ago Up 35 minutes 0.0.0.0:8562->80/tcp, :::8562->80/tcp nginx_c_b2
41abcb9be1b3 nginx "/docker-entrypoint.…" 35 minutes ago Up 35 minutes 0.0.0.0:8561->80/tcp, :::8561->80/tcp nginx_c_b1