K8S网络通信细节

一、网络必要基础知识

IP地址标准计算法

计算IP
192.168.20.125/27
IP/子网掩码长度

192.168.20.125
第一步 拆解IP地址
11000000 10101000 00010100 01111101

第二步 根据子网掩码长度 将拆解的ip分段
11000000 10101000 00010100  011/11101
                        网络位 /主机位
                    
第三步 直接计算IP地址
网段地址:网络位不变 主机位全为0    11000000 10101000 00010100  011/00000  192.168.20.96
广播地址:网络位不变 主机位全为1    11000000 10101000 00010100  011/11111  192.168.20.127
有效地址范围:192.168.20.97~192.168.20.126
子网掩码:网络位全为1 主机位全为0
11111111 11111111 11111111  111/00000 ---> 255.255.255.224

表示方法有两种
1) 192.168.20.125/27
2) 192.168.20.125 255.255.255.224

二、K8S网络通信分类

集群内

  • Pod中container和container之间通信(Container<->Container)

在同一个pod内部的不同容器所处在同一个Network Namespace,所以pod内部的container间的通信通过localhost即可完成。

  • Pod和Pod之间通信(Pod<->Pod)

pod和pod之间的通信,一般来说所有的pod在K8S系统网络规划的时候让其处在同一个CIDR下,跨主机的pod通信需要依赖CNI来实现,CNI一般有两大类基于隧道基于路由, 基于隧道的代表Flannel,基于路由的代表Calico。

  • Pod和Service之间通信(Pod<->Service)

Pod的Ip通过veth生成的虚拟网络设备来作为网络接口,而ServiceIp是Ipvs或者Iptables规则虚拟出来的Ip,没有网络接口。

集群外

  • 外部应用和服务之间通信
  • 同一个Pod之间的通信
  • 不同Pod中容器间的通信

三、分层网络模型Overlay和Underlay

为什么K8S要设计Underlay和Overlay两层网络结构?

  1. 可分配ip数目依赖于underlay网络的网络规划,有可能可用ip数目很少;

  2. 跨网段通讯需要nat或其它映射技术的支持;

  3. underlay网络ip的变化有可能会导致pod ip的变化;

基于Flannel作为K8S插件解释

Calico和Flannel对比

模式 Overlay 主要基于隧道 主要基于路由 性能损失 方式
Calico No No Yes IPIP(隧道)、BGP、RR
Flannel Yes Yes No UDP、VXLAN、Host-GW

Flannel的模式(基于隧道的网络实现):UDP、VXLAN、Host-GW三种模式,VXLAN是比较推荐的模式。

模式 应用场景 实现网络层
UDP 一般用于不支持VXLAN的内核服务器 二层网络支持(同广播域)
VXLAN 推荐一般性场景 三层网络支持
Host-GW 网络性能要求高 二层网络支持(同广播域)

如图中所示:

(1)给Node1以100.12.24.1/24作为其podIp的地址池、Node2以100.12.25.1/24、Node3以100.12.26.1/24,每台服务器可以运行pod为254个;
(2)192.168.0.0/24处于同一个二层网络或者三层空间中,属于Underlay网络,该网络的服务器可以通过二层网络直接通信;
(3)Flannel的VXLAN为一种隧道协议帮助跨主机间的Overlay网络通信,比如当Pod 100.12.24.2需要和100.12.26.3通信时,Flannel负责将容器中的网络报文封装成宿主机Node的报文,通过宿主机之间的隧道完成通信。
(4)VXLAN才有L2 over L4的封装模式,将二层报文用三层协议进行封装,可以实现二层网络在三次网络内扩展,形成一个大二层网络。

另外,在分布式pod中,K8S上的所有pod默认会从同一平面网络得到全局一个唯一IP地址和一个虚拟网络接口,无论是否都处于一个namespace,各个pod之间都可使用各自的IP地址直接进行通信。 为了满足分布式pod必须位于同一个平面网络内。对于一个K8S集群来说,容器数量是很多的,为了避免各个容器间网络的冲突,一个常见的解决方案是给每个宿主机分配同一个网络中的不同子网,再让宿主机根据自有子网向其内容器分配ip,如下:

K8S集群分配子网网段给Node --> Node根据划分的网段分配Ip给当前宿主机的Pod

因此整个K8S就会存在三种IP:

(1)node ip: 是物理机IP(或虚拟机ip)。每个service都会在node节点上开通过一个端口,外部可以通过http://nodeip:nodeport即可访问service里的pod提供的服务。
(2)cluster ip:是service的IP地址,此为虚拟ip地址,外部网络无法ping通,只有k8s集群内部访问使用;cluster ip仅仅作用于k8s service这个对象,并由k8s管理和分配IP地址;cluster ip无法被ping,他没有一个“实体网络对象”。单独的cluster ip不具备通信的基础,并且他们属于k8s集群这样一个封闭的空间;在不同service的pod节点在集群间相互访问可以通过cluster ip。
(3)pod ip:是每个pod的IP地址,一般由网络插件提供配置(flannel),通常是一个虚拟的二层网络。同service下的pod可以直接根据podip相互通信;不同service下的pod在集群间pod通信要借助于cluster ip;

四、集群地址分配

查看集群可使用IP范围、端口范围和集群地址池

kubectl cluster-info dump | egrep "service-cluster-ip-range|service-node-port-range|cluster-pool-ipv4-cidr"

得到:

                            "--service-cluster-ip-range=10.233.0.0/18",
                            "--service-node-port-range=30000-32767",
                            "--service-cluster-ip-range=10.233.0.0/18",
                            "--service-node-port-range=30000-32767",
                            "--service-cluster-ip-range=10.233.0.0/18",
                            "--service-node-port-range=30000-32767",
                            "--cluster-cidr=10.233.64.0/18",
                            "--service-cluster-ip-range=10.233.0.0/18",
                            "--cluster-cidr=10.233.64.0/18",
                            "--service-cluster-ip-range=10.233.0.0/18",
                            "--cluster-cidr=10.233.64.0/18",
                            "--service-cluster-ip-range=10.233.0.0/18",
level=info msg="  --cluster-pool-ipv4-cidr='10.0.0.0/8'" subsys=cilium-operator-generic

  • service-cluster-ip-range,表示集群Service可用IP地址数量的计算方式:10.233.0.0/18掩码18位【这个段我们理解为完整Overlay网络】,那么掩码就是 255.255.192.0( 11111111 11111111 11000000 00000000),第三段的范围就是 0 ~ 2^6-1,那么范围就是10.233.0.1 ~ 10.233.63.255,总共可用IP地址为2^14-2(16382)个地址。
  • service-node-port-range,表示服务端口范围。
  • cluster-cidr,表示为Pod分配的地址范围,然后节点从10.233.64.0/24、10.233.65.0/24、10.233.66.0/24 … 依次分配。

查看各个节点的IP地址池范围

通过此命令:kubectl get nodes -o yaml | egrep "io.cilium.network|kubernetes.io/hostname" 得到我们有三个Master和三个Node节点,他们各自的节点上pod可以分配如下所示:

      io.cilium.network.ipv4-cilium-host: 10.233.64.15
      io.cilium.network.ipv4-health-ip: 10.233.64.230
      io.cilium.network.ipv4-pod-cidr: 10.233.64.0/24
      kubernetes.io/hostname: cd.ops.k8s-master-01
      io.cilium.network.ipv4-cilium-host: 10.233.65.217
      io.cilium.network.ipv4-health-ip: 10.233.65.119
      io.cilium.network.ipv4-pod-cidr: 10.233.65.0/24
      kubernetes.io/hostname: cd.ops.k8s-master-02
      io.cilium.network.ipv4-cilium-host: 10.233.66.34
      io.cilium.network.ipv4-health-ip: 10.233.66.91
      io.cilium.network.ipv4-pod-cidr: 10.233.66.0/24
      kubernetes.io/hostname: cd.ops.k8s-master-03
      io.cilium.network.ipv4-cilium-host: 10.233.69.246
      io.cilium.network.ipv4-health-ip: 10.233.69.171
      io.cilium.network.ipv4-pod-cidr: 10.233.69.0/24
      kubernetes.io/hostname: cd.ops.server.k8s-node-01
      io.cilium.network.ipv4-cilium-host: 10.233.68.214
      io.cilium.network.ipv4-health-ip: 10.233.68.91
      io.cilium.network.ipv4-pod-cidr: 10.233.68.0/24
      kubernetes.io/hostname: cd.ops.server.k8s-node-02
      io.cilium.network.ipv4-cilium-host: 10.233.67.133
      io.cilium.network.ipv4-health-ip: 10.233.67.235
      io.cilium.network.ipv4-pod-cidr: 10.233.67.0/24
      kubernetes.io/hostname: cd.ops.server.k8s-node-03

我们用cd.ops.server.k8s-node-01这个节点举例10.233.69.0/24,那么在这个节点上的pod可用地址从10.233.69.2-10.233.69.255,共计 【254 】个可用地址,也就是在此节点运行的pod上限就是254个,其他节点一样。 根据上面的cluster-cidr=10.233.64.0/18,可以知道所有POD的Ip范围是从10.233.64.0-10.233.127.255,也就意味着集群的Node只能从64-127,共计能够容纳64个节点