docker单机网络+跨主机网络知识梳理(Flannel)

docker单机网络+跨主机网络知识梳理

前言

kubernetes和docker在最近几年特别火热,很多大型互联网公司已经大范围使用了,中小型公司也在逐渐过度和转变。并且随着技术发展,互联网的知识也变得更加容易获取,但是也带来了我们浮躁和不愿意思考的坏毛病,比如看到一个文章说:docker如何跨主机通信?这边bala  bala bala方案就列了出来:原理,架构,部署,甚至排错。而我们大多数初学者可能没有接触到生产环境docker单机网络的痛点或者限制。只是知道我们一定会在生产环境用跨主机方案。我相信大多数人会有一个疑惑:明明我已经掌握了xx技术可是还是不能灵活运用。其实这就是跨度学习的问题,知识毕竟是连贯起来的,任何一个技术的推动,发展和改进都是针对业务上的不足和缺陷,如果不了解这个过程,心中还是存在疑惑和不解。所以今天和大家通过实验来梳理下docker单机网络过渡到跨主机网络的过程。

docker单机网络通信

docker单机网络通信就是单机中容器和容器的通信方式,这里总共有四种模式,我们可以按照namespace是否独立来区分,而区分是namespace是否独立可以按照两个设备之前是否共用一个ip( 这里面需要强调两个设备既可以宿主机和容器,也可以容器和容器。

1.bridge(docker默认网络方式,独立namespace因为容器内ip和宿主机ip不同)

bridge模式就是容器通过一对veth 设备 一端连接着容器eth0网卡,一端桥接到宿主机桥接到docker0网卡上,下图可以看到,一个容器对应一个veth设备。

这里我开了一个容器,在网卡中看到了veth设备

docker inspect 容器 名查看详细信息

2.none   (网卡信息,ip都需要自己配置,拥有独立的namespace)

3.host    (和宿主机共用一个namespace,共用ip,端口)

这里主要提一下host模式是可以实现docker跨主机通信,但是生产环境下我们不会用,自己测试或者开发环境为了方便可以用。因为用了host模式,容器端口的映射就没意义了,同一namespace下容器用的端口都是宿主机的,如果此时宿主机跑了nginx占用80端口,你这台机器容器就没法跑web服务,因为没法端口映射,因为这就是真实的端口占用问题。而拥有独立的namspace是可以进行端口映射的。

4.container   (和其他容器共用一个namespace,共用一个ip和端口)

到底为什么要跨主机通信?

1.大前提就是因为我们的容器不可能只放在一台主机,当需要彼此之间通信的容器分布在多个主机上时就上升到了跨主机通信。

2.如果不做任何设置,我们容器间跨主机通信只能通过访问宿主机ip+映射端口的形式。当容器和服务特别多的时候,极大浪费了网络资源,并且管理不便。

单机跨主机方式验证

有很多小伙伴刚学docker网络常遇到这样一个误区:比如node-1上的docker0网段是172.17.0.1 ,node-2上docker0网段也是172.17.0.1,然后他修改了node-2的docker0网段变成了172.17.1.1这样两个宿主机的docker0网段在同一网段,两个主机的docker0网段可以通信,而容器又通过docker0网段通信,这不就实现了跨主机通信么?

  • 回答

实际不是这样的,docker0网段实际只是虚拟的,不能像上述那样,误认为只要两个宿主机docker0网段在同一网段就可以通信。跨主机容器的通信最终流量的入口还是宿主机的真实ip,然后通过转发以及端口映射到docker0上然后通过docker0进而和目的主机通信。

  • 验证

test-1创建了nginx 容器 ,80:80端口映射  容器ip:172.17.0.2

网卡信息如下:

test-2创建了一个centos7镜像(下载了telnet)容器ip:172.17.1.2

test-2容器ip:

test-2网卡信息:

二者docker0都是172.17网段,测试在test-2的centos容器用直接telnet ping test-1 nginx 容器

为什么修改默认的docker0网段?

备注:这种情况实际上是跨主机通信时仍然用的主机ip+端口映射,如果做了网络覆盖是不会出现的,都是独立的ip.大二层网络

原因:因为我们内网的宿主机ip网段有一部分有172.17.x.x 有一部分是172.16.x.x 。而容器默认docke0也是172.17.0.1 会和自己的主机ip冲突,导致ip  forword转发失效,而这里的转发就是把发给宿主机ip的请求,转发到docker0上进而和容器通信。

解决:修改docker默认端口有很多方式,但是最简单方式参考我的这个文章 https://wangtingwei.info/?p=184

如何跨主机通信?有什么方案?

docker容器跨主机通信实现的工具有Pipework、flannel 、Weave 、Calico、xvlan、openVswitch(虚拟交换机)

  • weave

实际上就是在每个宿主机都部署一个特殊的route容器,将不同宿主机的route容器连接起来,此时route容器会拦截所有容器的Ip请求,并通过udp包发送到其他宿主机的容器上,这样在跨主机的多个容器端就实现了在同一个扁平网络。但是weave部署是单机的,性能也没有其他模式好,目前已经被淘汰了。

  • flannel

flannel是CoreOS团队针对kubernetes设计的一个网络规划服务,他的作用就是让集群中的不同节点主机创建的docker容器拥有在集群中唯一的虚拟ip地址,底层通过udp/vxlan进行报文的封装和转方这就解决了刚才我们说的修改默认docker0网桥的问题,此时不再是各自docker0网卡给容器分配ip。而是统一由flannel规划,他能够做到让集群中的容器同属一个内网且重复的ip地址,并让不同节点之间的容器通过这个虚拟的内网ip通信。

flannel实际上是一种覆盖网络(overlay network),就是将一个网络(应用层)覆盖在原有网络上,这个网不依靠ip地址传递消息,而是采用映射机制把ip地址和identifiers做映射来资源定位。也就是将tcp数据包封装在另一种网络包里进行路由转发和通信。目前支持udp vxlan aqs vpc gce 路由等数据转发方式。

flannel默认使用8285端口作为udp封装报文的端口,vxlan使用8472端口

图解:

1.容器直接向目标容器的ip发起请求,默认通过该容器的eth0端口发送出去。

2.报文通过vethpair发送到vethxxx设备,由于vethxxx设备桥接在docker0网卡上,进而发送给docker0网卡上。然后从flannel虚拟网卡出去

3.查找etcd的路由表,得到路由规则,由于外部容器的ip报文都会转发到flannel0虚拟网卡,这是一个p2p的网卡。报文会根据路由规则转发到监听在另一端的flanneld

4.flanneld通过ETCD来维护各个节点之间的路由表,把原来的报文进行udp封装再通过宿主机网卡发送出去

5.报文通过主机之间的网路,找到了目标主机,然后继续向上传输交给监听再8285端口的flanneld程序处理,

6.数据被解包,然后发送给flannel0虚拟网卡

7.查找路由表,发现目标ip归属于docker0网卡,将报文发送给docker0网卡。

8.docker0网卡根据目标容器ip,将请求通过vethxxx发送给目标容器的eth0网卡。

总结:flannel0虚拟网卡查路由表,flanneld程序用来udp封装和解封。flannel是p2p网卡会通过udp封装的报发送给对端的flanneld程序上解包

思考:

1.为什么每个节点的docker0会使用不同的ip地址段

flannel通过etcd分配了每个节点的可用的ip地址段,偷偷修改了docker的启动参数。所以配置好要想生效需要重启docker。再flannel节点上之执行ps  aux |grep docker |grep “bip” 例如 –bip=182.48.25.1/24这个参数,他限制了所在节点容器获得的ip范围。这个范围是flannel自动分配的,由flannel通过保存在etcd中的记录进行协调分配,确保每个节点的容器ip不会重复

  • pipework

pipework是一个单机工具,结合了brctl等工具,可以认为pipework解决的是设置宿主机上容器的虚拟网卡,网桥,ip可以配合其他网络使用

如果容器数量不多,想要组件一个简单的大三层网络可以考虑weave。

如果容器数量很多,而且环境复杂,需要多个子网,可以考虑open vswitch或者flannel。

weave总体网络性能差,flanel vxlan可以满足需求,一般推荐flannel.

另外比较火热的还有calico基于BGP协议的路由方案,我会另写一个博文来介绍.

 

 

 

 

 

 

 

 

 

 

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Loading...