一、简介
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 7月 25 2018 etcd -rwxr-xr-x. 1 1000 1000 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. I | etcdmain: etcd Version: 3.3.9 2022-02-24 20:42:50. I | etcdmain: Git SHA: fca8add78 2022-02-24 20:42:50. I | etcdmain: Go Version: go1.10.3 2022-02-24 20:42:50. 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 8e9e05cd: 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 8e9e05cd: 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:
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:
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:
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:
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"
安装docker
在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:
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:
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:
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:
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:
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:
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
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/221038.html原文链接:https://javaforall.net
