一、简介
?1、介绍
?Docker跨主机容器间网络通信实现的工具有Pipework、Flannel、Weave、Open vSwitch(虚拟交换机)、Calico
Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。但在默认的Docker配置中,每个节点上的Docker服务会分别负责所在节点容器的IP分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外IP地址。并使这些容器之间能够之间通过IP地址相互找到,也就是相互ping通 Flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得"同属一个内网"且"不重复的"IP地址,并让属于不同节点上的容器能够直接通过内网IP通信 Flannel实质上是一种"覆盖网络(overlay network)",即表示运行在一个网上的网(应用层网络),并不依靠ip地址来传递消息,而是采用一种映射机制,把ip地址和identifiers做映射来资源定位。也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持UDP、VxLAN、AWS VPC和GCE路由等数据转发方式
?2、Flannel工作原理
?每个主机配置一个ip段和子网个数。例如,可以配置一个覆盖网络使用 10.100.0.0/16段,每个主机/24个子网。因此主机a可以接受10.100.5.0/24,主机B可以接受10.100.18.0/24的包。flannel使用etcd来维护分配的子网到实际的ip地址之间的映射。对于数据路径,flannel 使用udp来封装ip数据报,转发到远程主机。选择UDP作为转发协议是因为他能穿透防火墙。例如,AWS Classic无法转发IPoIP or GRE 网络包,是因为它的安全组仅仅支持TCP/UDP/ICMP。 Flannel工作原理流程图如下 (默认的节点间数据通信方式是UDP转发; ?flannel默认使用8285端口作为UDP封装报文的端口,VxLan使用8472端口)
?Flannel的工作原理解释如下
1、数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡,这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端 2、Flannel通过Etcd服务维护了一张节点间的路由表,该张表里保存了各个节点主机的子网网段信息 3、源主机的flanneld服务将原本的数据内容UDP封装后根据自己的路由表投递给目的节点的flanneld服务,数据到达以后被解包,然后直接进入目的节点的flannel0虚拟网卡,然后被转发到目的主机的docker0虚拟网卡,最后就像本机容器通信一样的由docker0路由到达目标容器
这样整个数据包的传递就完成了,这里需要说明三个问题: 1、UDP封装是怎么回事? 在UDP的数据内容部分其实是另一个ICMP(也就是ping命令)的数据包。原始数据是在起始节点的Flannel服务上进行UDP封装的,投递到目的节点后就被另一端的Flannel服务 还原成了原始的数据包,两边的Docker服务都感觉不到这个过程的存在
2、为什么每个节点上的Docker会使用不同的IP地址段? 这个事情看起来很诡异,但真相十分简单。其实只是单纯的因为Flannel通过Etcd分配了每个节点可用的IP地址段后,偷偷的修改了Docker的启动参数。 在运行了Flannel服务的节点上可以查看到Docker服务进程运行参数(ps aux|grep docker|grep "bip"),例如“--bip=182.48.25.1/24”这个参数,它限制了所在节 点容器获得的IP范围。这个IP范围是由Flannel自动分配的,由Flannel通过保存在Etcd服务中的记录确保它们不会重复
3、为什么在发送节点上的数据会从docker0路由到flannel0虚拟网卡,在目的节点会从flannel0路由到docker0虚拟网卡? 例如现在有一个数据包要从IP为172.17.18.2的容器发到IP为172.17.46.2的容器。根据数据发送节点的路由表,它只与172.17.0.0/16匹配这条记录匹配,因此数据从docker0出来以后就被投递到了flannel0。同理在目标节点,由于投递的地址是一个容器,因此目的地址一定会落在docker0对于的172.17.46.0/24这个记录上,自然的被投递到了docker0网卡
二、部署环境
系统 | IP | 主机名 | 服务 | CentOS 7.4 | 192.168.2.17 | Docker1 | etcd、flannel、docker | CentOS 7.4 | 192.168.2.1 | Docker2 | flannel、docker |
(一)、安装Etcd
下载Etcd:https://github.com/etcd-io/etcd
?只需要在Docker1上安装
[root@Docker1 ~]# wget https://github.com/coreos/etcd/releases/download/v3.3.9/etcd-v3.3.9-linux-amd64.tar.gz
...........
.....
[root@Docker1 ~]# tar -xf etcd-v3.3.9-linux-amd64.tar.gz
[root@Docker1 ~]# cd etcd-v3.3.9-linux-amd64
[root@Docker1 etcd-v3.3.9-linux-amd64]# ll
总用量 33992
drwxr-xr-x. 11 1000 1000 4096 7月 25 2018 Documentation
-rwxr-xr-x. 1 1000 1000 18934016 7月 25 2018 etcd
-rwxr-xr-x. 1 1000 1000 15809280 7月 25 2018 etcdctl
-rw-r--r--. 1 1000 1000 38864 7月 25 2018 README-etcdctl.md
-rw-r--r--. 1 1000 1000 7262 7月 25 2018 README.md
-rw-r--r--. 1 1000 1000 7855 7月 25 2018 READMEv2-etcdctl.md
[root@Docker1 etcd-v3.3.9-linux-amd64]# cp etcd* /usr/bin/
——————————启动命令————————
[root@Docker1 ~]# etcd -name etcd-17 -data-dir /var/lib/etcd --advertise-client-urls http://192.168.2.17:2379,http://127.0.0.1:2379 --listen-client-urls http://192.168.2.17:2379,http://127.0.0.1:2379
2022-02-24 20:42:50.856604 I | etcdmain: etcd Version: 3.3.9
2022-02-24 20:42:50.856815 I | etcdmain: Git SHA: fca8add78
2022-02-24 20:42:50.856818 I | etcdmain: Go Version: go1.10.3
2022-02-24 20:42:50.856843 I | etcdmain: Go OS/Arch: linux/amd64
..............
......
--name:取名 --data-dir:定义数据路径 --advertise-client-urls:建议使用的客户端通信url,该值用于etcd代理或etcd成员与etcd节点通信,即服务的url --listen-client-urls:监听的用于客户端通信的url,对外提供服务的地址,客户端会连接到这里和 etcd 交互,同样可以监听多个
重新打开一个终端,etcdctl? 是一个客户端连接工具
[root@Docker1 ~]# etcdctl member list
8e9e05c52164694d: name=etcd-17 peerURLs=http://localhost:2380 clientURLs=http://127.0.0.1:2379,http://192.168.2.17:2379 isLeader=true
使用etcdctl连接ectd数据库,检查etcd的连通性
[root@Docker1 ~]# etcdctl --endpoints http://127.0.0.1:2379 member list
8e9e05c52164694d: name=etcd-17 peerURLs=http://localhost:2380 clientURLs=http://127.0.0.1:2379,http://192.168.2.17:2379 isLeader=true
二、安装Flannel
Flannel下载地址:Releases · flannel-io/flannel · GitHub
?在Docker1、Docker2上进行安装
[root@Docker1 ~]# wget https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz
........
...
[root@Docker2 ~]# wget https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz
.......
...
?Docker1
[root@Docker1 ~]# tar -xf flannel-v0.11.0-linux-amd64.tar.gz
[root@Docker1 ~]# cp flanneld /usr/bin/
[root@Docker1 ~]# cp mk-docker-opts.sh /usr/bin/
[root@Docker1 ~]# etcdctl set /coreos.com/network/config '{"Network": "10.0.0.0/16", "SubnetLen": 24, "SubnetMin": "10.0.10.0","SubnetMax": "10.0.20.0", "Backend": {"Type": "vxlan"}}'
{"Network": "10.0.0.0/16", "SubnetLen": 24, "SubnetMin": "10.0.10.0","SubnetMax": "10.0.20.0", "Backend": {"Type": "vxlan"}}
Network:用于指定Flannel地址池 SubnetLen:用于指定分配给单个宿主机的docker0的ip段的子网掩码的长度 SubnetMin:用于指定最小能够分配的ip段 SudbnetMax:用于指定最大能够分配的ip段,在上面的示例中,表示每个宿主机可以分配一个24位掩码长度的子网,可以分配的子网从10.0.10.0/24到10.0.20.0/24,也就意味着在这个网段中,最多只能有10台宿主机 Backend:用于指定数据包以什么方式转发,默认为udp模式,host-gw模式性能最好,但不能跨宿主机网络
[root@Docker1 ~]# etcdctl get /coreos.com/network/config
/coreos.com/network/config
{"Network": "10.0.0.0/16", "SubnetLen": 24, "SubnetMin": "10.0.10.0","SubnetMax": "10.0.20.0", "Backend": {"Type": "vxlan"}}
————————————————————启动Flannel
[root@Docker1 ~]# /usr/bin/flanneld --etcd-endpoints="http://192.168.2.17:2379" --iface=192.168.2.17 --etcd-prefix=/coreos.com/network &
........
...
.
[root@Docker1 ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:7b:cb:af brd ff:ff:ff:ff:ff:ff
inet 192.168.2.17/24 brd 192.168.2.255 scope global ens33
valid_lft forever preferred_lft forever
inet6 fe80::c14f:f7cc:6b8e:2d49/64 scope link
valid_lft forever preferred_lft forever
4: ens38: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:7b:cb:c3 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.106/24 brd 192.168.0.255 scope global dynamic ens38
valid_lft 6454sec preferred_lft 6454sec
inet6 fe80::f8e8:73b:4787:320/64 scope link
valid_lft forever preferred_lft forever
5: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN
link/ether 2e:10:9c:11:b7:58 brd ff:ff:ff:ff:ff:ff
inet 10.0.19.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::2c10:9cff:fe11:b758/64 scope link
valid_lft forever preferred_lft forever
可以使用flannel提供的脚本将subnet.env转写成Docker启动参数,创建好的启动参数默认生成在/run/docker_opts.env文件中
[root@Docker1 ~]# mk-docker-opts.sh
[root@Docker1 ~]# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.0.0.0/16
FLANNEL_SUBNET=10.0.19.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=false
[root@Docker1 ~]# cat /run/docker_opts.env
DOCKER_OPT_BIP="--bip=10.0.19.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=true"
DOCKER_OPT_MTU="--mtu=1450"
DOCKER_OPTS=" --bip=10.0.19.1/24 --ip-masq=true --mtu=1450"
?Docker2
[root@Docker2 ~]# tar -xf flannel-v0.11.0-linux-amd64.tar.gz
[root@Docker2 ~]# cp flanneld /usr/bin/
[root@Docker2 ~]# cp mk-docker-opts.sh /usr/bin/
[root@Docker2 ~]# /usr/bin/flanneld --etcd-endpoints="http://192.168.2.17:2379" --iface=192.168.2.1 --etcd-prefix=/coreos.com/network &
............#注意修改地址
......
[root@Docker2 ~]# mk-docker-opts.sh -c
[root@Docker2 ~]# cat /run/docker_opts.env #可以查看一下分配的网段
DOCKER_OPTS=" --bip=10.0.17.1/24 --ip-masq=true --mtu=1450"
??在Docker1、Docker2上进行安装(安装过程省略)
?给docker的启动项添加内容
[root@Docker1 ~]# vim /usr/lib/systemd/system/docker.service
.......
...
13 EnvironmentFile=/run/docker_opts.env #添加内容
14 ExecStart=/usr/bin/dockerd $DOCKER_OPTS -H fd:// --containerd=/run/containerd/containerd.sock #添加 $DOCKER_OPTS 变量
15 ExecReload=/bin/kill -s HUP $MAINPID
....
..
保存
启动docker
[root@Docker1 ~]# systemctl daemon-reload
[root@Docker1 ~]# systemctl restart docker
[root@Docker1 ~]# etcdctl ls /coreos.com/network #这两个文件是
/coreos.com/network/config
/coreos.com/network/subnets #记录节点分配给容器的网段
[root@Docker2 ~]# vim /usr/lib/systemd/system/docker.service
.......
...
13 EnvironmentFile=/run/docker_opts.env #添加内容
14 ExecStart=/usr/bin/dockerd $DOCKER_OPTS -H fd:// --containerd=/run/containerd/containerd.sock #添加 $DOCKER_OPTS 变量
15 ExecReload=/bin/kill -s HUP $MAINPID
....
..
保存
启动docker
[root@Docker2 ~]# systemctl daemon-reload
[root@Docker2 ~]# systemctl restart docker
[root@Docker2 ~]# etcdctl ls /coreos.com/network #这两个文件是
/coreos.com/network/config
/coreos.com/network/subnets #记录节点分配给容器的网段
[root@Docker1 ~]# etcdctl ls /coreos.com/network/subnets
/coreos.com/network/subnets/10.0.19.0-24
/coreos.com/network/subnets/10.0.17.0-24
————————————————————可以查看两台主机的subnet网段
[root@Docker1 ~]# etcdctl get /coreos.com/network/subnets/10.0.19.0-24
{"PublicIP":"192.168.2.17","BackendType":"vxlan","BackendData":{"VtepMAC":"2e:10:9c:11:b7:58"}}
[root@Docker1 ~]# etcdctl get /coreos.com/network/subnets/10.0.17.0-24
{"PublicIP":"192.168.2.1","BackendType":"vxlan","BackendData":{"VtepMAC":"5a:ca:20:2c:df:b7"}}
?在docker1、2都开启路由功能
[root@Docker1 ~]# vim /etc/sysctl.conf
........内容加入到最后
......
net.ipv4.ip_forward=1 #开启路由功能
保存
[root@Docker1 ~]# systemctl restart network
三、验证容器互通
[root@Docker1 ~]# docker run -it busybox
..........
....
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
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@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue
link/ether 02:42:0a:00:13:02 brd ff:ff:ff:ff:ff:ff
inet 10.0.19.2/24 brd 10.0.19.255 scope global eth0
valid_lft forever preferred_lft forever
[root@Docker2 ~]# docker run -it busybox
............
.....
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
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,M-DOWN> mtu 1450 qdisc noqueue
link/ether 02:42:0a:00:11:03 brd ff:ff:ff:ff:ff:ff
inet 10.0.17.3/24 brd 10.0.17.255 scope global eth0
valid_lft forever preferred_lft forever
进行ping测试
/ # ping 10.0.17.3
PING 10.0.17.3 (10.0.17.3): 56 data bytes
64 bytes from 10.0.17.3: seq=0 ttl=62 time=0.391 ms
64 bytes from 10.0.17.3: seq=1 ttl=62 time=2.538 ms
64 bytes from 10.0.17.3: seq=2 ttl=62 time=1.073 ms
^C
--- 10.0.17.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.391/1.334/2.538 ms
——————————————————————————————————————
/ # ping 10.0.19.2
PING 10.0.19.2 (10.0.19.2): 56 data bytes
64 bytes from 10.0.19.2: seq=0 ttl=62 time=0.398 ms
64 bytes from 10.0.19.2: seq=1 ttl=62 time=0.363 ms
64 bytes from 10.0.19.2: seq=2 ttl=62 time=0.431 ms
^C
--- 10.0.19.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.363/0.397/0.431 ms
四、配置backend为host-gw
host-gw bakcend是flannel的另一个backend。与vxlan不同,host-gw不会封装数据包,而是在主机的路由表中创建到其他主机的subnet的路由条目,从而实现容器网络跨主机通信。需要说明的是,host-gw不能跨宿主机网络通信,或者说跨宿主机网络通信需要物理路由支持。性能最好
?在docker1上进行操作
[root@Docker1 ~]# jobs -l
[1]+ 19543 运行中 /usr/bin/flanneld --etcd-endpoints="http://192.168.2.17:2379" --iface=192.168.2.17 --etcd-prefix=/coreos.com/network &
[root@Docker1 ~]# kill -9 19543
[root@Docker1 ~]# etcdctl set /coreos.com/network/config '{"Network": "10.0.0.0/16", "SubnetLen": 24, "SubnetMin": "10.0.10.0","SubnetMax": "10.0.20.0", "Backend": {"Type": "host-gw"}}'
{"Network": "10.0.0.0/16", "SubnetLen": 24, "SubnetMin": "10.0.10.0","SubnetMax": "10.0.20.0", "Backend": {"Type": "host-gw"}}
———————————————————— 删除以分配的网段
[root@Docker1 ~]# etcdctl ls /coreos.com/network/subnets
/coreos.com/network/subnets/10.0.19.0-24
/coreos.com/network/subnets/10.0.17.0-24
[root@Docker1 ~]# etcdctl rm /coreos.com/network/subnets/10.0.19.0-24
PrevNode.Value: {"PublicIP":"192.168.2.17","BackendType":"vxlan","BackendData":{"VtepMAC":"2e:10:9c:11:b7:58"}}
[root@Docker1 ~]# etcdctl rm /coreos.com/network/subnets/10.0.17.0-24
PrevNode.Value: {"PublicIP":"192.168.2.1","BackendType":"vxlan","BackendData":{"VtepMAC":"5a:ca:20:2c:df:b7"}}
——————————————————————-启动flanneld
[root@Docker1 ~]# /usr/bin/flanneld --etcd-endpoints="http://192.168.2.17:2379" --iface=192.168.2.17 --etcd-prefix=/coreos.com/network &
??在docker2上进行操作
[root@Docker2 ~]# jobs -l
[1]+ 91690 运行中 /usr/bin/flanneld --etcd-endpoints="http://192.168.2.17:2379" --iface=192.168.2.17 --etcd-prefix=/coreos.com/network &
[root@Docker2 ~]# kill -9 91690
[root@Docker2 ~]# /usr/bin/flanneld --etcd-endpoints="http://192.168.2.17:2379" --iface=192.168.2.1 --etcd-prefix=/coreos.com/network &
?重启docker
[root@Docker1 ~]# systemctl restart docker
[root@Docker2 ~]# systemctl restart docker
ping测试
[root@Docker1 ~]# docker run -it busybox
/ # ping 10.0.17.2
PING 10.0.17.2 (10.0.17.2): 56 data bytes
64 bytes from 10.0.17.2: seq=0 ttl=62 time=0.505 ms
64 bytes from 10.0.17.2: seq=1 ttl=62 time=0.487 ms
64 bytes from 10.0.17.2: seq=2 ttl=62 time=0.604 ms
^C
--- 10.0.17.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.487/0.532/0.604 ms
——————————————————————————————————————————————————————————————————————————
[root@Docker2 ~]# docker run -it busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
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
13: eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue
link/ether 02:42:0a:00:11:02 brd ff:ff:ff:ff:ff:ff
inet 10.0.17.2/24 brd 10.0.17.255 scope global eth0
valid_lft forever preferred_lft forever
可以在宿主机上查看到路由条目(重新打开一个窗口)
#可以看到10.0.17.0/24 的下一跳就是 192.168.2.254,其中间就少了封装与解封装的过程
[root@Docker1 ~]# ip route
default via 192.168.0.1 dev ens37 proto static metric 100
default via 192.168.2.254 dev ens33 proto static metric 101
10.0.17.0/24 via 192.168.2.1 dev ens33
10.0.19.0/24 dev docker0 proto kernel scope link src 10.0.19.1
192.168.0.0/24 dev ens37 proto kernel scope link src 192.168.0.9 metric 102
192.168.2.0/24 dev ens33 proto kernel scope link src 192.168.2.17 metric 100
————————————————————————————————————————————————————
/ # traceroute 10.0.17.2 #可以进行抓包查看
traceroute to 10.0.17.2 (10.0.17.2), 30 hops max, 46 byte packets
1 10.0.19.1 (10.0.19.1) 0.005 ms 0.004 ms 0.004 ms
2 192.168.2.1 (192.168.2.1) 0.259 ms 0.843 ms 0.251 ms
3 10.0.17.2 (10.0.17.2) 0.432 ms 0.451 ms 0.399 ms
?
|