Docker系列之七:Docker 网络(内部网络、容器之间的连接)
系列链接Docker系列之一:Docker介绍及在Ubuntu上安装Docker系列之二:Docker 入门Docker系列之三:使用Docker镜像和仓库Docker系列之四:Dockerfile的使用Docker系列之五:Volume 卷的使用——以Redis为例Docker系列之六:Volume 卷的使用——在Dockerfile中的用法Docker系列之七:Do...
系列链接
Docker系列之一:Docker介绍及在Ubuntu上安装
Docker系列之五:Volume 卷的使用——以Redis为例
Docker系列之六:Volume 卷的使用——在Dockerfile中的用法
Docker系列之七:Docker 网络(内部网络、容器之间的连接)
Docker系列之八:在Dockerfile中使用多段构建Muti-stage build
Docker系列之九:Docker用于持续集成,构建Jenkins和Docker服务器
简介
到目前为止,我们的所有Docker容器都是公开端口并绑定到本地网络接口的,这样可以把容器里的服务在本地Docker宿主机所在的外部网络上(比如 docker run -p 81:80 nginx, 就是把容器里的80端口公开给宿主机的81端口)公开。除了这种用法外,还有一种就是内部网络。
在安装Docker时,会在宿主机上创建一个新的网络接口,名字是docker0,如下图
root@iZbp13z6cxj72rb7bxf0smZ:/home/cong/DockerDemo# ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:a0:ee:6d:22
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:135232 errors:0 dropped:0 overruns:0 frame:0
TX packets:183102 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:10375138 (10.3 MB) TX bytes:410126623 (410.1 MB)
eth0 Link encap:Ethernet HWaddr 00:16:3e:12:3d:bb
inet addr:172.16.252.142 Bcast:172.16.252.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:256622292 errors:0 dropped:0 overruns:0 frame:0
TX packets:250880563 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:28440795076 (28.4 GB) TX bytes:33484115651 (33.4 GB)
veth0653184 Link encap:Ethernet HWaddr da:a2:c2:62:b8:ca
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 errors:0 dropped:0 overruns:0 frame:0
TX packets:9 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:452 (452.0 B) TX bytes:624 (624.0 B)
网络接口docker0是一个虚拟的以太网桥,它专门用于连接容器和本地宿主机网络,每个Docker容器都会在这个接口上分配一个IP地址,接口本身的地址是172.17.0.1,子网掩码是255.255.0.0,也就是说Docker会默认使用172.17.X.X作为子网地址。
如果进一步查看Docker宿主机的其它网络,发现在一系列名这以veth开头的网络接口。Docker每创建一个容器就会创建一组互联的网络接口,这组接口就像管道的两端(就是说,从一端发送的数据会在另一端接收到)。这组接口其中一端作为容器里的eth0接口(注意不是上图的eth0),而另一端统一命名为类似veth0653184这种名字,作为宿主机的一个端口。可以把veth接口认为是虚拟网线的一端,这个虚拟网线的一端插在名为docker0的网桥上,另一端插到容器里的eth0上。通过把每个veth*接口绑定到docker0网桥,Docker创建了一个虚拟的子网,这个子网有宿主机和所有的Docker容器共享。
进入容器看看子网的另一端,IP地址为172.17.0.3,如下图。
root@iZbp13z6cxj72rb7bxf0smZ:/home/cong/DockerDemo# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
70ea150288c3 redis "docker-entrypoint.s…" About an hour ago Up About an hour 0.0.0.0:6380->6379/tcp optimistic_curran
1b25aef5f610 redis "docker-entrypoint.s…" About an hour ago Up About an hour 0.0.0.0:6379->6379/tcp kind_curran
root@iZbp13z6cxj72rb7bxf0smZ:/home/cong/DockerDemo# docker exec -it 70ea bash
root@70ea150288c3:/data# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.3 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:03 txqueuelen 0 (Ethernet)
RX packets 1787 bytes 8247431 (7.8 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1510 bytes 108655 (106.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
我们从容器内对外通信,观察这个路由,看看是如何建立连接的。
root@70ea150288c3:/data# apt-get update && apt-get install -y traceroute
root@70ea150288c3:/data# traceroute baidu.com
traceroute to baidu.com (123.125.115.110), 30 hops max, 60 byte packets
1 172.17.0.1 (172.17.0.1) 0.025 ms 0.011 ms 0.009 ms
2 * * *
3 11.223.44.53 (11.223.44.53) 4.664 ms 11.223.48.189 (11.223.48.189) 5.392 ms 11.223.44.53 (11.223.44.53) 4.753 ms
4 * 11.223.48.6 (11.223.48.6) 5.342 ms 11.223.48.142 (11.223.48.142) 5.624 ms
5 11.223.80.42 (11.223.80.42) 1.225 ms 11.223.80.38 (11.223.80.38) 1.336 ms 1.337 ms
6 106.11.37.50 (106.11.37.50) 1.316 ms 1.528 ms 116.251.118.30 (116.251.118.30) 1.495 ms
7 42.120.247.62 (42.120.247.62) 4.186 ms 42.120.247.70 (42.120.247.70) 1.692 ms 42.120.247.66 (42.120.247.66) 1.558 ms
8 124.160.190.57 (124.160.190.57) 2.119 ms 124.160.190.69 (124.160.190.69) 2.384 ms 124.160.190.85 (124.160.190.85) 1.644 ms
9 124.160.189.117 (124.160.189.117) 7.897 ms 124.160.189.121 (124.160.189.121) 9.376 ms 124.160.189.113 (124.160.189.113) 7.0 31 ms
10 219.158.107.217 (219.158.107.217) 29.105 ms 29.066 ms 28.828 ms
11 61.49.214.2 (61.49.214.2) 29.539 ms 29.504 ms 29.322 ms
12 61.51.115.110 (61.51.115.110) 31.945 ms 31.897 ms 31.632 ms
13 61.49.168.98 (61.49.168.98) 30.156 ms 202.106.43.66 (202.106.43.66) 28.249 ms 61.49.168.102 (61.49.168.102) 30.589 ms
14 * * *
可以看到,容器地址后的下一跳是宿主网络上docker0接口的网关ip 172.17.0.1 。
不过Docker网络还有另一个部分配置才能允许建立连接:防火墙规则和NAT配置,这些配置允许Docker在宿主网络和容器间路由。如下是宿主机上的IPTables NAT 配置。
root@iZbp13z6cxj72rb7bxf0smZ:/home/cong/DockerDemo# iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.20.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.18.0.0/16 0.0.0.0/0
MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:6379
MASQUERADE tcp -- 172.17.0.3 172.17.0.3 tcp dpt:6379
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:6379 to:172.17.0.2:6379
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:6380 to:172.17.0.3:6379
注意这里默认容器是无法访问的,从宿主网络与容器通信时,必须明确指定打开的端口,上面的DNAT(即目标NAT)这个规则中,就是把容器里的访问(端口6379)路由到Docker宿主机的6380端口。
既然是内部网络,那们直接通过IP地址访问容器里的redis,查看容器的网络地址,并使用redis-cli 连接,发现是可以使用ip直接与redis容器通信的,如下:
root@iZbp13z6cxj72rb7bxf0smZ:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
70ea150288c3 redis "docker-entrypoint.s…" 6 days ago Up 6 days 0.0.0.0:6380->6379/tcp optimistic_curran
1b25aef5f610 redis "docker-entrypoint.s…" 6 days ago Up 6 days 0.0.0.0:6379->6379/tcp kind_curran
root@iZbp13z6cxj72rb7bxf0smZ:~# docker inspect -f '{{.NetworkSettings.IPAddress}}' 70ea
172.17.0.3
root@iZbp13z6cxj72rb7bxf0smZ:~# redis-cli -h 172.17.0.3
172.17.0.3:6379> get foo
"1"
但这里会有一个问题,如果重启容器的话,Docker会改变容器的IP地址,要在应用程序中对redis容器的IP地址做硬编码是行不通的,这里就引出了Doker Networking.
Doker Networking
它允许用户创建自己的网络,容器可在这个网络中互相通信。我们先来熟悉一下操作Networking的基本命令。
#创建一个新的网络congnet
root@iZbp13z6cxj72rb7bxf0smZ:~# docker network create congnet
a627c0ddc08828706882f2ae0ae1fb91f6fbb6e339d40c34bf84121dd6ffe97d
#查看所有网络
root@iZbp13z6cxj72rb7bxf0smZ:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
0a5324c3bd83 bridge bridge local
a627c0ddc088 congnet bridge local
fa685cfac4f3 host host local
22f6181538a9 none null local
#检查congnet网络信息
root@iZbp13z6cxj72rb7bxf0smZ:~# docker network inspect congnet
[
{
"Name": "congnet",
"Id": "a627c0ddc08828706882f2ae0ae1fb91f6fbb6e339d40c34bf84121dd6ffe97d",
"Created": "2018-11-12T11:51:58.67769031+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.21.0.0/16",
"Gateway": "172.21.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
#删除网络congnet
root@iZbp13z6cxj72rb7bxf0smZ:~# docker network rm congnet
congnet
接下来,我们需要两个容器,一个是redis容器(已有6379端口),一个是nginx容器(需要创建),并将这两个容器放在同一个网络congnet(需要创建)中,在redis容器里通过nginx容器的容器名mynginx(即docker 内部DNS name) 访问nginx容器。
#查看正在运行的容器
root@iZbp13z6cxj72rb7bxf0smZ:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
70ea150288c3 redis "docker-entrypoint.s…" 6 days ago Up 44 minutes 0.0.0.0:6380->6379/tcp optimistic_curran
1b25aef5f610 redis "docker-entrypoint.s…" 6 days ago Up 6 days 0.0.0.0:6379->6379/tcp kind_curran
#创建网络congnet
root@iZbp13z6cxj72rb7bxf0smZ:~# docker network create congnet
c4f47431e5b1e22c1901bdcb91d2de4d53aa7347d0ce496dd0b17d11b6aaccf8
#创建nginx容器,名为mynginx,并加入到网络congnet中
root@iZbp13z6cxj72rb7bxf0smZ:~# docker run -itd -p 80:80 --name mynginx --net congnet nginx
78761e99c06f6a4892e78c78d2a2eeeca4957ae7c3acade2ce42668d4e1b2e22
#将已存在的redis容器加入到网络congnet中
root@iZbp13z6cxj72rb7bxf0smZ:~# docker network connect congnet 1b25
#检查网络,发现网络中的Containers中有两个,nginx容器的IP为172.24.0.2,redis容器IP为172.24.0.3
root@iZbp13z6cxj72rb7bxf0smZ:~# docker network inspect congnet
[
{
"Name": "congnet",
"Id": "c4f47431e5b1e22c1901bdcb91d2de4d53aa7347d0ce496dd0b17d11b6aaccf8",
"Created": "2018-11-12T12:24:11.2083913+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.24.0.0/16",
"Gateway": "172.24.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"1b25aef5f610668a210e5bfb92c0cd9bf3cf05260e8d3376a676d509463fe4f2": {
"Name": "kind_curran",
"EndpointID": "9bc2e6be3022f43ea344dcee0ff03111796cb089ccd18ef5710990dad8efc131",
"MacAddress": "02:42:ac:18:00:03",
"IPv4Address": "172.24.0.3/16",
"IPv6Address": ""
},
"78761e99c06f6a4892e78c78d2a2eeeca4957ae7c3acade2ce42668d4e1b2e22": {
"Name": "mynginx",
"EndpointID": "3059ee174de65d5cb5ac3be60f1d27afcceb675ac933f7949564c869e656a9c3",
"MacAddress": "02:42:ac:18:00:02",
"IPv4Address": "172.24.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
#进入到redis容器中,查看hosts,并没有mynginx
root@iZbp13z6cxj72rb7bxf0smZ:~# docker exec -it 1b25 bash
root@1b25aef5f610:/data# 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.2 1b25aef5f610
172.24.0.3 1b25aef5f610
#安装curl
root@1b25aef5f610:/data# apt-get update -y && apt-get install curl -yqq
...
done.
#重点:通过容器名访问nginx容器
root@1b25aef5f610:/data# curl mynginx
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
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>
#安装ping
root@1b25aef5f610:/data# apt-get install iputils-ping -y
...
#通过容器名也是可以ping通的
root@1b25aef5f610:/data# ping mynginx
PING mynginx (172.24.0.2) 56(84) bytes of data.
64 bytes from mynginx.congnet (172.24.0.2): icmp_seq=1 ttl=64 time=0.065 ms
64 bytes from mynginx.congnet (172.24.0.2): icmp_seq=2 ttl=64 time=0.074 ms
--- mynginx ping statistics ---
9 packets transmitted, 9 received, 0% packet loss, time 7998ms
rtt min/avg/max/mdev = 0.055/0.078/0.091/0.011 ms
#回头进入nginx容器,看看里面的hostname和hosts
root@iZbp13z6cxj72rb7bxf0smZ:~# docker exec -it mynginx bash
#这里可以看出,容器的ID就是容器的主机名
root@78761e99c06f:/# cat /etc/hostname
78761e99c06f
# hosts中也没有mynginx
root@78761e99c06f:/# 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.24.0.2 78761e99c06f
运行完上面的命令后,发现在同一网络中的两个容器,它们之间是可以直接使用容器名进行通信的,但容器名并不保存在/etc/hosts中,容器名直接被当作了网络内部的域名。
到这里,Docker的网络就介绍到这里,演示完成后,我们需要断开网络,可以使用如下命令。
root@iZbp13z6cxj72rb7bxf0smZ:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
78761e99c06f nginx "nginx -g 'daemon of…" 41 minutes ago Up 41 minutes 0.0.0.0:80->80/tcp mynginx
1b25aef5f610 redis "docker-entrypoint.s…" 6 days ago Up 6 days 0.0.0.0:6379->6379/tcp kind_curran
root@iZbp13z6cxj72rb7bxf0smZ:~# docker network disconnect congnet mynginx
root@iZbp13z6cxj72rb7bxf0smZ:~# docker network disconnect congnet 1b25
root@iZbp13z6cxj72rb7bxf0smZ:~# docker network inspect congnet
[
{
"Name": "congnet",
"Id": "c4f47431e5b1e22c1901bdcb91d2de4d53aa7347d0ce496dd0b17d11b6aaccf8",
"Created": "2018-11-12T12:24:11.2083913+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.24.0.0/16",
"Gateway": "172.24.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
相关链接
https://docs.docker.com/network/
https://docs.docker.com/v17.09/engine/userguide/networking/configure-dns/
总结
“桥接”和“NAT”的区别:使用NAT模式可以实现在虚拟系统里访问互联网,虚拟系统无法和本局域网中的其他真实主机进行通讯。而使用桥接模式下虚拟出来的操作系统就像是局域网中的一台独立的主机,它可以访问网内任何一台机器。所以Docker0是属于桥接模式。
博主生活艰难,支付宝扫如下二维码救助,十块八块都行。
更多推荐
所有评论(0)