玩转Docker-------第四部------------(原生网络&自定义网络、容器通信)
1.Docker原生网络
安装Docker时,它会自动创建三个网络,bridge(创建容器默认连接到此网络)、 none 和host。
docker network ls
docker网络模式 | 功能 |
Host | 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。 |
Container(Joined) | 创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围。 |
None | 该模式关闭了容器的网络功能。 |
Bridge | 此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。 |
运行容器的时候指定网络模式:
模式 参数
host模式 使用 --net=host(–network host) 指定。
none模式 使用 --net=none 指定。
bridge模式 使用 --net=bridge 指定,默认设置。
container模式 使用 --net=container:NAME_or_ID 指定。
bridge网桥模式
bridge模式是Docker默认的网络设置,当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥。
docker安装时会创建一个名为docker0的linux bridge,新建的容器会自动桥接到这个接口
[root@server1 ~]# docker run -d --name nginx nginx
998e60bd9dcd844c256d2889dfbc31e5cb7b7a34cfbd9298340b57d65142ac9c
[root@server1 ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02422aa432f0 no veth3c75f87
可以发现,当挂起一个容器时,就会出现相对应的网桥接口:
docker inspect nginx
这时,我们的容器就通过docker0网桥与宿主机建立通信了:
ping 172.17.0.3
curl 172.17.0.3
桥接模式下容器没有公共 ip,只有宿主机可以访问,外部主机不可见,容器要访问外网必须通过宿主机的 NAT 规则,所以我们如果要想通过访问本机的80端口去访问容器,就需要做端口映射,将容器的80映射到server1主机上的80端口才行。而且桥接模式下容器是没有对外的ip的,在企业中我们的server1主机ip应该是能进入到公网的IP,所以我们打开ip_forward的功能,让容器可以通过server1访问到外网。
不仅如此,只要宿主机做了NAT,我们同时在容器中做好dns解析,就能与外网通信
这样在容器开启的时候,就会生成网络对,相当于一根网线,连接容器与docker0,docker0在通过内核路由功能转发到我们的真实网卡上面,然后在出去,访问到公网。
host模式
host模式下容器会和宿主机共享网络。
相当于使用了Vmware中的桥接模式,与宿主机在同一个网络中,但没有独立IP地址。
一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
host模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。
做此过程之前删除上一步骤中运行的所有容器
netstat -antlp #先查看宿主机的端口情况
docker run -d --name nginx --network host nginx
netstat -antlp #再次查看宿主机的端口情况(80端口打开了)
brctl show
我们可以看到它并不像bridge模式那样,没有在docker0下桥接接口,而是与宿主机共用一个网络
用外部主机访问,可以看到nginx默认发布页面
curl 192.168.43.71
docker run -it --rm --network host busybox
ip addr
可以看到与宿主机的网络一模一样
这时候,如果此时我们再开一个nginx:
docker run -d --name nginx2 --network host nginx
docker ps #开启成功
docker ps #其中一个nginx消失
docker ps -a #可以看到被停掉的nginx
这是因为两个nginx的端口和ip冲突了
none模式
none模式是指禁用网络功能,只有lo接口,在容器创建时使用–network none
none模式关闭了容器的网络功能,但是在以下两种情况下是有用的:
- 容器并不需要网络(例如只需要写磁盘卷的批处理任务)。
- overlay:在docker1.7代码进行了重构,单独把网络部分独立出来编写,所以在docker1.8新加入的一个overlay网络模式。
docker run -it --rm --network none busybox
ip addr
可以看到只有lo接口
查看到网络是不可以使用的
2.Docker自定义网络
原生网络是docker安装后自动创建的,真正需要配置的是自定义网络。
Docker 提供三种 user-defined 网络驱动:bridge, overlay 和 macvlan。
bridge驱动类似默认的bridge网络模式,但增加了一些新的功能
overlay 和 macvlan 用于创建跨主机的网络
bridge网络驱动
[root@server1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
f905d0cfd038 bridge bridge local
5dc66fa4665c host host local
304f37a81f85 none null local
[root@server1 ~]# docker network create -d bridge my_net1 # 创建自定义桥接
c73546940ab666d4c641eed79df76d8963f133a578b570209f1a6e922ce90efe
[root@server1 ~]# docker network ls #可以看出我们的自定义网络加进去了
NETWORK ID NAME DRIVER SCOPE
f905d0cfd038 bridge bridge local
5dc66fa4665c host host local
c73546940ab6 my_net1 bridge local
304f37a81f85 none null local
[root@server1 ~]# docker run -it --name vm1 --network my_net1 ubuntu # 用自定义网络运行
root@9616cfb055e9:/# ip a
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
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
## 发现网段变成了172.18网段了,说明自定义网络的时候网段是递增的,和开启容器时ip递增一样
## 我们还可以自定义网关和网段:
[root@server1 ~]# docker network create -d bridge --subnet 172.22.0.0/24 --gateway 172.22.0.1 my_net2
f5f5655d83dbd39958379c8c90d75025748a18787eb5de120f64224e83eee3d3
[root@server1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
f905d0cfd038 bridge bridge local
5dc66fa4665c host host local
c73546940ab6 my_net1 bridge local
f5f5655d83db my_net2 bridge local
304f37a81f85 none null local
[root@server1 ~]# docker run -it --name vm2 --network my_net2 ubuntu
root@2da970629875:/# ip a
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
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:16:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.22.0.2/24 brd 172.22.0.255 scope global eth0 #确实变成了172.22网段
valid_lft forever preferred_lft forever
##自定义IP
[root@server1 ~]# docker run -it --name vm3 --network my_net2 --ip 172.22.0.100 ubuntu
root@71d882cabbc7:/# ip a
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
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:16:00:64 brd ff:ff:ff:ff:ff:ff
inet 172.22.0.100/24 brd 172.22.0.255 scope global eth0
valid_lft forever preferred_lft forever
root@71d882cabbc7:/# ping 172.22.0.2 #可以ping同刚才那个VM2容器,因为桥接在同一网络上
PING 172.22.0.2 (172.22.0.2) 56(84) bytes of data.
64 bytes from 172.22.0.2: icmp_seq=1 ttl=64 time=0.207 ms
64 bytes from 172.22.0.2: icmp_seq=2 ttl=64 time=0.086 ms
64 bytes from 172.22.0.2: icmp_seq=3 ttl=64 time=0.158 ms
^C
--- 172.22.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.086/0.150/0.207/0.050 ms
[root@server1 ~]# brctl show
bridge name bridge id STP enabled interfaces
br-c73546940ab6 8000.02421577f327 no veth734b2f2 #my_net1只桥接了一个网卡
br-f5f5655d83db 8000.024214c10f10 no vethee1d0ed
vethfdca8a6 # my_net2桥接了两个
docker0 8000.02422aa432f0 no
# 桥接在不同网桥上的容器,彼此之间不能通信#
[root@server1 ~]# iptables -S
-A DOCKER-ISOLATION-STAGE-2 -o br-f5f5655d83db -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o br-c73546940ab6 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
可以看到三个网桥对互相之间的数据都是DROP
都是互相丢弃的所以不同网桥之间是无法通信的。
那我们如何让不同网桥之间进行通信呢?
[root@server1 ~]# docker network connect my_net2 vm1
[root@server1 ~]# docker attach vm1
root@9616cfb055e9:/# ip a
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
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
16: eth1@if17: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:16:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.22.0.3/24 brd 172.22.0.255 scope global eth1 # 多了172.22的网卡
valid_lft forever preferred_lft forever
root@9616cfb055e9:/# ping vm1
PING vm1 (172.18.0.2) 56(84) bytes of data.
64 bytes from 9616cfb055e9 (172.18.0.2): icmp_seq=1 ttl=64 time=0.028 ms
64 bytes from 9616cfb055e9 (172.18.0.2): icmp_seq=2 ttl=64 time=0.033 ms
64 bytes from 9616cfb055e9 (172.18.0.2): icmp_seq=3 ttl=64 time=0.032 ms
^C
--- vm1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.028/0.031/0.033/0.002 ms
root@9616cfb055e9:/# ping vm2
PING vm2 (172.22.0.2) 56(84) bytes of data.
64 bytes from vm2.my_net2 (172.22.0.2): icmp_seq=1 ttl=64 time=0.066 ms
64 bytes from vm2.my_net2 (172.22.0.2): icmp_seq=2 ttl=64 time=0.050 ms
64 bytes from vm2.my_net2 (172.22.0.2): icmp_seq=3 ttl=64 time=0.048 ms
^C64 bytes from vm2.my_net2 (172.22.0.2): icmp_seq=4 ttl=64 time=0.048 ms
64 bytes from vm2.my_net2 (172.22.0.2): icmp_seq=5 ttl=64 time=0.050 ms
64 bytes from vm2.my_net2 (172.22.0.2): icmp_seq=6 ttl=64 time=0.140 ms
64 bytes from vm2.my_net2 (172.22.0.2): icmp_seq=7 ttl=64 time=0.066 ms
^C64 bytes from vm2.my_net2 (172.22.0.2): icmp_seq=8 ttl=64 time=0.051 ms
64 bytes from vm2.my_net2 (172.22.0.2): icmp_seq=9 ttl=64 time=0.049 ms
^C
--- vm2 ping statistics ---
9 packets transmitted, 9 received, 0% packet loss, time 8001ms
rtt min/avg/max/mdev = 0.048/0.063/0.140/0.028 ms
root@9616cfb055e9:/# ping vm3
PING vm3 (172.22.0.100) 56(84) bytes of data.
64 bytes from vm3.my_net2 (172.22.0.100): icmp_seq=1 ttl=64 time=0.057 ms
^C
--- vm3 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.057/0.057/0.057/0.000 ms
#我们发现可以ping通#
root@9616cfb055e9:/# 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.18.0.2 9616cfb055e9
172.22.0.3 9616cfb055e9 #这就是dns的自动解析,它自带dns解析
这里我们先介绍bridge,overlay 和macvlan在我们学习了容器之间的通信之后在进行学习
3.Docker容器间通信
通过上面的实验我们可以知道,使用同一自定义网络(即同一网段)运行的容器是可以相互通信的,那么如果两个容器不在同一网段,但我们也想让它通信,怎么办?
方法1:使用内嵌的 DNS server 通过容器名去自动解析容器的IP地址
[root@server1 ~]# docker run -it --name vm1 --network my_net1 ubuntu
root@21692ff10442:/# [root@server1 ~]# docker run -it --name vm2 --network my_net1 ubuntu
root@c9c781f485c2:/# ping vm1
PING vm1 (172.18.0.2) 56(84) bytes of data.
64 bytes from vm1.my_net1 (172.18.0.2): icmp_seq=1 ttl=64 time=0.061 ms
64 bytes from vm1.my_net1 (172.18.0.2): icmp_seq=2 ttl=64 time=0.051 ms
^C
--- vm1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.051/0.056/0.061/0.005 ms
root@c9c781f485c2:/# ip a
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
20: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
我们可以看到这里多出了一个网卡eth0@if21,ip为172.18.0.3
方法2:joined(类似于默认的 host 模式,容器之间共享网络)
container模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
[root@server1 ~]# docker run -it --name vm1 ubuntu
root@9ff4ffcba7c0:/# ip a
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
22: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
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
root@9ff4ffcba7c0:/# [root@server1 ~]# docker run -it --name vm2 --network container:vm1 ubuntu
root@9ff4ffcba7c0:/# ip a
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
22: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
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
可以发现,和vm1网络完全一致,使用相同的网络name space:
这样做的用处在于,两个容器之间可以使用 localhost(回环接口)进行快速通信,适用于 web 服务器和应用服务器。
方法3:使用–link连接容器
[root@server1 ~]# docker run -d nginx #不加--name时系统会自动分配一个容器名
105b5c0c65f16c22681f6f193411ba459b12af7a225021cb8f23282cba5d818d
[root@server1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
105b5c0c65f1 nginx "nginx -g 'daemon of…" 15 seconds ago Up 14 seconds 80/tcp lucid_aryabhata
[root@server1 ~]# docker run -it --name vm1 --link lucid_aryabhata:web ubuntu
###使用--link连接这个容器,并为它起个别名web,因为系统给的太难记了###
root@6c5b91bc7b5b:/# ping web
PING web (172.17.0.2) 56(84) bytes of data.
64 bytes from web (172.17.0.2): icmp_seq=1 ttl=64 time=0.070 ms
64 bytes from web (172.17.0.2): icmp_seq=2 ttl=64 time=0.106 ms
^C #发现是可以通信的,这是因为我们的容器做了两件事
--- web ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.070/0.088/0.106/0.018 ms
root@6c5b91bc7b5b:/# 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 web 105b5c0c65f1 lucid_aryabhata
172.17.0.3 6c5b91bc7b5b
root@6c5b91bc7b5b:/# env # 第二设置了环境变量
...
WEB_NAME=/vm1/web
WEB_PORT_80_TCP_PROTO=tcp
WEB_PORT_80_TCP_ADDR=172.17.0.2
...
[root@server1 ~]# docker stop lucid_aryabhata #关闭刚开始的nginx容器
lucid_aryabhata
[root@server1 ~]# docker run -d nginx #重新开一个
db52f4e7087049f0c0fba22cc2d9b909eb52a70cb266fb6940308f72a668fdf2
[root@server1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
db52f4e70870 nginx "nginx -g 'daemon of…" 2 minutes ago Up 2 minutes 80/tcp happy_ptolemy
[root@server1 ~]# docker inspect happy_ptolemy
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2", #它的ip地址也是172.17.0.2
"IPPrefixLen": 16,
[root@server1 ~]# docker start lucid_aryabhata #再打开刚才第一个nginx容器
lucid_aryabhata
[root@server1 ~]# docker inspect lucid_aryabhata
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3", # 它的ip地址变成了0.3了
#####说明容器关闭后其获得的ip地址会被释放#####
[root@server1 ~]# docker start lucid_aryabhata
lucid_aryabhata
[root@server1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
db52f4e70870 nginx "nginx -g 'daemon of…" 13 minutes ago Up 13 minutes 80/tcp happy_ptolemy
105b5c0c65f1 nginx "nginx -g 'daemon of…" 29 minutes ago Up 8 seconds 80/tcp lucid_aryabhata
[root@server1 ~]# docker start vm1
vm1
[root@server1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
db52f4e70870 nginx "nginx -g 'daemon of…" 13 minutes ago Up 13 minutes 80/tcp happy_ptolemy
6c5b91bc7b5b ubuntu "/bin/bash" 29 minutes ago Up 5 seconds vm1
105b5c0c65f1 nginx "nginx -g 'daemon of…" 30 minutes ago Up 25 seconds 80/tcp lucid_aryabhata
[root@server1 ~]# docker attach vm1
root@6c5b91bc7b5b:/#
root@6c5b91bc7b5b:/#
root@6c5b91bc7b5b:/#
root@6c5b91bc7b5b:/# 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 web 105b5c0c65f1 lucid_aryabhata #VM1上的本地解析也变了
172.17.0.4 6c5b91bc7b5b
4.容器是如何访问外网的?
容器与外网通信是通过iptables的SNAT实现的:
容器访问外网是通过 iptables 的 SNAT 实现的,容器和 docker0 是桥接的方式,docker0 是
容器的网关,到达 docker0 后,通过 linux 内核的路由功能(ip_forward),然后防火墙会做
伪装,也就是 SNAT,然后通过物理网卡接口到外网。
[root@server1 ~]# iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N DOCKER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.22.0.0/24 ! -o br-f5f5655d83db -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-c73546940ab6 -j MASQUERADE #为容器的ip做了地址伪装
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i br-f5f5655d83db -j RETURN
-A DOCKER -i br-c73546940ab6 -j RETURN
-A DOCKER -i docker0 -j RETURN
在物理机开启ip_forward让server1可以上网
[root@server0 ~]# sysctl -a |grep ip_forward
net.ipv4.ip_forward = 1
net.ipv4.ip_forward_use_pmtu = 0
# 测试:
[root@server1 ~]# ping baidu.com
PING baidu.com (220.181.38.148) 56(84) bytes of data.
64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=1 ttl=51 time=61.6 ms
64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=2 ttl=51 time=47.0 ms
^C
--- baidu.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 47.045/54.362/61.679/7.317 ms
#然后我们在进入ubuntu容器测试:
[root@server1 ~]# docker start vm1
vm1
[root@server1 ~]# docker attach vm1
root@6c5b91bc7b5b:/#
root@6c5b91bc7b5b:/# ping baidu.com
PING baidu.com (39.156.69.79) 56(84) bytes of data.
64 bytes from 39.156.69.79: icmp_seq=1 ttl=47 time=58.3 ms
64 bytes from 39.156.69.79: icmp_seq=2 ttl=47 time=48.7 ms
^C
--- baidu.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 48.767/53.583/58.399/4.816 ms
root@6c5b91bc7b5b:/#
# 这样容器就也可以上网了。
5.外网是如何访问容器的?
[root@server1 ~]# docker run -d --name vm1 -p 80:80 nginx 做端口映射(冒号后的是容器内部的端口)
d995bd5ba8fe0008c288081d958fcb0bab05c9532232dc6c1986c7be2e3eee85
[root@server1 ~]# docker port vm1 ##查看容器端口映射情况
80/tcp -> 0.0.0.0:80
[root@server1 ~]# iptables -t nat -S ##查看防火墙策略
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.2:80
访问流程:
外部主机访问时--> 访问到宿主机的 eth0(192.168.43.71:80)--> 然后火墙做了DNAT转换 --> 访问172.17.0.2(容器地
址)
测试:访问192.168.43.71
docker-proxy 打开了80端口,我们每打开一个容器,都会开启一个docker-proxy 进程
注意:
宿主机访问本机容器使用 iptables 的 DNAT,外部主机访问容器或者容器之间访问是
docker-proxy 实现的。
流程:
外部主机 --> 宿主机 eth0 --> docker-proxy(外部访问容器时通过 docker-proxy 处理数据
包,不是防火墙) --> docker0(172.17.0.1) --> 容器
6.Docker跨主机的容器网络
docker跨主机的网络方式:
docker原生的overlay和macvlan
第三方的flannel、weave、calico
CNM(container network model)模型
三类组件 | 作用 |
Sandbox | 容器网络栈(namespace |
Endpoint | 将sandbox接入network(veth) |
Network | 包含一组endpoint,同一network的endpoint可以通信 |
使用macvlan实现Docker容器跨主机网络
macvlan特点:
- 使用linux内核提供的一种网卡虚拟化技术
- 性能好:因为无需使用桥接,直接使用宿主机物理网口。
清理掉之前的容器,删除自定义网络:
[root@server1 ~]# docker network prune
WARNING! This will remove all networks not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Networks:
my_net1
my_net2
[root@server1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
baa31764f59f nginx "nginx -g 'daemon of…" 8 minutes ago Up 8 minutes 0.0.0.0:8080->80/tcp vm2
d995bd5ba8fe nginx "nginx -g 'daemon of…" 19 minutes ago Up 19 minutes 0.0.0.0:80->80/tcp vm1
[root@server1 ~]# docker rm -f vm1
vm1
[root@server1 ~]# docker rm -f vm2
vm2
[root@server1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
给两台主机都添加上一块新的虚拟网卡。
[root@server1 ~]# ip a
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 pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:a5:b1:7f brd ff:ff:ff:ff:ff:ff
inet 192.168.43.71/24 brd 192.168.43.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 240e:bf:d429:dd57:20c:29ff:fea5:b17f/64 scope global mngtmpaddr dynamic
valid_lft 2237sec preferred_lft 2237sec
inet6 2409:8a70:f8a1:ae10:20c:29ff:fea5:b17f/64 scope global mngtmpaddr dynamic
valid_lft 259144sec preferred_lft 172744sec
inet6 fe80::20c:29ff:fea5:b17f/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:2a:a4:32:f0 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:2aff:fea4:32f0/64 scope link
valid_lft forever preferred_lft forever
44: eth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 00:0c:29:a5:b1:89 brd ff:ff:ff:ff:ff:ff
添加之后,我们可以看到现在有ens0和eth1这两块网卡
激活这两块网卡,并开启混杂模式:
server1:
[root@server1 ~]# ip link set up eth1
[root@server1 ~]# ip link set eth1 promisc on
server2:
[root@server2 ~]# ip link set up eth1
[root@server2 ~]# ip link set eth1 promisc on
[root@server2 ~]# ip a
能看到PROMISC 就会说明开启了
4: eth1: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP>
server1和server2上创建macvlan 网络模型:
[root@server1 ~]# docker network create -d macvlan --subnet 172.20.0.0/24 --gateway 172.20.0.1 -o parent=eth0 macvlan1
90bee86d976bb3293faf2531d7c71e82666f540cafed47ec6d3f756480cbb1fc
[root@server2 ~]# docker network create -d macvlan --subnet 172.20.0.0/24 --gateway 172.20.0.1 -o parent=eth0 macvlan1
499d58aa9c967b3559302a57a664d1abde6c0afef74d12c7cc70a8f9eba3b0ee
[root@server1 ~]# docker run -it --name vm1 --network macvlan1 --ip 172.20.0.11 ubuntu
[root@server2 ~]# docker run -it --name vm2 --network macvlan1 --ip 172.20.0.12 ubuntu
#运行容器,macvlan 模型这里必须手动指定 ip 地址,如果不指定会自动分配,单调递增,两台主机可能会冲突。
在server1上:
root@1046c09fdcab:/# ping 172.20.0.12
PING 172.20.0.12 (172.20.0.12) 56(84) bytes of data.
64 bytes from 172.20.0.12: icmp_seq=1 ttl=64 time=0.956 ms
64 bytes from 172.20.0.12: icmp_seq=2 ttl=64 time=0.548 ms
在server2上:
root@e22da2aae025:/# ping 172.20.0.11
PING 172.20.0.11 (172.20.0.11) 56(84) bytes of data.
64 bytes from 172.20.0.11: icmp_seq=1 ttl=64 time=0.458 ms
64 bytes from 172.20.0.11: icmp_seq=2 ttl=64 time=0.558 ms
#########两台主机就可以互相通信了##########
[root@server1 rpms]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02422aa432f0 no
[root@server2 ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242c65fa427 no
并且两台主机的网络都没有桥接到docker0上。而是直接占用了物理网卡。无需 NAT 或端口映射
但是,我们发现这样就出现了一个问题,我们每次创建一个网络就需要一块新的网卡,这样显然是不现实的。
解决方法:
macvlan会独占网卡,但是可以使用vlan的子接口实现多macvlan网络,最多可以将物理二层网络划分成4094各逻辑网络,且彼此隔离。
所以:macvlan网络在物理网络二层是隔离的,无法直接通信,但是可以在三层上(即容器内部)通过网关将macvlan网络连通起来。
使用 vlan 子接口实现多 macvlan 网络
vlan 可以将物理二层网络划分为 4094 个逻辑网络,彼此隔离,取值为 1~4094,这样就极大的提升了网卡的复用性
[root@server1 rpms]# docker network create -d macvlan --subnet 172.21.0.0/24 --gateway 172.21.0.1 -o parent=eth0.1 macvlan2
b908e524d6ae3471366ef0351b7fcfb6d8213fcbd1258d065e6bb7324329a541
[root@server1 rpms]# docker network ls
NETWORK ID NAME DRIVER SCOPE
f905d0cfd038 bridge bridge local
5dc66fa4665c host host local
90bee86d976b macvlan1 macvlan local
b908e524d6ae macvlan2 macvlan local
304f37a81f85 none null local
[root@server2 ~]# docker network create -d macvlan --subnet 172.21.0.0/24 --gateway 172.21.0.1 -o parent=eth0.1 macvlan2
6731f6bfad762ebe68028166a566a59102d6a4a34017c00408fa4aa5a0a7da70
[root@server2 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
353e8449c00f bridge bridge local
0c145a0ac5db host host local
499d58aa9c96 macvlan1 macvlan local
6731f6bfad76 macvlan2 macvlan local
7369a58f2403 none null local
server1和server2:
6: eth0.1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 00:0c:29:05:08:07 brd ff:ff:ff:ff:ff:ff
inet6 fe80::20c:29ff:fe05:807/64 scope link
valid_lft forever preferred_lft forever
[root@server1 ~]# docker run -it --name vm3 --network macvlan2 --ip 172.21.0.11 ubuntu
[root@server2 ~]# docker run -it --name vm4 --network macvlan2 --ip 172.21.0.12 ubuntu
同一个macvlan的容器才可以互相通信
注意:macvlan 网络在二层是隔离的,所以不同 macvlan 容器不能通信,可以在三层通过网关连接(加路由),docker 本身不做任何限制,像传统 vlan 那样管理即可。网络选型时如果对网络比较熟悉,选 macvlan较好,因为只需要把网络设备调整好,docker 方面基本不用做什么调整。