赞
踩
方式1
- [root@localhost ~]# docker network create --subnet 172.18.0.0/16 mynetwork #模式是bridge模式,可以 -d指定类型,macvlan,host,none等类型
- af4fa989039a30ac098a18a5259b1c74f445188db20afae9189a55dbfecfff84
- [root@localhost ~]# docker network ls
- NETWORK ID NAME DRIVER SCOPE
- 72618157442d bridge bridge local
- c21cf229ce4d host host local
- af4fa989039a mynetwork bridge local
- 0b7d39b3b42a none null local
- [root@localhost ~]# docker run -it --name a1 --network mynetwork -d docker.io/centos:latest
- 4f8b5e9fbbc4742671a5ffcd3f68d30f21608a761f72843e70c583865dab9522
- [root@localhost ~]# docker run -it --name a2 --network mynetwork -d docker.io/centos:latest
- 06527ad89c4c973acbd4a118833a2f668287cb1317474e06748ad572acaa1a6e
- [root@localhost ~]# docker run -it --name a1 --network mynetwork -d
- [root@localhost ~]# docker exec -it 4f8b5e9fbbc47426 ping a1
- PING a1 (172.18.0.2) 56(84) bytes of data.
- 64 bytes from 4f8b5e9fbbc4 (172.18.0.2): icmp_seq=1 ttl=64 time=0.016 ms
- ^C
- --- a1 ping statistics ---
- 1 packets transmitted, 1 received, 0% packet loss, time 0ms
- rtt min/avg/max/mdev = 0.016/0.016/0.016/0.000 ms
- [root@localhost ~]# docker exec -it 4f8b5e9fbbc47426 ping a2
- PING a2 (172.18.0.3) 56(84) bytes of data.
- 64 bytes from a2.mynetwork (172.18.0.3): icmp_seq=1 ttl=64 time=0.056 ms
- ^C
- --- a2 ping statistics ---
- 1 packets transmitted, 1 received, 0% packet loss, time 0ms
- rtt min/avg/max/mdev = 0.056/0.056/0.056/0.000 ms
- [root@localhost ~]#
方式2
- [root@localhost ~]# docker run -it --name a1 --network mynetwork -d docker.io/centos:latest
- bf44c47d94975b1229d8c6dbb2bb5f0ffa0e4e9cffd7a8531a2a0c4dbd5f5020
- [root@localhost ~]# docker run -it --name a2 --network container:a1 -d docker.io/centos:latest
- 26ca032cfe326a52b8208c7c6adb7b547913df6c5a826c1f84ffc7a2575d1053
- [root@localhost ~]# docker exec -it bf44c47d949 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
- 26: eth0@if27: <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 link-netnsid 0
- inet 172.18.0.2/16 scope global eth0
- valid_lft forever preferred_lft forever
- inet6 fe80::42:acff:fe12:2/64 scope link
- valid_lft forever preferred_lft forever
- [root@localhost ~]# docker exec -it 26ca032cfe3 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
- 26: eth0@if27: <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 link-netnsid 0
- inet 172.18.0.2/16 scope global eth0
- valid_lft forever preferred_lft forever
- inet6 fe80::42:acff:fe12:2/64 scope link
- valid_lft forever preferred_lft forever
方式3
- [root@localhost ~]# docker run -it --name a1 -d docker.io/centos:latest
- d22770ead1bf38054d9a87dac109ed56350f915a7641f656861fe08ebcd73d27
- [root@localhost ~]# docker run -it --name a2 --link a1:wubo -d docker.io/centos:latest
- 0845fa63c8a39b1103a610ce82f27f20357a82464c8fe108c8c0ea4e7c8f726d
- [root@localhost ~]# docker exec -it 0845fa63c 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 wubo d22770ead1bf a1
- 172.17.0.3 0845fa63c8a3
- [root@localhost ~]# docker exec -it 0845fa63c ping wubo
- PING wubo (172.17.0.2) 56(84) bytes of data.
- 64 bytes from wubo (172.17.0.2): icmp_seq=1 ttl=64 time=0.056 ms
- ^C
- --- wubo ping statistics ---
- 1 packets transmitted, 1 received, 0% packet loss, time 0ms
- rtt min/avg/max/mdev = 0.056/0.056/0.056/0.000 ms
- [root@localhost ~]# docker exec -it 0845fa63c ping a1
- PING wubo (172.17.0.2) 56(84) bytes of data.
- 64 bytes from wubo (172.17.0.2): icmp_seq=1 ttl=64 time=0.046 ms
- ^C
- --- wubo ping statistics ---
- 1 packets transmitted, 1 received, 0% packet loss, time 0ms
- rtt min/avg/max/mdev = 0.046/0.046/0.046/0.000 ms
方案:
- [root@localhost ~]# ip link set dev ens33 promisc on
- [root@localhost ~]# ip l
- 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
- link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
- 2: ens33: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
- link/ether 00:0c:29:f9:78:c3 brd ff:ff:ff:ff:ff:ff
- 3: ens37: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
- link/ether 00:0c:29:f9:78:cd brd ff:ff:ff:ff:ff:ff
- 4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
- link/ether 02:42:88:95:65:19 brd ff:ff:ff:ff:ff:ff
- [root@localhost ~]#
- [root@localhost ~]# docker network create -d macvlan --subnet 172.20.0.0/24 --gateway 172.20.0.1 -o parent=ens33 macvlan1
- b82a55b4a08b833a1b3b53df9f3ec7c26d9540498318e6a725317336142ac743
- [root@localhost ~]# docker network create -d macvlan --subnet 172.20.0.0/24 --gateway 172.20.0.1 -o parent=ens38 macvlan1
- 2aea05f669cb37f2cffef89fa17bfba08acde31821e1eb9919a6ec9e09a409a3
-
- [root@localhost ~]# docker run -it --name vm1 --network macvlan1 --ip 172.20.0.10 -d docker.io/centos:latest
- 7643bacf68b78462e0d8e9d6fe2b951feebb073b1f17d974176de90f63b1667d
- [root@localhost ~]# docker exec -it 7643bacf ip a s
- 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
- 32: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
- link/ether 02:42:ac:14:00:0a brd ff:ff:ff:ff:ff:ff link-netnsid 0
- inet 172.20.0.10/24 scope global eth0
- valid_lft forever preferred_lft forever
- inet6 fe80::42:acff:fe14:a/64 scope link
- valid_lft forever preferred_lft forever
手动设置ip(不能与server1的ip一样,否则会冲突) 在第二台host中配置相同网段的网络(相当于交换机,只在二层进行交换,所以需要设置为相同的网段)
-
- [root@localhost ~]# docker run -it --name a1 --name vm1 --network macvlan1 --ip 172.20.0.11 -d docker.io/centos:latest
- e5098bfd3d804860d4d80a4cd936e145ae877306c2fff57b72428a6aa9d0c76f
- [root@localhost ~]# docker exec -it e5098bfd3d8 ip a s
- 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
- 22: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
- link/ether 02:42:ac:14:00:0b brd ff:ff:ff:ff:ff:ff link-netnsid 0
- inet 172.20.0.11/24 scope global eth0
- valid_lft forever preferred_lft forever
- inet6 fe80::42:acff:fe14:b/64 scope link
- valid_lft forever preferred_lft forever
- [root@localhost ~]# brctl show
- bridge name bridge id STP enabled interfaces
- docker0 8000.024288956519 no
- [root@localhost ~]#
- [root@localhost ~]# brctl show
- bridge name bridge id STP enabled interfaces
- docker0 8000.0242cb5b632f no
- [root@localhost ~]#
测试
- [root@localhost ~]# docker exec -it 7643bacf 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.904 ms
- 64 bytes from 172.20.0.11: icmp_seq=2 ttl=64 time=0.445 ms
- 64 bytes from 172.20.0.11: icmp_seq=3 ttl=64 time=1.25 ms
- 64 bytes from 172.20.0.11: icmp_seq=4 ttl=64 time=0.515 ms
- ^C
- --- 172.20.0.11 ping statistics ---
- 4 packets transmitted, 4 received, 0% packet loss, time 6ms
- rtt min/avg/max/mdev = 0.445/0.778/1.251/0.325 ms
- [root@localhost ~]# docker exec -it 7643bacf ping 172.20.0.1
- PING 172.20.0.1 (172.20.0.1) 56(84) bytes of data.
- ^C
- --- 172.20.0.1 ping statistics ---
- 3 packets transmitted, 0 received, 100% packet loss, time 2ms
此方法的缺陷:在企业中,即使创建多个网卡仍然不够用,并且机器真实的物理网卡有限。解决方案参考https://blog.csdn.net/Michaelwubo/article/details/110483589就是创建vlan子接口的方式实现多macvlan。vlan在二层最多(vlan id 1-4096)个逻辑网络
参考https://blog.csdn.net/weixin_44791884/article/details/105441016
直观上看,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)与外界相通,并可以收发数据包;此外,如果不同子网之间要进行通信,需要额外的路由机制。
Docker中的网络接口默认都是虚拟的接口。虚拟接口的最大优势就是转发效率极高。这是因为Linux通过在内核中进行数据复制来实现虚拟接口之间的数据转发,即发送接口的发送缓存中的数据包将被直接复制到接收接口的接收缓存中,而无需通过外部物理网络设备进行交换。对于本地系统和容器内系统来看,虚拟接口跟一个正常的以太网卡相比并无区别,只是它速度要快得多。
Docker容器网络就很好地利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做veth pair)。
一般情况下,Docker创建一个容器的时候,会具体执行如下操作:
1.创建一对虚拟接口,分别放到本地主机和新容器的命名空间中;
2.本地主机一端的虚拟接口连接到默认的docker0网桥或指定网桥上,并具有一个以veth开头的唯一名字,如veth1234;
3.容器一端的虚拟接口将放到新创建的容器中,并修改名字作为eth0。这个接口只在容器的命名空间可见;
4.从网桥可用地址段中获取一个空闲地址分配给容器的eth0(例如172.17.0.2/16),并配置默认路由网关为docker0网卡的内部接口docker0的IP地址(例如172.17.42.1/16)。
完成这些之后,容器就可以使用它所能看到的eth0虚拟网卡来连接其他容器和访问外部网络。用户也可以通过docker network命令来手动管理网络
按docker官方的说法,docker容器的网络有五种模式:
1)bridge模式,--net=bridge(默认) 这是dokcer网络的默认设置,为容器创建独立的网络命名空间,容器具有独立的网卡等所有单独的网络栈,是最常用的使用方式。 在docker run启动容器的时候,如果不加--net参数,就默认采用这种网络模式。安装完docker,系统会自动添加一个供docker使用的网桥docker0,我们创建一个新的容器时, 容器通过DHCP获取一个与docker0同网段的IP地址,并默认连接到docker0网桥,以此实现容器与宿主机的网络互通。 2)host模式,--net=host 这个模式下创建出来的容器,直接使用容器宿主机的网络命名空间,除了网络不隔离其他namespace还是隔离的。 将不拥有自己独立的Network Namespace,即没有独立的网络环境。它使用宿主机的ip和端口。 3)none模式,--net=none ovs时候使用openswitch(目前docker 原生还不支持openswit创建的网桥,需要手动给容器加虚拟网卡) 为容器创建独立网络命名空间,但不为它做任何网络配置,容器中只有lo,用户可以在此基础上,对容器网络做任意定制。 这个模式下,dokcer不为容器进行任何网络配置。需要我们自己为容器添加网卡,配置IP。 因此,若想使用pipework或ovs配置docker容器的ip地址,必须要在none模式下才可以。 4)其他容器模式(即container模式),--net=container:NAME_or_ID 与host模式类似,只是容器将与指定的容器共享网络命名空间。除了网络不隔离其他namespace还是隔离的。 这个模式就是指定一个已有的容器,共享该容器的IP和端口。除了网络方面两个容器共享,其他的如文件系统,进程等还是隔离开的。 5)用户自定义:docker 1.9版本以后新增的特性,允许容器使用第三方的网络实现或者创建单独的bridge网络,提供网络隔离能力。
bridge模式是docker默认的,也是开发者最常使用的网络模式。在这种模式下,docker为容器创建独立的网络栈,保证容器内的进程使用独立的网络环境,
实现容器之间、容器与宿主机之间的网络栈隔离。同时,通过宿主机上的docker0网桥,容器可以与宿主机乃至外界进行网络通信。
其网络模型可以参考下图:
从上面的网络模型可以看出,容器从原理上是可以与宿主机乃至外界的其他机器通信的。同一宿主机上,容器之间都是连接掉docker0这个网桥上的,它可以作为虚拟交换机使容器可以相互通信。
然而,由于宿主机的IP地址与容器veth pair的 IP地址均不在同一个网段,故仅仅依靠veth pair和namespace的技术,还不足以使宿主机以外的网络主动发现容器的存在。为了使外界可以方位容器中的进程,docker采用了端口绑定的方式,也就是通过iptables的NAT,将宿主机上的端口
端口流量转发到容器内的端口上。
- 举一个简单的例子,使用下面的命令创建容器,并将宿主机的3306端口绑定到容器的3306端口:
- [root@localhost ~]# docker run -it --name a1 -p 9999:8888 -d docker.io/centos:latest
-
- 在宿主机上,可以通过iptables -t nat -L -n,查到一条DNAT规则: #因为是外面叫进到docker里面所以是DNAT 对目标地址和端口做替换
-
- DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9999 to:172.17.0.2:8888
-
- [root@localhost ~]# iptables -t nat -L -n
- Chain PREROUTING (policy ACCEPT)
- target prot opt source destination
-
- Chain INPUT (policy ACCEPT)
- target prot opt source destination
-
- Chain OUTPUT (policy ACCEPT)
- target prot opt source destination
-
- Chain POSTROUTING (policy ACCEPT)
- target prot opt source destination
- MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:8888
-
- Chain DOCKER (0 references)
- target prot opt source destination
- DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9999 to:172.17.0.2:8888
-
- 上面的172.17.0.2即为bridge模式下,创建的容器IP。
-
- 很明显,bridge模式的容器与外界通信时,必定会占用宿主机上的端口,从而与宿主机竞争端口资源,对宿主机端口的管理会是一个比较大的问题。同时,由于容器与外界通信是基于三层上iptables NAT,性能和效率上的损耗是可以预见的。和macvlan比那个效率高,只考虑效率,不考虑macvlan占用一个物理网卡的问题
就目前Docker自身默认的网络来说,单台主机上的不同Docker容器可以借助docker0网桥直接通信,这没毛病,而不同主机上的Docker容器之间只能通过在主机上用映射端口的方法来进行通信,有时这种方式会很不方便,甚至达不到我们的要求,因此位于不同物理机上的Docker容器之间直接使用本身的IP地址进行通信很有必要。再者说,如果将Docker容器起在不同的物理主机上,我们不可避免的会遭遇到Docker容器的跨主机通信问题。本文就来尝试一下。
如下图所示,我们有两个物理主机1和主机2,我们在各自宿主机上启动一个centos容器,启动成功之后,两个容器分别运行在两个宿主机之上,默认的IP地址分配如图所示,这也是Docker自身默认的网络。
此时两台主机上的Docker容器如何直接通过IP地址进行通信?
一种直接想到的方案便是通过分别在各自主机中 添加路由 来实现两个centos容器之间的直接通信。我们来试试吧
由于使用容器的IP进行路由,就需要避免不同主机上的容器使用了相同的IP,为此我们应该为不同的主机分配不同的子网来保证。于是我们构造一下两个容器之间通信的路由方案,如下图所示。
各项配置如下:
这样配置之后,两个主机上的Docker容器就肯定不会使用相同的IP地址从而避免了IP冲突。
我们接下来 定义两条路由规则 即可:
综上所述,数据包在两个容器间的传递过程如下:
我们心里方案想的是这样,接下来实践一下看看是否可行。
操作系统 | 服务器地址 | Dockerd地址 |
CentOS Linux release 7.8.2003 (Core) | 10.10.1.216 | 172.17.0.2 |
CentOS Linux release 7.8.2003 (Core) | 10.10.1.219 | 172.18.0.2 |
docker的版本为 :# docker --version
Docker version 1.13.1, build 0be3e21/1.13.1
比如主机1,我需要运行一个docker镜像,要求它的网段必须是 172.17.0.0/24 ,怎么设置呢?
有2个办法:
1. 修改docker0的网段
2. 创建一个docker网桥
这里为了快速实现,选用第一种方案。直接修改/etc/sysconfig/docker 文件,添加--bip 参数即可
- [root@localhost ~]# cat /etc/sysconfig/docker
- # /etc/sysconfig/docker
-
- # Modify these options if you want to change the way the docker daemon runs
- OPTIONS='--selinux-enabled --log-driver=journald --signature-verification=false --bip 172.17.0.1/16'
特别注意,OPTIONS参数后面必须有引号。--bip后面的ip就是docker0的ip地址,也即是bridge ip ,docker0这个网桥的ip,一般从第一个ip开始!
查看主机1上docker0的ip地址
- [root@localhost ~]# ip a s docker0
- 4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
- link/ether 02:42:88:95:65:19 brd ff:ff:ff:ff:ff:ff
- inet 172.17.0.1/16 scope global docker0
- valid_lft forever preferred_lft forever
- inet6 fe80::42:88ff:fe95:6519/64 scope link
- valid_lft forever preferred_lft forever
查看主机2上docker0的ip地址
- [root@localhost ~]# ip a s docker0
- 4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
- link/ether 02:42:cb:5b:63:2f brd ff:ff:ff:ff:ff:ff
- inet 172.18.0.1/16 scope global docker0
- valid_lft forever preferred_lft forever
- inet6 fe80::42:cbff:fe5b:632f/64 scope link
- valid_lft forever preferred_lft forever
发现默认的网段已经改变了!
查看路由表
- [root@localhost ~]# route -n
- Kernel IP routing table
- Destination Gateway Genmask Flags Metric Ref Use Iface
- 0.0.0.0 10.10.1.1 0.0.0.0 UG 0 0 0 ens33
- 10.10.1.0 0.0.0.0 255.255.255.0 U 0 0 0 ens33
- 169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 ens33
- 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
默认只有自己本身的路由,如果需要访问 172.18.0.0/24 网段,需要添加路由
主机1上添加路由规则如下:
- [root@localhost ~]# route add -net 172.18.0.0/24 gw 10.10.1.219 dev ens33
- 或
- [root@localhost ~]# ip route add 172.18.0.0/24 via 10.10.1.219 dev ens33
- 意思是去往172.18.0.0网络通过ens33网卡出去,吓一跳的地址是10.10.1.219,此时要求在ens33网卡上能ping通10.10.1.219的ip ping -I ens33 10.10.1.219 或ping -I 10.10.1.216 10.10.1.219
- gw 表示下一跳地址,这里的地址就是主机2的ip地址
- 再次查看路由,发现已经添加上了
- [root@localhost ~]# route -n
- Kernel IP routing table
- Destination Gateway Genmask Flags Metric Ref Use Iface
- 0.0.0.0 10.10.1.1 0.0.0.0 UG 0 0 0 ens33
- 10.10.1.0 0.0.0.0 255.255.255.0 U 0 0 0 ens33
- 169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 ens33
- 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
- 172.18.0.0 10.10.1.219 255.255.255.0 UG 0 0 0 ens33
主机2上添加路由规则如下:
[root@localhost ~]# ip route add 172.17.0.0/24 via 10.10.1.216 dev ens38
- [root@localhost ~]# route -n
- Kernel IP routing table
- Destination Gateway Genmask Flags Metric Ref Use Iface
- 0.0.0.0 10.10.1.1 0.0.0.0 UG 0 0 0 ens38
- 10.10.1.0 0.0.0.0 255.255.255.0 U 0 0 0 ens38
- 172.17.0.0 10.10.1.216 255.255.255.0 UG 0 0 0 ens38
- 172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
- 192.168.13.0 0.0.0.0 255.255.255.0 U 0 0 0 ens39
在主机1上,ping主机2的docker0地址
- [root@localhost ~]# ping 172.18.0.1
- PING 172.18.0.1 (172.18.0.1) 56(84) bytes of data.
- 64 bytes from 172.18.0.1: icmp_seq=1 ttl=64 time=0.505 ms
- 64 bytes from 172.18.0.1: icmp_seq=2 ttl=64 time=0.453 ms
- 64 bytes from 172.18.0.1: icmp_seq=3 ttl=64 time=0.417 ms
- ^C
- --- 172.18.0.1 ping statistics ---
- 3 packets transmitted, 3 received, 0% packet loss, time 2001ms
- rtt min/avg/max/mdev = 0.417/0.458/0.505/0.040 ms
在主机2上,ping主机1的docker0地址
- [root@localhost ~]# ping 172.17.0.1
- PING 172.17.0.1 (172.17.0.1) 56(84) bytes of data.
- 64 bytes from 172.17.0.1: icmp_seq=1 ttl=64 time=0.561 ms
- 64 bytes from 172.17.0.1: icmp_seq=2 ttl=64 time=0.353 ms
- ^C
- --- 172.17.0.1 ping statistics ---
- 2 packets transmitted, 2 received, 0% packet loss, time 1000ms
- rtt min/avg/max/mdev = 0.353/0.457/0.561/0.104 ms
ok,既然docker0都通了,那么起一个docker容器,会不会也是通的的呢?测试一下吧
在主机1上面启动一个容器,这里选用centos镜像,它只有4.5M,并且自带ping命令!
先查看ip地址
- root@localhost ~]# docker run -it --name a1 -d docker.io/centos:latest
- 7fc75f8c87dc8c07dcb487070d52326d16343998ea2ccd2b3feb15025f962d78
- [root@localhost ~]# docker exec -it 7fc75f8c 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
- 41: eth0@if42: <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 link-netnsid 0
- inet 172.17.0.2/16 scope global eth0
- valid_lft forever preferred_lft forever
- inet6 fe80::42:acff:fe11:2/64 scope link
- valid_lft forever preferred_lft forever
它的ip地址为 172.17.0.2 ,由于docker0占用了第一个ip地址。所以容器启动时,ip地址从第二个开始分配!
在主机2上面启动一个容器
- [root@localhost ~]# docker run -it --name a2 -d docker.io/centos:latest
- 7b82b6bce81a9978c74fafe609244778f951efa63ed0eb18fa485303581f795c
- [root@localhost ~]# docker exec -it 7b82b6bce 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
- 23: eth0@if24: <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 link-netnsid 0
- inet 172.18.0.2/16 scope global eth0
- valid_lft forever preferred_lft forever
- inet6 fe80::42:acff:fe12:2/64 scope link
- valid_lft forever preferred_lft forever
在主机1上的容器中 ping 主机2中的容器
先来ping 主机2的docker0,再ping 主机2中的容器
- [root@localhost ~]# docker exec -it 7fc75f8c87dc ping 172.18.0.1
- \PING 172.18.0.1 (172.18.0.1) 56(84) bytes of data.
- 64 bytes from 172.18.0.1: icmp_seq=1 ttl=63 time=0.423 ms
- \64 bytes from 172.18.0.1: icmp_seq=2 ttl=63 time=1.05 ms
- ^C
- --- 172.18.0.1 ping statistics ---
- 2 packets transmitted, 2 received, 0% packet loss, time 2ms
- rtt min/avg/max/mdev = 0.423/0.734/1.045/0.311 ms
- [root@localhost ~]# docker exec -it 7fc75f8c87dc ping 172.18.0.2
- PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
- ^C
- --- 172.18.0.2 ping statistics ---
- 2 packets transmitted, 0 received, 100% packet loss, time 1ms
-
- [root@localhost ~]# docker exec -it 7fc75f8c87dc ping 10.10.1.216
- PING 10.10.1.216 (10.10.1.216) 56(84) bytes of data.
- 64 bytes from 10.10.1.216: icmp_seq=1 ttl=64 time=0.038 ms
- 64 bytes from 10.10.1.216: icmp_seq=2 ttl=64 time=0.114 ms
- ^C
- --- 10.10.1.216 ping statistics ---
- 2 packets transmitted, 2 received, 0% packet loss, time 1000ms
- rtt min/avg/max/mdev = 0.038/0.076/0.114/0.038 ms
- [root@localhost ~]# docker exec -it 7fc75f8c87dc ping 10.10.1.1
- PING 10.10.1.1 (10.10.1.1) 56(84) bytes of data.
- 64 bytes from 10.10.1.1: icmp_seq=1 ttl=253 time=1.45 ms
- 64 bytes from 10.10.1.1: icmp_seq=2 ttl=253 time=1.73 ms
-
-
- [root@localhost ~]# docker exec -it 7fc75f8c87dc ping www.baidu.com
- PING www.a.shifen.com (182.61.200.7) 56(84) bytes of data.
- 64 bytes from 182.61.200.7 (182.61.200.7): icmp_seq=1 ttl=54 time=4.76 ms
- 64 bytes from 182.61.200.7 (182.61.200.7): icmp_seq=2 ttl=54 time=5.48 ms
- ^C
- --- www.a.shifen.com ping statistics ---
- 2 packets transmitted, 2 received, 0% packet loss, time 3ms
从结果中,可以发现。docker0是通的,但是主机2中的容器是不通的,为什么呢?
1)首先宿主机上创建一对虚拟网卡veth pair设备,veth设备总是成对出现的,形成一个通信通道,数据传输就是基于这个链路的,veth设备常用来连接两个网络设备
2)Docker将veth pair设备的一端放在容器中,并命名为eth0,然后将另一端加入docker0网桥中,可以通过brctl show命令查看
3)从docker0网卡中分配一个IP到给容器使用,并设置docker0的IP地址为容器默认网关
4)此时容器IP与宿主机是可以通信的,宿主机也可以访问容器中的ip地址,在bridge模式下,连接同一网桥的容器之间可以相互通信,同时容器可以访问外网,但是其他物理机不能访问docker容器IP,需要通过NAT将容器的IP的port映射为宿主机的IP和port;
- [root@localhost ~]# ip link
- 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
- link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
- 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
- link/ether 00:0c:29:f9:78:c3 brd ff:ff:ff:ff:ff:ff
- 3: ens37: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
- link/ether 00:0c:29:f9:78:cd brd ff:ff:ff:ff:ff:ff
- 4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
- link/ether 02:42:88:95:65:19 brd ff:ff:ff:ff:ff:ff
- 42: veth0d73285@if41: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
- link/ether d2:c7:4e:13:71:30 brd ff:ff:ff:ff:ff:ff link-netnsid 0
会发现有一个 veth077daec 的网卡设备。咦,这是个啥?
当运行docker容器后,再次执行ifconfig命令可以看到会多出个网卡驱动veth开头的名字,所以补充下veth。
Linux container 中用到一个叫做veth的东西,这是一种新的设备,专门为 container 所建。veth 从名字上来看是 Virtual ETHernet 的缩写,它的作用很简单,就是要把从一个 network namespace 发出的数据包转发到另一个 namespace。veth 设备是成对的,一个是 container 之中,另一个在 container 之外,即在真实机器上能看到的。
VETH设备总是成对出现,一端请求发送的数据总是从另一端以请求接受的形式出现。创建并配置正确后,向其一端输入数据,VETH会改变数据的方向并将其送入内核网络子系统,完成数据的注入,而在另一端则能读到此数据。(Namespace,其中往veth设备上任意一端上RX到的数据,都会在另一端上以TX的方式发送出去)veth工作在L2数据链路层,veth-pair设备在转发数据包过程中并不串改数据包内容。
成数据的注入,而在另一端则能读到此数据。(Namespace,其中往veth设备上任意一端上RX到的数据,都会在另一端上以TX的方式发送出去)veth工作在L2数据链路层,veth-pair设备在转发数据包过程中并不串改数据包内容。
显然,仅有veth-pair设备,容器是无法访问网络的。因为容器发出的数据包,实质上直接进入了veth1设备的协议栈里。如果容器需要访问网络,需要使用bridge等技术,将veth1接收到的数据包通过某种方式转发出去 。
veth参考链接
https://blog.csdn.net/Michaelwubo/article/details/110198072
https://blog.csdn.net/Michaelwubo/article/details/110224269
因此,如果要多台主机之间的docker通信,需要使用NAT转换。那么接下来,就是设置iptables规则了
在主机1上查看默认的nat 规则
- [root@localhost ~]# iptables -t nat -L
- Chain PREROUTING (policy ACCEPT)
- target prot opt source destination
- DOCKER all -- anywhere anywhere 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 -- anywhere !loopback/8 ADDRTYPE match dst-type LOCAL
-
- Chain POSTROUTING (policy ACCEPT)
- target prot opt source destination
- MASQUERADE all -- 172.17.0.0/16 anywhere
-
- Chain DOCKER (2 references)
- target prot opt source destination
- RETURN all -- anywhere anywhere
- [root@localhost ~]#
这些nat规则,都是docker帮你做的。
增加一条规则 prerouting 就是DNAT操作,把进来(也就是外面近容器里面)的目标公网地址和端口(也就是ens33网卡的地址)转换到内网地址和端口(也即是容器里面的ip和端口)
- [root@localhost ~]# iptables -t nat -I PREROUTING -s 172.17.0.0/16 -d 172.18.0.0/16 -j DNAT --to 172.17.0.1
- [root@localhost ~]# iptables -t nat -L
- Chain PREROUTING (policy ACCEPT)
- target prot opt source destination
- DNAT all -- 172.17.0.0/16 172.18.0.0/16 to:172.17.0.1
- DOCKER all -- anywhere anywhere 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 -- anywhere !loopback/8 ADDRTYPE match dst-type LOCAL
-
- Chain POSTROUTING (policy ACCEPT)
- target prot opt source destination
- MASQUERADE all -- 172.17.0.0/16 anywhere
-
- Chain DOCKER (2 references)
- target prot opt source destination
- RETURN all -- anywhere anywhere
PREROUTING:可以在这里定义进行目的NAT的规则,因为路由器进行路由时只检查数据包的目的ip地址,所以为了使数据包得以正确路由,我们必须在路由之前就进行目的NAT;
上面那一条路由规则是啥意思呢?就是当源地址为172.17.0.0/16网段 访问 172.18.0.0/16 时,在路由之前,将ip转换为172.17.0.1。
注意:一定要加-d参数。如果不加,虽然docker之间可以互通,但是不能访问网站,比如百度,qq之类的!
为什么呢?访问172.18.0.024 时,通过docker0网卡出去的。但是访问百度,还是通过docker0,就出不去了!
真正连接外网的是ens33网卡,必须通过它才行!因此必须要指定-d参数!
- [root@localhost ~]# docker exec -it 7fc75f8c87dc ping 172.18.0.2
- PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
- 64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.037 ms
- 64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.120 ms
- 64 bytes from 172.18.0.2: icmp_seq=3 ttl=64 time=0.045 ms
- ^C
- --- 172.18.0.2 ping statistics ---
- 3 packets transmitted, 3 received, 0% packet loss, time 3ms
- rtt min/avg/max/mdev = 0.037/0.067/0.120/0.037 ms
是可以通讯的!
注意:iptables必须在 PREROUTING 上面做,而不是常规的 POSTROUTING。我测试在POSTROUTING做规则,始终无法通讯!POSTROUTING是出去的是容器访问外面的也就是内网转公网的时候用https://blog.csdn.net/Michaelwubo/article/details/109451475iptable 相关
主机2上添加如下规则:
- [root@localhost ~]# iptables -t nat -I PREROUTING -s 172.18.0.0/16 -d 172.17.0.0/16 -j DNAT --to 172.18.0.1
- [root@localhost ~]# iptables -t nat -L
- Chain PREROUTING (policy ACCEPT)
- target prot opt source destination
-
-
-
- DNAT all -- 172.18.0.0/16 172.17.0.0/16 to:172.18.0.1
参考https://www.cnblogs.com/xiao987334176/p/10049844.html
参考https://blog.csdn.net/Michaelwubo/article/details/110929884
flannel目前已经支持UDP、VxLAN、host-gw、AWS VPC和GCE路由等多种backend
跨主机通信的一个解决方案是Flannel,由CoreOS推出,支持3种实现:UDP、VXLAN、host-gw
udp模式:使用设备flannel.0进行封包解包,不是内核原生支持,上下文切换较大,性能非常差
vxlan模式:使用flannel.1进行封包解包,内核原生支持,性能较强
host-gw模式:这是一种纯三层网络的方案,性能最高,无需flannel.1这样的中间设备,直接宿主机当作子网的下一跳地址,性能最强,符合SDN(openflow)思想
host-gw的性能损失大约在10%左右,而其他所有基于VXLAN“隧道”机制 的网络方案,性能损失在20%~30%左右
容器直接使用目标容器的ip访问,默认通过容器内部的eth0发送出去。
报文通过veth pair被发送到vethXXX。
vethXXX是直接连接到虚拟交换机docker0的,报文通过虚拟bridge docker0发送出去。
查找路由表,外部容器ip的报文都会转发到flannel0虚拟网卡,这是一个P2P的虚拟网卡,然后报文就被转发到监听在另一端的flanneld。
flanneld通过etcd维护了各个节点之间的路由表,把原来的报文UDP封装一层,通过配置的iface发送出去。
报文通过主机之间的网络找到目标主机。
报文继续往上,到传输层,交给监听在8285端口的flanneld程序处理。
数据被解包,然后发送给flannel0虚拟网卡。
查找路由表,发现对应容器的报文要交给docker0。
docker0找到连到自己的容器,把报文发送过去
https://www.cnblogs.com/breezey/p/9419612.html
https://www.jianshu.com/p/82864baeacac
https://www.cnblogs.com/kevingrace/p/6864804.html
https://www.cnblogs.com/breezey/p/9419612.html
https://blog.csdn.net/myy1066883508/article/details/106494910
https://blog.csdn.net/wuyuchen20/article/details/104515974
https://www.lmlphp.com/user/3182/article/item/383522/
两台均配置为这样echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf && sysctl -p
清除iptables底层默认规则,并开启允许转发功能 iptables -P INPUT ACCEPT&&iptables -P FORWARD ACCEPT&&iptables -F&&iptables -L -n
关闭selinux :setenforce 0
需要安装的组件为:1,etcd2,flannel 3,docker
etcd在10.10.1.216节点即可,不做etcd集群。
yum install -y epel-release etcd flannel
- [root@localhost ~]# cat /etc/etcd/etcd.conf | grep -v "^#"
- ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
- ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
- ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379,http://0.0.0.0:4001"
- ETCD_NAME="node1"
- ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.10.1.216:2380"
- ETCD_ADVERTISE_CLIENT_URLS="http://10.10.1.216:2379"
- [root@localhost ~]# cat /etc/sysconfig/flanneld | grep -v "^#"
-
- FLANNEL_ETCD_ENDPOINTS="http://10.10.1.216:2379"
-
- FLANNEL_ETCD_PREFIX="/atomic.io/network"
etcdctl mk /atomic.io/network/config '{"Network":"192.168.0.0/16", "SubnetLen": 24 ,"SubnetMin": "192.168.1.0", "SubnetMax": "192.168.254.0","Backend": {"Type": "vxlan","Directrouting":"true"}}'
docker
两台都启动后我们来配置docker
可以发现的是在我们先安装flannel和etcd后最后安装docker后我们使用 sysetmctl show docker 将会发现 在安装flannel后自动生成配置
DropInPaths=/usr/lib/systemd/system/docker.service.d/flannel.conf
故在docker的systemd文件中应用对应变量 DOCKER_NETWORK_OPTIONS 即可
- [root@localhost ~]# cat /usr/lib/systemd/system/docker.service
- [Unit]
- Description=Docker Application Container Engine
- Documentation=http://docs.docker.com
- After=network.target
- Wants=docker-storage-setup.service
- Requires=docker-cleanup.timer
-
- [Service]
- Type=notify
- NotifyAccess=main
- EnvironmentFile=-/run/containers/registries.conf
- EnvironmentFile=-/etc/sysconfig/docker
- EnvironmentFile=-/etc/sysconfig/docker-storage
- EnvironmentFile=-/etc/sysconfig/docker-network
- Environment=GOTRACEBACK=crash
- Environment=DOCKER_HTTP_HOST_COMPAT=1
- Environment=PATH=/usr/libexec/docker:/usr/bin:/usr/sbin
- ExecStart=/usr/bin/dockerd-current $DOCKER_NETWORK_OPTIONS \
- --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current \
- --default-runtime=docker-runc \
- --exec-opt native.cgroupdriver=systemd \
- --userland-proxy-path=/usr/libexec/docker/docker-proxy-current \
- --init-path=/usr/libexec/docker/docker-init-current \
- --seccomp-profile=/etc/docker/seccomp.json \
- $OPTIONS \
- $DOCKER_STORAGE_OPTIONS \
- $DOCKER_NETWORK_OPTIONS \
- $ADD_REGISTRY \
- $BLOCK_REGISTRY \
- $INSECURE_REGISTRY \
- $REGISTRIES
- ExecReload=/bin/kill -s HUP $MAINPID
- LimitNOFILE=1048576
- LimitNPROC=1048576
- LimitCORE=infinity
- TimeoutStartSec=0
- Restart=on-abnormal
- KillMode=process
-
- [Install]
- WantedBy=multi-user.target
添加上述红色部分即可,引用了flannel的网络
重载配置systemctl daemon-reload&&systemctl restart docker
可以看到docker 0 的网桥已经使用flannel的网络段了
目前两台物理机的docker 0 网络处于同一子网下面的都是使用的flannel的子网,是可以相互ping通的
在node1上,ping node2的docker 0 网桥ip
我们来在容器内部ping下node2的docker 0网桥 IP,是可以ping通的,没问题
容器外部ping容器内部的ip也是通的
需要注意的几点要点:
1,向etcd集群写入子网信息时候务必要注意建立的子网文件夹要和flannel的配置文件内一直。
2,flannel的配置,etcd集群地址写正确,子网文件夹信息要和etcd写入一致
3,系统iptables默认规则一定要清除,并开启允许转发
4,确定下docker的网络是否被flannel接管,我们查看下docker的进程要确认下
- [root@localhost ~]# ps -ef | grep docker
- root 35905 1 0 16:14 ? 00:00:02 /usr/bin/dockerd-current --bip=192.168.19.1/24 --ip-masq=true --mtu=1472 --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current --default-runtime=docker-runc --exec-opt native.cgroupdriver=systemd --userland-proxy-path=/usr/libexec/docker/docker-proxy-current --init-path=/usr/libexec/docker/docker-init-current --seccomp-profile=/etc/docker/seccomp.json --selinux-enabled --log-driver=journald --signature-verification=false --bip 172.17.0.1/16 --storage-driver overlay2 --bip=192.168.19.1/24 --ip-masq=true --mtu=1472
- root 35911 35905 0 16:14 ? 00:00:01 /usr/bin/docker-containerd-current -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --shim docker-containerd-shim --runtime docker-runc --runtime-args --systemd-cgroup=true
- root 37290 18696 0 16:34 pts/0 00:00:00 grep --color=auto docker
总之只要物理主机之间能互通,不管各自物理主机上面是模式虚拟网卡,虚拟网络,通过iptable,ip,route,brctl等相关工具都能实现容器或不同net namespace互通
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。