k8s 网络基础

2022-03-16  本文已影响0人  真之棒2016

author:sufei
说明:本文主要记录在学习k8s网络方面的相关知识


一、Linux虚拟网络基础

1.1 网络命名空间

 Linux在内核网络栈中引入网络命名空间,将独立的网络协议栈隔离到不同的命令空间中,彼此间无法通信;

Network Namespace.png
这是网络虚拟化的基础,有几点需要说明:

1、Linux操作系统,解析和封装网络包是通过一个网络协议栈完成,下层为上层服务,这个协议栈中即包括如软件也包括硬件网络设备。网络命名空间就是以软件方式隔离出单独的网络栈信息;

2、不同network namespace的软硬件资源相互不可见,好像处在物理隔离的不同物理机上一样,彼此隔离;

3、不同的网络命名空间会有自己独立的网卡、路由表、ARP 表、iptables 等和网络相关的资源

4、实验:可以借助ip netns命令来完成对 Network Namespace 的各种操作,如:

$ ip netns help
Usage: ip netns list
       ip netns add NAME
       ip netns set NAME NETNSID
       ip [-all] netns delete [NAME]
       ip netns identify [PID]
       ip netns pids NAME
       ip [-all] netns exec [NAME] cmd ...
       ip netns monitor
       ip netns list-id

# 创建Network Namespace
$ ip netns add ns0
# 在新的Network Namespace 创建网卡信息
$ ip netns exec ns0 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# 开启回环网卡
$ ip netns exec ns0 ip link set lo up

1.2 Veth设备对(转移设备)

问题:什么是转移设备?

 可以在不同的 Network Namespace 之间转移设备(如veth)。由于一个设备只能属于一个 Network Namespace ,所以转移后在这个 Network Namespace 内就看不到这个设备了。veth设备属于可转移设备,而很多其它设备(如lo、bridge等)是不可以转移的。

通过转移设备veth,我们可以实现不同网络命名空间的联通。类似于两条物理机之间通过网线两端互联。

 veth pair 全称是 Virtual Ethernet Pair,是一个成对的端口,所有从这对端口一 端进入的数据包都将从另一端出来,反之也是一样。而veth pair就是为了在不同的 Network Namespace 直接进行通信,利用它可以直接将两个 Network Namespace 连接起来。

veth pair.png

实验

# 创建veth pair
$ sudo ip link add type veth
$ ip addr
61: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether e6:39:e1:e0:3a:a0 brd ff:ff:ff:ff:ff:ff
62: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether be:41:49:42:23:6a brd ff:ff:ff:ff:ff:ff

# 指定 veth pair 两个端点的名称
$ ip link add vethfoo type veth peer name vethbar

# 实现Network Namespace间通信
# 1、创建另一个网络命名空间
$ ip netns add ns1
$ ip netns list
ns1
ns0
# 2、分别将veth两端移动到对应网络命名空间
$ ip link set veth0 netns ns0
$ ip link set veth1 netns ns1
# 3、分别为这对veth pair配置上ip地址,并启用它们
$ ip netns exec ns0 iplink set veth0 up
$ ip netns exec ns0 ip addr add 10.0.1.1/24 dev veth0
$ ip netns exec ns1 iplink set veth1 up
$ ip netns exec ns1 ip addr add 10.0.1.2/24 dev veth1
# 4、尝试在ns1中访问ns0中的ip地址
$ ip netns exec ns1 ping -c 3 10.0.1.1
PING 10.0.1.1 (10.0.1.1) 56(84) bytes of data.
64 bytes from 10.0.1.1: icmp_seq=1 ttl=64 time=0.091 ms
64 bytes from 10.0.1.1: icmp_seq=2 ttl=64 time=0.035 ms
64 bytes from 10.0.1.1: icmp_seq=3 ttl=64 time=0.037 ms

--- 10.0.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.035/0.054/0.091/0.026 ms

1.3 网桥

 veth pair打破了 Network Namespace 的限制,实现了不同 Network Namespace 之间的通信。但veth pair有一个明显的缺陷,就是只能实现两个网络接口之间的通信。如果我们想实现多个网络接口之间的通信,就可以使用下面介绍的网桥(Bridge)技术(类似于物理交换机)。
 简单来说,网桥就是把一台机器上的若干个网络接口“连接”起来。其结果是,其中一个网口收到的报文会被复制给其他网口并发送出去。以使得网口之间的报文能够互相转发。

 网桥是一个二层网络设备,通过网桥可以将linux支持的不同的端口连接起来,并实现类似交换机那样的多对多的通信。

bridge.png

实验:

# 创建网桥
brctl addbr br0 
# 绑定网口
# 让eth0 成为br0 的一个端口
brctl addif br0 eth0  

1.4 Iptables/Netfilter

 Netfilter负责在内核中执行各种挂接的规则(过滤、修改、丢弃等),运行在内核 模式中;Iptables模式是在用户模式下运行的进程,负责协助维护内核中Netfilter的各种规则表;通过二者的配合来实现整个Linux网络协议栈中灵活的数据包处理机制。

 iptables/netfilter(简称iptables)组成了Linux平台下的包过滤防火墙,可以完成封包过滤、封包重定向和网络地址转换(NAT)等功能。这部分主要了解两部分知识:

消息处理链

 应用层不管是要发送还是接收网络消息,都需要通过linux内核提供的一系列关卡。每个”关卡“担负着不同的工作。这里的”关卡“被称为”链“。如下图:

iptables

规则表

我们在上面的消息处理链上定义规则,规则一般的定义为:如果数据包头符合这样的条件,就这样处理“。这些规则并不是严格按照添加顺序排列在一张规则表中,而是按照功能进行分类,存储在不同的表中,每个表存储一类规则:

1.5 总结

二、Docker网络模式

当我们安装docker后,在宿主机上就存在一个docker0的网卡信息,其实他就是一个网桥设备。
[11:20:10 teledb@ubuntu1804 ~]$ ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:7ff:fe5e:97e4  prefixlen 64  scopeid 0x20<link>
        ether 02:42:07:5e:97:e4  txqueuelen 0  (Ethernet)
        RX packets 5866985  bytes 55416110112 (55.4 GB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 9649674  bytes 15424321793 (15.4 GB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

 Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关(如上面的172.17.0.1)。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。

 Docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法通过直接Container-IP访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主主机(端口映射),即docker run创建容器时候通过 -p 或 -P 参数来启用,访问容器的时候就通过[宿主机IP]:[容器端口]访问容器。

 下面具体来说说docker容器的几种网络模式,以便后续学习k8s网络。

2.1 host模式

docker host.png

 在host模式下( –net=host),容器不会去建立新的网络命名空间,而直接使用宿主机的网络设备以及网络协议栈。这样自然不会虚拟出自己的网卡,配置自己的IP等。其特点如下:

2.2 container模式

docker container.png

 这个模式就是在创建容器时,指定网络(–net=container:NAME_or_ID)与之前容器在同一个网络命名空间中,而不是和宿主机共享(这也就是k8s中pod内各容器的一种网络模式)。下面说明几点:

2.3 none模式(很少使用吧)

docker none.png

 none模式(–net=none)Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。

2.4 bridge模式

docker bridge.png

 bridge模式是docker容器的默认模式,当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器在bridge模式下会连接到这个虚拟网桥上,并由网桥自动分配ip。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。

 下面说明这个模式下的工作方式:

  1. 从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关;
  2. 在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中;
  3. 实现端口转发功能则采用在iptables做了DNAT规则。

三、k8s网络实现

 首先我们来看看k8s想要一个什么样的网络,也就是k8s网络设计的要求,具体如下:

  • 每个 Pod 都拥有一个独立的 IP 地址,且不冲突;
  • 所有 Pod 都在一个可以直接连通的、扁平的网络空间中(不管它们是否在同一个 Node(宿主机)中);

 下面简单从几中不同的通信要求来看看k8s网络实现。

3.1 Pod内容器间通信

k8s pod内通信.png

 在 Kubernetes 的世界里,IP 是以 Pod 为单位进行分配的。一个 Pod 内部的所有容器共享一个网络堆栈。实际上就是docker container网络模式。可以直接通过本地localhost进行网络访问。这个模式在mysql容器化中就是agent容器与mysql容器的网络通信方式。

3.2 Pod直接通信

3.2.1 同一个Node

k8s pod间通信(同node).png

 Pod1和Pod2都是通信veth pair连接到同一个docker0网桥上,它们的IP地址都是从docker0网段上动态获取的,它们和网桥本身的IP是同一个网段的。可以通过docker0作为交换机进行通信,也就是采用的docker bridge网络模式进行通信。

3.2.3 不同Node

 由于在同一个网桥docker0上即可以保证分配的pod IP不会冲突,且可以相互通信,而如果需要跨Node物理节点,则无法通过docker网络直接满足要求了,那这些要求具体有哪些呢?

解决方案

方法一:k8s中通过在etcd中记录正在运行中pod的IP分配信息,这样我们就可以满足Pod IP与Node IP之间映射关系的记录;

方法二:可以在etcd中规划配置好所有主机docker0网桥的子网范围,从而满足Pod IP不冲突的要求;如:

节点A:10.0.1.0/24
节点B:10.0.2.0/24
节点C:10.0.3.0/24

方法三:要实现Pod跨Node通信,以k8s默认网络Flannel为例,就是采用overlay(覆盖网络)实现。具体下面说明:

问题:什么是覆盖网络?

覆盖网络就是应用层网络,是指建立在另一个网络上的网络。怎么理解呢?简单理解就是将TCP数据包装在另一种网络包里面进行路由转发和通信,另一种网络包目前可以是UDP、VxLAN、AWS VPC和GCE路由等数据转发方式。默认以UDP为例来说明flannel工作方式。

下面看看具体实现

k8s pod通信(不同node).png
  1. 数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡;
  2. flanneld服务监听在网卡的另外一端。Flannel通过Etcd服务维护了一张节点间的路由表;
  3. 源主机的flanneld服务将原本的数据内容UDP封装;
  4. 根据自己的路由表投递给目的节点的flanneld服务,数据到达以后被解包;
  5. 然后直接进入目的节点的flannel0虚拟网卡,然后被转发到目的主机的docker0虚拟网卡,最后就像本机容器通信一下的有docker0路由到达目标容器。这样整个数据包的传递就完成了。

问题:为保证各node内docker容器分配的ip地址不冲突,每个节点上的Docker会使用不同的IP地址段?如何实现的呢?

其实只是单纯的因为Flannel通过Etcd分配了每个节点可用的IP地址段后,偷偷的修改了Docker的启动参数,即通过--bip=10.1.15.1/24。它限制了所在节点容器获得的IP范围。这个IP范围是由Flannel自动分配的,由Flannel通过保存在Etcd服务中的记录确保它们不会重复。

问题:为什么在发送节点上的数据会从docker0路由到flannel0虚拟网卡,在目的节点会从flannel0路由到docker0虚拟网卡?

这其实是通过路由表实现的,如在目标node与源node执行route -n结果如下:

# 源主机
$ route -n
Destination     Gateway         Genmask Flags Metric Ref    Use Iface  
10.1.0.0     *               255.255.0.0     U     0      0        0 flannel0
10.1.15.0     *              255.255.255.0   U     0      0        0 docker0

# 目标主机
$ route -n
Destination     Gateway         Genmask Flags Metric Ref    Use Iface  
10.1.0.0     *               255.255.0.0     U     0      0        0 flannel0
10.1.20.0     *              255.255.255.0   U     0      0        0 docker0
上一篇 下一篇

猜你喜欢

热点阅读