实验环境:
server1 172.25.254.1
server2 172.25.254.2
Docker的三种原生网络
简介
docker 在安装好时会有 3 种网络模型:bridge(默认),host,none
[root@server1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
672f5543b2d3 bridge bridge local
e181fb352854 host host local
7261b2daa122 none null local- bridge模式:
bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将并将一个主机上的Docker容器连接到一个虚拟网桥上。当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配IP了,Docker会从RFC1918所定义的私有IP网段中,选择一个和宿主机不同的IP地址和子网分配给docker0,连接到docker0的容器就从这个子网中选择一个未占用的IP使用。如一般Docker会使用172.17.0.0/16这个网段,并将172.17.42.1/16分配给docker0网桥(在主机上使用ifconfig命令是可以看到docker0的,可以认为它是网桥的管理端口,在宿主机上作为一块虚拟网卡使用)。 - host模式:
如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace,共享网络。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。 - none模式:
在none模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。这种模式主要用于数据库等重要的服务。就是通过网络无法访问到。
bridge模式
bridge模式是Docker默认的网络设置,当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥.
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:1a:46:fc:82 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:1aff:fe46:fc82/64 scope link
valid_lft forever preferred_lft forever[root@server1 ~]# docker run -d --name nginx nginx #开启这个容器
7dbb267f5ecd7e0e75e2b2dd7372bd15fd8c90cbd76764ea1ccc853dc4a36d68
[root@server1 ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02421a46fc82 no veth27dc0b7 # 这个容器桥接在docker0上。
[root@server1 ~]# docker inspect nginx
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2", #开启容器后它自动获取了这个ip地址
[root@server1 ~]# curl 172.17.0.2 #尝试访问
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
# 是可以访问到的,这里nginx开放的是80端口,它开在容器中,在我们的主机上时没有开放的:
[root@server1 ~]# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2991/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 3101/master
tcp6 0 0 :::22 :::* LISTEN 2991/sshd
tcp6 0 0 ::1:25 :::* LISTEN 3101/master桥接模式下容器没有公共 ip,只有宿主机可以访问,外部主机不可见,容器要访问外网必须通过宿主机的 NAT 规则,所以我们如果要想通过访问本机的80端口去访问容器,就需要做端口映射,将容器的80映射到server1主机上的80端口才行。而且桥接模式下容器是没有对外的ip的,在企业中我们的server1主机ip应该是能进入到公网的IP,所以我们打开ip_forward的功能,让容器可以通过server1访问到外网。
物理主机上开启内核路由功能,让server1可以上网。
[root@rhel7host network-scripts]# sysctl -a |grep ip_forward
net.ipv4.ip_forward = 1
net.ipv4.ip_forward_use_pmtu = 0这样在容器开启的时候,就会生成网络对,相当于一根网线,连接容器与docker0,docker0在通过内核路由功能转发到我们的真实网卡上面,然后在出去,访问到公网。
Host模式
host模式下容器会和宿主机共享网络。
[root@server1 ~]# docker run -it --name vm1 --network host ubuntu
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: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 52:54:00:e4:9b:44 brd ff:ff:ff:ff:ff:ff
inet 172.25.254.1/24 brd 172.25.254.255 scope global ens3
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fee4:9b44/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:1a:46:fc:82 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:1aff:fe46:fc82/64 scope link
valid_lft forever preferred_lft forever #我们可以看到的信息和宿主机是一样的
root@server1:/# exit # 退出容器
[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: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 52:54:00:e4:9b:44 brd ff:ff:ff:ff:ff:ff
inet 172.25.254.1/24 brd 172.25.254.255 scope global ens3
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fee4:9b44/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:1a:46:fc:82 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:1aff:fe46:fc82/64 scope link
valid_lft forever preferred_lft forever这样我们就可以直接通过宿主机的网络来和外界通信, 但是如果开启了成百上千个容器的话,就容易产生冲突,比如端口的冲突,也缺失了容器的隔离性。
none模式
[root@server1 ~]# docker run -it --name vm2 --network none ubuntu
root@cac3293e4f47:/# 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 #只有一个回环接口
root@cac3293e4f47:/#自定义Docker网络
三种网络驱动:1.bridge(自定义的bridge自带DNS解析功能。直接使用 vm1 vm2 这种容器名去访问,不一定要使用 ip。)
2.overlay
3.macvlan,
后面两种用于跨主机的网络,也就是集群等
bridge网络驱动
# 创建自定义桥接
[root@server1 ~]# docker network create -d bridge my_net1
043a6d4b3291e5ddb67b1814f22529f6d2242ed2577dd8cc0775f3e96cb1a4a3
# 查看,可以看出我们的自定义网络加进去了
[root@server1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
672f5543b2d3 bridge bridge local
e181fb352854 host host local
043a6d4b3291 my_net1 bridge local
7261b2daa122 none null local
# 用自定义网络运行
[root@server1 ~]# docker run -it --name vm1 --network my_net1 ubuntu
WARNING: IPv4 forwarding is disabled. Networking will not work.
root@ce31d9f09ee8:/# 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
11: eth0@if12: <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
3779005fd53e06b5d9238c1568381f5e362f210977c7a785581f799bd4c29ae4
[root@server1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
672f5543b2d3 bridge bridge local
e181fb352854 host host local
043a6d4b3291 my_net1 bridge local
3779005fd53e my_net2 bridge local
7261b2daa122 none null local
[root@server1 ~]# docker run -it --name vm2 --network my_net2 ubuntu
WARNING: IPv4 forwarding is disabled. Networking will not work.
root@93c5a4ebb2bf:/# 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: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@221518b54527:/# 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
18: eth0@if19: <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@221518b54527:/#
root@221518b54527:/# 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.051 ms
。。。
[root@server1 ~]# brctl show
bridge name bridge id STP enabled interfaces
br-043a6d4b3291 8000.02423f6daeee no ## my_net1只桥接了一个网卡
br-3779005fd53e 8000.02425938ed4e no veth688058b
veth834b548 ## my_net2桥接了两个
docker0 8000.02421a46fc82 no
# 桥接在不同网桥上的容器,彼此之间不能通信:
[root@server1 ~]# iptables -S
-A DOCKER-ISOLATION-STAGE-2 -o br-3779005fd53e -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o br-043a6d4b3291 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
可以看到三个网桥对互相之间的数据都是DROP
都是互相丢弃的所以不同网桥之间是无法通信的。那我们如何让不同网桥之间进行通信:
## 给VM1添加新网络接口并桥接在my_net2上
[root@server1 ~]# docker network connect my_net2 vm1
[root@server1 ~]# docker attach vm1
root@ce31d9f09ee8:/# 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: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
24: eth1@if25: <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@ce31d9f09ee8:/# ping vm1
PING vm1 (172.18.0.2) 56(84) bytes of data.
64 bytes from ce31d9f09ee8 (172.18.0.2): icmp_seq=1 ttl=64 time=0.017 ms
root@ce31d9f09ee8:/# 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.050 ms
root@ce31d9f09ee8:/# 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.051 ms
## 这次就都能ping通了
root@ce31d9f09ee8:/# cat /etc/host
cat: /etc/host: No such file or directory
root@ce31d9f09ee8:/# 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 ce31d9f09ee8
172.22.0.3 ce31d9f09ee8 ##这就是dns的自动解析overlay 和macvlan在我们学习了容器之间的通信之后在进行学习。
容器之间的通信
使用内嵌的 DNS server.通过容器名去自动解析容器的IP地址。
[root@server1 ~]# docker run -it --name vm1 --network my_net1 ubuntu
root@eaa57a7f3c63:/#
[root@server1 ~]# docker run -it --name vm2 --network my_net1 ubuntu
root@6b12261d3b90:/# 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.053 ms使用joined 方式(类似于默认的 host 模式,容器之间共享网络)
[root@server1 ~]# docker run -it --name vm1 ubuntu
root@5ac2f9bb5edf:/# ip a
1: lo:
34: eth0@if35: <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@5ac2f9bb5edf:/
# [root@server1 ~]# docker run -it --name vm2 --network container:vm1 ubuntu
# 使用container的方式和vm1共享网络
root@5ac2f9bb5edf:/# ip a
1: lo:
34: eth0@if35: <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 ## 发现两个容器的网络信息是一样的这样做的用处在于,两个容器之间可以使用 localhost(回环接口)进行快速通信,适用于 web 服务器和应用服务器。
使用–link 来链接两个容器
[root@server1 ~]# docker run -d nginx # 不加--name时系统会自动分配一个容器名
doa8cc285f34e968bdc01e96d18591befabbd7829af4137d34e014fbcd35076a4f
c [root@server1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a8cc285f34e9 nginx "nginx -g 'daemon of…" 3 seconds ago Up 1 second 80/tcp unruffled_montalcini #这就是分配的容器名
[root@server1 ~]# docker run -it --name vm1 --link unruffled_montalcini:web ubuntu
# 使用--link连接这个容器,并为它起个别名web,因为系统给的太难记了
root@b83a76c4062d:/# 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.065 ms
# 发现是可以通信的,这时因为我们的容器做了两件事:
## 第一,写了解析
root@b83a76c4062d:/# 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 a8cc285f34e9 unruffled_montalcini ## 这里
172.17.0.3 b83a76c4062d
## 第二设置了环境变量
root@b83a76c4062d:/# env
。。。
WEB_NAME=/vm1/web
WEB_PORT_80_TCP_PROTO=tcp
WEB_PORT_80_TCP_ADDR=172.17.0.2
[root@server1 ~]# docker stop unruffled_montalcini #关闭刚开始的nginx容器
unruffled_montalcini
[root@server1 ~]# docker run -d nginx #重新开一个
a0fde5256cedce1ca5ed106b1f861518526df2243b3b52dd6214c7aefe057945
[root@server1 ~]# docker inspect tender_blackburn
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2", #它的ip地址也是172.17.0.2
[root@server1 ~]# docker start unruffled_montalcini #再打开第一个nginx容器
unruffled_montalcini
[root@server1 ~]# docker inspect unruffled_montalcini
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.4", # 它的ip地址变成了0.4了
说明容器关闭后其获得的ip地址会被释放。
root@b83a76c4062d:/# 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.4 web a8cc285f34e9 unruffled_montalcini
VM1上的本地解析也变了。容器访问外网(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-3779005fd53e -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-043a6d4b3291 -j MASQUERADE #为容器的ip做了地址伪装
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i br-3779005fd53e -j RETURN
-A DOCKER -i br-043a6d4b3291 -j RETURN
-A DOCKER -i docker0 -j RETURN在物理机开启ip_forward让server1可以上网;
[root@rhel7host ~]# iptables -t nat -I POSTROUTING -s 172.25.254.0/24 -j MASQUERADE
[root@rhel7host ~]# 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=46 time=29.5 ms
## server1可以上网了。
##然后我们在进入ubuntu容器测试:
[root@server1 ~]# docker start vm1
vm1
[root@server1 ~]# docker attach vm1
root@b83a76c4062d:/# ping baidu.com
PING baidu.com (220.181.38.148) 56(84) bytes of data.
64 bytes from 220.181.38.148: icmp_seq=1 ttl=45 time=27.7 ms
## 这样容器就也可以上网了。外网访问容器(端口映射docker-proxy)
[root@server1 ~]# docker run -d --name vm1 -p 80:80 nginx
07edbf644899bdeb49625dbc4a66265295e076965cf1e4316462333afa3d58c3
## 做端口映射(冒号后的是容器内部的端口)
[root@server1 ~]# docker port vm1 ##查看容器端口映射情况
80/tcp -> 0.0.0.0:80 # 容器的80端口对server1主机的80端口
[root@server1 ~]# iptables -t nat -S ##查看防火墙策略
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination
172.17.0.3:80
说明访问的流程时这样的
外部主机访问时--> 访问到宿主机的 eth0(172.25.254.1:80)--> 然后火墙做了DNAT转换 --> 访问172.17.0.3(容器地
址)浏览器访问:

就可以通过外网访问容器了。
[root@server1 ~]# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2991/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 3101/master
tcp6 0 0 :::80 :::* LISTEN 19911/docker-proxy
docker-proxy 打开了80端口,我们每打开一个容器,都会开启一个docker-proxy 进程
[root@server1 ~]# docker run -d --name vm2 -p 8080:80 nginx
dd444d0f4776c60b48001c53afca504e7aa72904e4cdccc6390571a08ecb6c22
[root@server1 ~]# ps ax |grep docker-proxy
19911 ? Sl 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.2 -container-port 80 ## vm1
20440 ? Sl 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8080 -container-ip 172.17.0.3 -container-port 80 ## vm2
20507 pts/1 S+ 0:00 grep --color=auto docker-proxy这里就需要注意:
- 宿主机访问本机容器使用 iptables 的 DNAT,外部主机访问容器或者容器之间访问是
docker-proxy 实现的。
流程:
‘外部主机 --> 宿主机 ens3(我这里是ens3) --> docker-proxy(外部访问容器时通过 docker-proxy 处理数据
包,不是防火墙) --> docker0(172.17.0.1) --> 容器’
跨主机容器网络
解决方案:
- docker 原生:overlay 和 macvlan
- 第三方:flannel、weave、calico
这些解决方案怎么和 docker 集成在一起?
是通过 libnetwork ,docker 容器网络库,其中 CNM(Container Network Model)是它的核心,它对容器网络进行了抽象。
CNM 由 3 部分组成:
- Sandbox:容器的网络栈。包含容器的 interface、路由表和 DNS 等
- Endpoint:作用是将 Sandbox 接入 Network。(生成veth pair,虚拟网络对,相当于网线的两端连接沙盒和network)
- Network:包含一组 Endpoint,同一 Network 的 Endpoint 可以直接通信
我们主要通过macvlan进行演示。
macvlan
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 rm -f vm1
^[[Avm1
[root@server1 ~]# docker rm -f vm2
vm2给两台主机都添加上一块新的虚拟网卡。

[root@server2 ~]# 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: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 52:54:00:68:5c:65 brd ff:ff:ff:ff:ff:ff
inet 172.25.254.2/24 brd 172.25.254.255 scope global ens3
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fe68:5c65/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:af:4a:61:17 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
4: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 52:54:00:8b:37:59 brd ff:ff:ff:ff:ff:ff
## 现在有ens3 和eth0这两块网卡。激活这两块网卡,并开启混杂模式:
[root@server1 ~]# ip link set up eth0
[root@server1 ~]# ip link set eth0 promisc on
[root@server2 ~]# ip link set up eth0
[root@server2 ~]# ip link set eth0 promisc on
[root@server2 ~]# ip a
能看到PROMISC 就会说明开启了
4: eth0: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP>创建macvlan 网络模型;
server1和server2上
[root@server1 ~]# docker network create -d macvlan --subnet 172.20.0.0/24 --gateway 172.20.0.1 -o parent=eth0 macvlan1
[root@server2 ~]# docker network create -d macvlan --subnet 172.20.0.0/24 --gateway 172.20.0.1 -o parent=eth0 macvlan1
-o 指定使用的物理接口 起名为 macvlan1
[root@server1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
672f5543b2d3 bridge bridge local
e181fb352854 host host local
4c5448255098 macvlan1 macvlan local ## 这里
7261b2daa122 none null local[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 地址,如果不指定会自动分配,单调递增,两台主机可能会冲突。
root@a59b31a27751:/# 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.030 ms
64 bytes from 172.20.0.11: icmp_seq=2 ttl=64 time=0.027 ms
root@c3af04bec7fa:/# 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.662 ms
64 bytes from 172.20.0.11: icmp_seq=2 ttl=64 time=0.374 ms
##两台主机就可以互相通信了。
[root@server2 ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242af4a6117 no
[root@server1 ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02421a46fc82 no
##并且两台主机的网络都没有桥接到docker0上。而是直接占用了物理网卡。无需 NAT 或端口映射这样就就出现了一个问题每次创建一个网络就需要一块新的网卡,这是不现实的,
我们可以:
使用 vlan 子接口实现多 macvlan 网络
vlan 可以将物理二层网络划分为 4094 个逻辑网络,彼此隔离,取值为 1~4094,这样就极大的提升了网卡的复用性.
[root@server1 ~]# docker network create -d macvlan --subnet 172.21.0.0/24 --gateway 172.21.0.1 -o parent=eth0.1 macvlan2
[root@server2 ~]# docker network create -d macvlan --subnet 172.21.0.0/24 --gateway 172.21.0.1 -o parent=eth0.1 macvlan2
81bd9c8e1951e76a7b7c4c15d86803976b2573d94bfc8e1fe8dd4e85d29bed66
[root@server2 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
62adb4558a7e bridge bridge local
bfe9ba62d903 host host local
123424e654e3 macvlan1 macvlan local
81bd9c8e1951 macvlan2 macvlan local
0f50d5bf6719 none null local
[root@server2 ~]# ip a
4: eth0: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default
7: eth0.1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
## 就出现了eth0.1这个网卡
[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
root@45f205287186:/# 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
8: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether 02:42:ac:15:00:0c brd ff:ff:ff:ff:ff:ff
inet 172.21.0.12/24 brd 172.21.0.255 scope global eth0 #自定义的ip地址
valid_lft forever preferred_lft forever
root@45f205287186:/# ping 172.20.0.11
PING 172.20.0.11 (172.20.0.11) 56(84) bytes of data.
^C ## ping不通
3 packets transmitted, 0 received, 100% packet loss, time 1999ms
root@45f205287186:/# ping 172.21.0.11
PING 172.21.0.11 (172.21.0.11) 56(84) bytes of data.
64 bytes from 172.21.0.11: icmp_seq=1 ttl=64 time=0.378 ms ## 通
# 我们ping macvlan1网络的容器是不能ping通的,同一个macvlan的容器才可以互相通信‘注意:macvlan 网络在二层是隔离的,所以不同 macvlan 容器不能通信,可以在三层通过网关连接(加路由),docker 本身不做任何限制,像传统 vlan 那样管理即可。网络选型时如果对网络比较熟悉,选 macvlan较好,因为只需要把网络设备调整好,docker 方面基本不用做什么调整’
















