k8s网络原理-总结

2,556 阅读15分钟

linux网络虚拟化

network namespace 作为隔离linux系统中的ip地址,端口,路由表,防火墙规则等,每个network namespace刚开始只有一个lo回环设备(刚开始还是关闭的需要开启),其他设备需要自行去创建

namespace刚开始想要一直存在需要有进程一直在里面运行,后面用/proc/PIC/ns目录下各个namespace文件来关联namespace, 每个文件有对应的链接,链接是用来表示那几个进程共享namespace, 然后这些文件的作用是,当我们打开这些文件对应的namespace就会一直存在

veth pair设备

veth pair是一对网卡设备,可以将其看成网线,一段的流量会到达另一端(不管其namespace是否相同),2个设备都设置好ip后,2个设备就可以互通

linux bridge

顾名思义linux bridge就是linux中的网桥,但是行为更像是网络交换机,任意真是物理设备和虚拟设备都可以连接到linux bridge上,linux bridge不能跨机访问,linux bridge有多个端口,数据可以从任意端口进来,进来后从那个端口出去取决于mac地址,原理和物理交换机差不多

像当veth pair其中一个挂载到bridge上之后,该设备的流量不会直接交给网络协议栈,而是先交给bridge然后由bridge交给网络协议栈,所以2个veth pair互ping的时候,由于ping的是veth地址,但是包又交给了bridge这会导致ping不会返回对应arp的mac地址,所以veth pair设备一端挂载到bridge的时候不需要给他设置ip地址,直接当作网线来用,另一端可以设置ip地址来进行和bridge进行通讯,

同理物理网卡eth0挂载到bridge上的时候也会变成网线,他的数据包都会给网桥,这时候使用它就ping不同网关,因为包给了网桥了,所以这时候eth0也不需要ip了,因为设置了反而影响协议栈的路由选择(比如默认ping的设备)

网卡的混杂模式

在非混杂模式下,网卡只会接收目的mac地址是他的单播多播和广播帧,而混杂模式是会接收经过的所有帧

当veth设备挂载到linux bridge上的时候,linux bridge会自动变成混杂模式,毫无疑问,linux bridge要接收veth的流量,但是veth流量不一定指向bridge,但是veth另一端又会将流量转移到bridge上

tun/tap设备

tun/tap和veth很像,tun/tap设备文件实现内核和用户态数据交互,tun设备通过/dev/tunx(设备文件)收发数据包,所有对这个文件的写操作都会通过tun设备转换成一个数据包发送给协议栈,当想给tun设备发送数据包的时候,tun设备通过读这个文件

tap设备和tun相同,tun设备收发的是ip包,只能在l3层,无法与网卡做桥接,tap设备通过/dev/tapx文件收发链路层包,可以与物理网卡桥接

当ping tun设备的时候,我们将看不到数据包,因为发送的数据包交给了用户态程序

tun设备上一些自带的功能

  • ipip 在ipv4上在封装ipv4
  • GRE 一种网络协议封装成其他网络层协议
  • sit ipv4封装成ipv6
  • ISATAP 自动隧道寻址协议,和sit差不多用作ipv6封装

iptable

iptable的实现原理是netfilter

netfilter

netfilter是一个抽象的框架,提供一套hook函数(网络包回调函数)来提供包过滤,包处理,网络地址转换,透明代理,访问控制,连接跟踪,带宽限制等功能,netfilter框架就是在整个网络框架的若干位置放一些钩子,并在每个钩子上挂载一些处理函数

ip层有5个钩子位置分别是,preRouting, postRouting, input, output, forword

  • preRouting 网络报文刚进入的时候,可以routing到input或者forword
  • input 网络报文要进入本地进程的时候
  • forword 作为中间地位,preRouting和input可以将数据包给他,然后forword可以作为数据转换(转发重定向)
  • postRouting 报文要出去的时候
  • ouput网络报文刚出进程的时候 可以路由到forword或者postRouting

iptable 的 table chain和rule

chain

5条链分别就是netfilter的5个钩子

table

  • filter 用于过滤数据包
  • nat 用于修改原数据包的源地址和目的地址
  • managle 用于修改数据包ip头信息
  • raw iptable是由状态的,有连接追踪,raw是用来去除这种追踪机制的
  • security 在数据包上应用seLinux

有些表可以挂在某些链上,有些不能,因为某些链无法提供这些表规定的功能

rule

rule就是用户进行写的规则,有很多常见的动作

  • drop 数据包丢弃
  • reject 会返回错误的拒绝访问
  • queue 将数据包放入用户空间队列,供用户空间程序处理
  • return 跳出当前链,该链后续规则不再执行
  • accept 同意数据包通过,继续后续规则
  • jump 跳转到其他用户自定义的链继续执行

用户自定义的链不会被hook, 只能又某个链的hook跳转过来

rule挂载到那个table,其实table只是作为rule规则的汇总分类, 真实的还是chain链

vxLan

blog.csdn.net/tony_vip/ar…

总结:

  • vtep 就是一种设备将数据包进行封装,然后到达后解封,解封后继续根据解封的数据包进行寻找设备
  • vni 类似lan id用作虚拟子网分割的,他只是自定义更大的子网分割

总的来讲就是将数据包封装成udp包,通过原和目的ip地址都转换成vtep设备来进行网络通讯,真实的地址等到达vtep设备再来解包进行响应的路由到对应的容器或者虚拟机或者真机

macvlan

就是父接口虚拟出很多子接口,每个子接口可以进行配置ip和mac地址

macvlan的5种模式

  • bridge 子接口可以直接互通,广播帧会被泛洪,类似于网桥,但是子接口不需要学习mac地址,也不用生成树协议,性能优于linux网桥
  • vepa 子接口的流量一股脑发给父接口,子接口通讯会被阻塞,外部交换机需要支持hairpin子接口才能通讯。然后泛洪的流量会给子接口
  • private 类似于vepa完全阻止了子接口通讯,即使开启了hairpin, 具体方式是丢弃广播和多播帧,从而arp协议无法工作
  • passthru 父接口只能和一个子接口捆绑
  • source 寄生在物理设备上,只能接收指定的原mac地址的数据包,其他数据一概丢弃

ipvlan

ipvlan也是虚拟出多个接口,区别是ipvlan只虚拟ip, mac地址都相同,这时候就要注意dhcp分配ip地址,因为dhcp是根据mac地址来分配ip地址

l2模式,l2模式和bridge差不多,父接口作为交换机转发子接口的数据

l3模式,l3模式ipvlan就像交换机,子接口不在一个网段也可以互通,l3模式下不会接收多播和广播报文,因为网络都会发给父接口,所以arp都是由父接口完成的,外部网络默认是不知道ipvlan模拟出来的网络,所以外部路由器不配置好对应的规则, ipvlan是不能被外部网络直接访问的

docker网络

docker网络的4种网络模式

bridge

docker会在宿主机上创建bridge网卡,然后每次启动一个容器都会创建一个veth pair设备,然后一端放入容器中,一端挂载在主机上的bridge上,所有访问容器网段的流量网关都是bridge(容器之间访问也是一样)

外网访问bridge模式的容器时用的是将端口映射,其实本质就是在iptable做了DNAT

然后访问外网其实就是将主机作为路由器(开启ip_forword),然后再iptable上做SNAT/MASQUERADE

host

host模式就是容器和宿主机拥有一样的network namespace,容器不会虚拟出自己的网卡,而是使用宿主机的ip和端口

container

创建的容器需要指向一个容器,表示共享那个容器的network namespace(其他命名空间还是隔离的)

none

容器创建后只有lo的回环网络,这个容器虽然独享network namespace,但是没有网卡ip和路由等信息,需要我们自行配置

DNS和主机名

DNS和主机名一般通过三个系统文件维护,分别是

  • /etc/resolv/conf 再创建容器时候,默认与本地主机/etc/resolv.conf保持一致
  • /etc/hosts 记录了容器容器自身的一些地址
  • /etc/hostname 记录容器的主机名、

容器网络主流有2种解决方案

隧道方案

该网络也称为overlay, 该方案在laas网络应用中也不叫多,overlay网络最大的优点是适用几乎现有的网络基础设施,他唯一的要求就是主机之间互通,典型的overlay的网络插件有

  • weave
  • open vswicth
  • flannel

weave和flannel封包技术有些类似,使用的是vxlan, 另外weave思路是共享ip而非绑定,在传输层先找到目的地址,然后把包发到对端,节点之间互相通过协议共享信息

flannel的udp模式的性能损失在50%以上,因为要在内核和用户态转换多次,而vxlan损失差不多20%-30%

路由方案

封包的目的是无法知道目的地址,没有办法把ip包投递到正确的地方,传统设备路由规则是通过BGP协议部署分布式集群来对包进行传递,对于路由来实现的插件有如下

  • calico 基于BGP的路由方案
  • Macvlan 从逻辑上和内核来看,是隔离性和性能最优的方案,基于二层隔离,需要二层路由器支持,大多数云厂商不支持
  • metaswitch 容器内配置路由指向自己的宿主机,存3层网络,性能接近原生网络

路由方案出问题比较好排查,因为是存粹的路由规则,通过排查各个路由规则即可知问题出在那,但是前提是用户需要了解底层网络基础结构

calico

calico其实是通过在每台宿主机上利用内核的vRoute来进行数据转发,每个vRoute通过BGP把自己节点上的工作负载信息向整个calico网络传播,在集群规模比较大的情况下,可以指定几个节点来完成路由转发的工作(BGP Route Reflector)(好像是通过上报给Reflector,然后Reflector统一转发)

容器网络组网类型

  • overlay网络 在传统网络上模拟一个虚拟网络,基本上这种虚拟网络都要对报文的包头进行封装和剥离

  • L2 overlay L2 overlay是相对于传统网络上的L2, 是一个大二层的概念,其中大的意思是跨多个数据中心(容器可以跨L3 underlay进行L2通讯),而二层的意思就是在一个逻辑的网段内 vxlan是大二层的典型实现,通过udp包封装l2报文

  • L3 overlay L3 overlay组网类似L2 overlay, 会在节点上添加一个网关,每个节点的容器都在一个子网内,可以进行二层通讯,跨节点通讯只能走L3, 都会通过网关转发,性能稍微比L2弱,好处就是容器可以在不同的网段中 flannel的UDP模式采用的就是L3 overlay模型

  • underlay网络 underlay网络一般理解为底层网络,传统网络组网就是underlay

  • L2 underlay 就是链路层互通的网络,ipvlan l2模式,macvlan属于l2 underlay网络

  • L3 underlay ipvlan l3模式,该模式下ipvlan就像路由器一样,在各个虚拟机网络和主机网络之间进行不同的网络报文的路由转发工作,只要父接口相同,不同网段的容器都可以互通, flannel的host-gw模式,calico的BGP的方式都是该类型网络

pod的概念

就是多个容器共享PID(进程id), UTC(主机名),volumn(挂载), network(网络), ipc(多个容器可以用消息队列通讯),pod中还有一个pause容器,其他用户容器其实就是挂载到这个容器中

k8s网络

总结就是2点,关注Ip分配和流量的路由

docker本身的网桥基本是没用的,启动基本都是 --net=none ,cni的插件会自行给容器内创建设备和路由信息

k8s本身的网络方案还是bridge和veth pair, 但是k8s不解决多机器之间通讯,这之间的通讯一般都是交给cni插件来做ip转换和路由,(但是路由类别的集群网络类似calico就是不利用bridge来进行路由而是使用自己的创建的设备来进行操作) ?

pod到pod

pod到pod之间的流量通过ip+端口(不需要端口映射)就可以直接通讯,底层需要cni接口插件来实现

pod到service

service有几种模式

  • ClusterIP 就是类似pod的虚拟ip一样,只能内网流通
  • LoadBalancer 需要厂商支持,会生成一个LoadBalancer外部负载将外部流量交给内部service
  • NodePort 使用节点上的端口来进行暴露
  • Headless 其实就是访问外部服务,endpoint要自行配置没有selector

service其实就是每个节点上kube-proxy通过iptable/ipvs来虚拟出来的一个服务,本身只是路由关系

pod到集群外

一般都是做snat,将ip包进行地址转换,返回时又将ip地址转回来

pod的hosts文件

pod的hosts可以通过k8s的download api来进行设置,不推荐容器启动后自行修改hosts文件

pod的hostname

docker启动时候 --uts=""可以让容器创建的时候使用新的主机名,而--uts="host"则会使用宿主机的主机名,k8s创建pod会做一层判断,判断docker的网络模式如果是hosts就会自动用--uts="host",因为pod内的容器是共享uts的,所以改变其中一个uts其他的也会改变

pause容器

总的作用就是提供各种namespace, 他来使用pid = 1然后收集pod的僵尸进程,在unix中所有进程都是init进程的子进程,当进程没有父进程的时候会将进程交给pid=1的进程来管理,所以pid=1很重要也是为什么pause使用pid=1,其他进程共享pid namespace然后挂载pid=1下, 然后现在k8s默认是不隔离pid的也就是说各个容器都有各自的pid, 因为僵尸进程基本没啥,容器基本不会自己创建进程,然后有些容器就是想要pid=1

cni

cni是k8s为了屏蔽k8s和底层网络插件之间的一个抽象层,2个接口addNetwork和delNetwork其实就是当创建容器和销毁容器的时候调用该接口来进行网络设置

k8s和cni规定了2个默认地址 /etc/cni/net.d cni的配置文件地址 和/opt/cni/bin cni二进制文件的地址,然后还有一个/etc/cni/net.d/10-mynet.conf k8s只读取conf文件,然后k8s启动的时候可以配置-network-plugin=cni来设置启动那个cni组件来初始化网络

cni组件目前还支持带宽控制,但是目前还是用注解来标识实现的,具体底层还是调用linux的tc

service

service总的来说是一个抽象的概念,本质是每个节点上的iptable的路由规则,然后service的标签选择器选中的pod都会对应创建endpoint,然后k8s-dns服务上会有对应域名到ip的映射机制,

service上的3种port

  • port其实就是service对应的port
  • targetPort其实就是对应容器的port
  • nodePort就是对应宿主机上的port

然后headless service其实就是没有selector的service然后自行创建对应的entpoint来进行路由,基本是用来路由到外部服务的

service的访问基本都是通过负载均衡的,当然有时候我们本节点有对应的容器,想要直接访问本地也是可以的,通过设置externamTrafficPolicy来设置只到同一节点上

ingress

ingress是一种l7层功能的,一般用Loadbanlance的service来暴露,当然也可以用service来暴露,他本身是暴露在service之上的,像那种nginx-ingress其实就是启动了一个nginx然后根据各种Ingress的配置来生成ngixn的conf然后提供l7路由的功能

dns

kubernetes启动的时候会启动一个kube-dns的服务,该服务就是提供各种dns查询的,各个pod的dns更新其实是kubelet来进行刷新的,在kube-dns启动后会有一个ip地址,然后对应端口是54, kubelet就会被配置一个--cluster-dns=“dns地址”来进行dns的发现和