kubernetes集群网络

1 kubernetes网络模型

Kubernetes 要求所有的网络插件实现必须满足如下要求:

    一个Pod一个IP
    所有的 Pod 可以与任何其他 Pod 直接通信,无需使用 NAT 映射
    所有节点可以与所有 Pod 直接通信,无需使用 NAT 映射
    Pod 内部获取到的 IP 地址与其他 Pod 或节点与其通信时的 IP 地址是同一个。

2 Docker容器网络模型

网络的命名空间:Linux在网络栈中引入网络命名空间,将独立的网络协议栈隔离到不同的命令空间中,彼此间无法通信;Docker利用这一特性,实现不同容器间的网络隔离。

Veth设备对:Veth设备对的引入是为了实现在不同网络命名空间的通信。

Iptables/Netfilter:Docker使用Netfilter实现容器网络转发。

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

路由:Linux系统包含一个完整的路由功能,当IP层在处理数据发送或转发的时候,会使用路由表来决定发往哪里。

Docker容器网络示意图如下:
kubernetes集群网络

3 Pod网络

问题:Pod是K8S最小调度单元,一个Pod由一个容器或多个容器组成,当多个容器时,怎么都用这一个Pod IP?

实现:k8s会在每个Pod里先启动一个infra container小容器,然后让其他的容器连接进来这个网络命名空间,然后其他容器看到的网络试图就完全一样了。即网络设备、IP地址、Mac地址等。这就是解决网络共享的一种解法。在Pod的IP地址就是infra container的IP地址。
kubernetes集群网络
在 Kubernetes中,每一个Pod都有一个真实的IP地址,并且每一个Pod都可以使用此IP地址与其他 Pod通信。
Pod之间通信会有两种情况:
    两个Pod在同一个Node上
    两个Pod在不同Node上

两个Pod在同一个Node上
同节点Pod之间通信道理与Docker网络一样的,如下图

kubernetes集群网络
1.对 Pod1 来说,eth0 通过虚拟以太网设备(veth0)连接到 root namespace;
2.网桥 cbr0 中为 veth0 配置了一个网段。一旦数据包到达网桥,网桥使用ARP 协议解析出其正确的目标网段 veth1;
3.网桥 cbr0 将数据包发送到 veth1;
4.数据包到达 veth1 时,被直接转发到 Pod2 的 network namespace 中的 eth0 网络设备。

两个Pod在不同Node上
K8S网络模型要求Pod IP在整个网络中都可访问,这种需求是由第三方网络组件实现。

kubernetes集群网络

4 CNI(容器网络接口)

CNI(Container Network Interface,容器网络接口):是一个容器网络规范,Kubernetes网络采用的就是这个CNI规范,CNI实现依赖两种插件,一种CNI Plugin是负责容器连接到主机,另一种是IPAM负责配置容器网络命名空间的网络。

CNI插件默认路径:
# ls /opt/cni/bin/
地址:https://github.com/containernetworking/cni

当你在宿主机上部署Flanneld后,flanneld 启动后会在每台宿主机上生成它对应的CNI 配置文件(它其实是一个 ConfigMap),从而告诉Kubernetes,这个集群要使用 Flannel 作为容器网络方案。

CNI配置文件路径:
/etc/cni/net.d/10-flannel.conflist

当 kubelet 组件需要创建 Pod 的时候,先调用dockershim它先创建一个 Infra 容器。然后调用 CNI 插件为 Infra 容器配置网络。

这两个路径在kubelet启动参数中定义:
 --network-plugin=cni \
 --cni-conf-dir=/etc/cni/net.d \
 --cni-bin-dir=/opt/cni/bin

5 Kubernetes网络组件之Flannel

Flannel是CoreOS维护的一个网络组件,Flannel为每个Pod提供全局唯一的IP,Flannel使用ETCD来存储Pod子网与Node IP之间的关系。flanneld守护进程在每台主机上运行,并负责维护ETCD信息和路由数据包。

5.1 Flannel工作模式及原理

Flannel支持多种数据转发方式: UDP:最早支持的一种方式,由于性能最差,目前已经弃用。 VXLAN:Overlay Network方案,源数据包封装在另一种网络包里面进行路由转发和通信 Host-GW:Flannel通过在各个节点上的Agent进程,将容器网络的路由信息刷到主机的路由表上,这样一来所有的主机都有整个容器网络的路由数据了。 Directrouting:兼顾(vxlan+host-gw)

5.2 VXLAN

默认模式是vxlan

 # kubeadm部署指定Pod网段
kubeadm init --pod-network-cidr=10.244.0.0/16

 二进制部署指定
cat /opt/kubernetes/cfg/kube-controller-manager.conf
--allocate-node-cidrs=true \
--cluster-cidr=10.244.0.0/16 \

#配置文件 kube-flannel.yml
net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
   }

子网分配查看

cat /run/flannel/subnet.env 
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

为了能够在二层网络上打通“隧道”,VXLAN 会在宿主机上设置一个特殊的网络设备作为“隧道”的两端。这个设备就叫作 VTEP,即:VXLAN Tunnel End Point(虚拟隧道端点)。
下图flannel.1的设备就是VXLAN所需的VTEP设备。
kubernetes集群网络

如果Pod 1访问Pod 2,源地址10.244.3.123,目的地址10.244.5.124,数据包传输流程如下:

a.容器路由:容器根据路由表从eth0发出

kubectl run bs1 --image=busybox -- sleep 36000
kubectl run bs2 --image=busybox -- sleep 36000
[root@k8s-master01 ~]# kubectl get pods -o wide
NAME                                      READY   STATUS    RESTARTS   AGE     IP             NODE         NOMINATED NODE   READINESS GATES
bs1                                       1/1     Running   0          11m     10.244.3.123   k8s-node03   <none>           <none>
bs8                                       1/1     Running   0          3m22s   10.244.5.124   k8s-node01   <none>           <none>

[root@k8s-master01 ~]# kubectl exec -it bs1 -- sh
/ # ip route
default via 10.244.3.1 dev eth0 
10.244.0.0/16 via 10.244.3.1 dev eth0 
10.244.3.0/24 dev eth0 scope link  src 10.244.3.123 

b.主机路由:数据包进入到宿主机虚拟网卡cni0,根据路由表转发到flannel.1虚拟网卡,也就是来到了隧道的入口。

[root@k8s-master01 ~]#  ip route
default via 192.168.10.2 dev eth0 
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1 
10.244.3.0/24 via 10.244.3.0 dev flannel.1 onlink 
10.244.5.0/24 via 10.244.5.0 dev flannel.1 onlink 

最后一条规则意思是,凡是发往10.244.5.0/24网段的数据包,都需要经过flannel.1设备发出,并且网关地址是10.244.5.0,即Node2 VTEP设备flannel.1。

c.VXLAN封装:而这些VTEP设备之间组成一个二层网络,但是二层网络必须要知道目的MAC地址,那这个MAC地址从哪获取到呢?其实在flanneld进程启动后,就会自动添加其他节点ARP记录,可以通过ip命令查看,如下所示:

[root@k8s-master01 ~]# ip neigh show dev flannel.1
10.244.5.0 lladdr 4a:41:25:e7:97:f7 PERMANENT
10.244.3.0 lladdr 2e:18:04:0d:d9:62 PERMANENT

d.二次封包:知道了目的MAC地址,Linux内核就可以进行二层封包了。但是,对于宿主机网络来说这个二层帧并不能在宿主机二层网络里传输。所以接下来,Linux内核还要把这个数据帧进一步封装成为宿主机网络的一个普通数据帧,好让它载着内部数据帧,通过宿主机的eth0网卡进行传输。数据格式如下图:

kubernetes集群网络

e.封装到UDP包发出去:在封装成宿主机网络可传输的数据帧时,还缺少目标宿主机MAC地址,也就是说这个UDP包该发给哪台宿主机呢?
flanneld进程也维护着一个叫做FDB的转发数据库,可以通过bridge fdb命令查看:

[root@k8s-master01 ~]# bridge fdb show  dev flannel.1
2e:18:04:0d:d9:62 dst 192.168.10.74 self permanent
3e:6e:50:73:41:0b dst 192.168.10.73 self permanent
4a:41:25:e7:97:f7 dst 192.168.10.72 self permanent
56:3d:fe:bd:9d:74 dst 192.168.10.75 self permanent
4e:d9:41:81:77:f1 dst 192.168.10.75 self permanent
可以看到,上面用的对方flannel.1的MAC地址对应宿主机IP,也就是UDP要发往的目的地。所以使用这个目的IP与MAC地址进行封装。

f.数据包到达目的宿主机:接下来,就是宿主机与宿主机之间通信了,数据包从Node1的eth0网卡发出去,Node2接收倒数据包,解封装发现是VXLAN数据包,把它交给flannel.1设备。flannel.1设备则会进一步拆包,取出原始IP包(源容器IP和目标容器IP),通过cni0网桥二层转发给容器.

5.3 Host-GW

host-gw模式相比vxlan简单了许多,直接添加路由,将目的主机当做网关,直接路由原始封包。
kubernetes集群网络
# kube-flannel.yml
net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "host-gw"
      }
    }

或者 kubectl edit cm kube-flannel-cfg  -n kube-system 
修改vxlan为host-gw

kubectl delete pod kube-flannel-* -n kube-system 

当你设置flannel使用host-gw模式,flanneld会在宿主机上创建节点的路由表:
[root@k8s-master01 k8s-ceshi]# ip route 
default via 192.168.10.2 dev eth0 
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1 
10.244.3.0/24 via 192.168.10.74 dev eth0 
10.244.5.0/24 via 192.168.10.72 dev eth0 
169.254.0.0/16 dev eth0 scope link metric 1002 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
192.168.10.0/24 dev eth0 proto kernel scope link src 192.168.10.71 

目的IP地址属于10.244.5.0/24网段的IP包,应该经过本机的eth0设备发出去(即:dev eth0;并且,它下一跳地址是192.168.10.72。一旦配置了下一跳地址,那么接下来,当IP包从网络层进入链路层封装成帧的时候,eth0设备就会使用下一跳地址对应的MAC地址,作为该数据帧的目的MAC 地址。

而Node2 的内核网络栈从二层数据帧里拿到IP包后,会“看到”这个IP包的目的IP地址是container-2 的IP地址。这时候,根据Node 2的路由表,该目的地址会匹配到第二条路由规则,从而进入cni0网桥,进而进入到container-2当中。

5.4 vxlan和host-gw优缺点

1、vxlan不受网络环境限制,只要三层可达就行
2、vxlan需要二层解封包,降低工作效率
3、hostgw基于路由表转发,效率更高
4、hostgw只适用于二层网络(本身网络架构受限,节点数量也受限)
5、hostgw将宿主机当做网关

6 Kubernetes网络方案之Calico

Calico是一个纯三层的数据中心网络方案,Calico支持广泛的平台,包括Kubernetes、OpenStack等。 Calico在每一个计算节点利用Linux Kernel实现了一个高效的虚拟路由器( vRouter)来负责数据转发,而每个vRouter通过BGP协议负责把自己上运行的workload的路由信息向整个Calico网络内传播。 此外,Calico项目还实现了Kubernetes网络策略,提供ACL功能。

6.1 BGP概述

实际上,Calico项目提供的网络解决方案,与Flannel的host-gw模式几乎一样。也就是说,Calico也是基于路由表实现容器数据包转发,但不同于Flannel使用flanneld进程来维护路由信息的做法,而Calico项目使用BGP协议来自动维护整个集群的路由信息。 BGP英文全称是Border Gateway Protocol,即边界网关协议,它是一种自治系统间的动态路由发现协议,与其他 BGP 系统交换网络可达信息。
kubernetes集群网络

在这个图中,有两个自治系统(autonomous system,简称为AS):AS 1 和 AS 2。
在互联网中,一个自治系统(AS)是一个有权自主地决定在本系统中应采用何种路由协议的小型单位。这个网络单位可以是一个简单的网络也可以是一个由一个或多个普通的网络管理员来控制的网络群体,它是一个单独的可管理的网络单元(例如一所大学,一个企业或者一个公司个体)。一个自治系统有时也被称为是一个路由选择域(routing domain)。一个自治系统将会分配一个全局的唯一的16位号码,有时我们把这个号码叫做自治系统号(ASN)。

在正常情况下,自治系统之间不会有任何来往。如果两个自治系统里的主机,要通过 IP 地址直接进行通信,我们就必须使用路由器把这两个自治系统连接起来。BGP协议就是让他们互联的一种方式。

6.2 Calico BGP实现

kubernetes集群网络

在了解了 BGP 之后,Calico 项目的架构就非常容易理解了,Calico主要由三个部分组成:

Felix:以DaemonSet方式部署,运行在每一个Node节点上,主要负责维护宿主机上路由规则以及ACL规则。
BGP Client(BIRD):主要负责把 Felix 写入 Kernel 的路由信息分发到集群 Calico 网络。
Etcd:分布式键值存储,保存Calico的策略和网络配置状态。
calicoctl:允许您从简单的命令行界面实现高级策略和网络。

6.3 calico部署

curl https://docs.projectcalico.org/manifests/calico-etcd.yaml -o calico.yaml

下载完后还需要修改里面配置项:
具体步骤如下:
    配置连接etcd地址,如果使用https,还需要配置证书。(ConfigMap,Secret)
    根据实际网络规划修改Pod CIDR(CALICO_IPV4POOL_CIDR)
    选择工作模式(CALICO_IPV4POOL_IPIP),支持BGP(Never)、IPIP(Always)、CrossSubnet(开启BGP并支持跨子网)

修改并应用calico.yaml
拷贝证书,粘贴到calico配置文件的:Secret下的 etcd_key,etcd_cert,etcd_ca 中,另外获取的etcd信息修改到ConfigMap下的etcd_endpoints 中

 cat /etc/kubernetes/pki/etcd/ca.crt |base64  -w 0
 cat /etc/kubernetes/pki/apiserver-etcd-client.crt |base64  -w 0
 cat /etc/kubernetes/pki/apiserver-etcd-client.key |base64  -w 0

vim calico.yaml
etcd_endpoints: "https://192.168.10.71:2379,https://127.0.0.1:2379"
etcd_ca: "/calico-secrets/etcd-ca"
etcd_cert: "/calico-secrets/etcd-cert"
etcd_key: "/calico-secrets/etcd-key"
- name: CALICO_IPV4POOL_CIDR
  value: "10.244.0.0/16"
- name: CALICO_IPV4POOL_IPIP  # 启用IPIP()
  value: "Never"

删除flannel网络
[root@k8s-master01 k8s-ceshi]# kubectl delete -f kube-flannel.yml 
$ ip link delete cni0
$ ip link delete docker0
$ ip link delete flannel.1

[root@k8s-master01 k8s-ceshi]# ip route
default via 192.168.10.2 dev eth0 
10.244.2.0/24 via 192.168.10.73 dev eth0 
10.244.3.0/24 via 192.168.10.74 dev eth0 
10.244.4.0/24 via 192.168.10.75 dev eth0 
10.244.5.0/24 via 192.168.10.72 dev eth0 
169.254.0.0/16 dev eth0 scope link metric 1002 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
192.168.10.0/24 dev eth0 proto kernel scope link src 192.168.10.71 

ip route del 10.244.2.0/24 via 192.168.10.73 dev eth0 
ip route del 10.244.3.0/24 via 192.168.10.74 dev eth0
ip route del 10.244.4.0/24 via 192.168.10.75 dev eth0 
ip route del 10.244.5.0/24 via 192.168.10.72 dev eth0

应用calico
[root@k8s-master01 calico]# kubectl apply -f caliconew.yaml 

[root@k8s-master01 manifests]# kubectl get pods -n kube-system  |grep calico
calico-kube-controllers-7dd874d5b4-zddpw   1/1     Running            0          107m
calico-node-2wvkz                          1/1     Running            0          107m
calico-node-8vqcq                          1/1     Running            0          107m
calico-node-jn4qh                          1/1     Running            0          107m
calico-node-kljb9                          1/1     Running            0          107m
calico-node-sf5bq                          1/1     Running            0          93m

查看路由
[root@k8s-master01 ~]# ip route|grep bird
10.244.58.192/26 via 192.168.10.73 dev eth0 proto bird 
10.244.85.192/26 via 192.168.10.72 dev eth0 proto bird 
10.244.135.128/26 via 192.168.10.74 dev eth0 proto bird 
10.244.217.64/26 via 192.168.10.75 dev eth0 proto bird 

6.4 Calico管理工具

# wget -O /usr/local/bin/calicoctl https://github.com/projectcalico/calicoctl/releases/download/v3.9.1/calicoctl
# chmod +x /usr/local/bin/calicoctl

查看node
[root@k8s-master01 calico]# calicoctl node status
Calico process is running.

IPv4 BGP status
+---------------+-------------------+-------+----------+-------------+
| PEER ADDRESS  |     PEER TYPE     | STATE |  SINCE   |    INFO     |
+---------------+-------------------+-------+----------+-------------+
| 192.168.10.72 | node-to-node mesh | up    | 08:45:06 | Established |
| 192.168.10.74 | node-to-node mesh | up    | 08:43:29 | Established |
| 192.168.10.75 | node-to-node mesh | up    | 08:42:47 | Established |
| 192.168.10.73 | node-to-node mesh | up    | 08:15:51 | Established |
+---------------+-------------------+-------+----------+-------------+

# mkdir /etc/calico
[root@k8s-master01 calico]# cat /etc/calico/calicoctl.cfg 
apiVersion: projectcalico.org/v3
kind: CalicoAPIConfig
metadata:
spec:
  datastoreType: "etcdv3"
  etcdEndpoints: "https://192.168.10.71:2379,https://127.0.0.1:2379"
  etcdKeyFile: "/etc/kubernetes/pki/apiserver-etcd-client.key"
  etcdCertFile: "/etc/kubernetes/pki/apiserver-etcd-client.crt"
  etcdCACertFile: "/etc/kubernetes/pki/etcd/ca.crt"

使用calicoctl查看服务状态:
ss -antp  | grep 179   #监听179端口
 calicoctl get nodes
NAME           
k8s-master01   
k8s-node01     
k8s-node02     
k8s-node03     
k8s-node04     

查看 IPAM的IP地址池:
[root@k8s-master01 calico]# calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   SELECTOR   
default-ipv4-ippool   10.244.0.0/16   true   Never      Never       false      all()      

6.5 Calico BGP原理

kubernetes集群网络
[root@k8s-node01 ~]# ifconfig |grep cali
cali0031d1b85c8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1440
cali6a2d75a76dd: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1440

Pod 1 访问 Pod 2大致流程如下: a.数据包从容器1出到达Veth Pair另一端(宿主机上,以cali前缀开头); b.宿主机根据路由规则,将数据包转发给下一跳(网关); c.到达Node2,根据路由规则将数据包转发给cali设备,从而到达容器2

Calico项目实际上将集群里的所有节点,都当作是边界路由器来处理,它们一起组成了一个全连通的网络,互相之间通过BGP协议交换路由规则。这些节点,我们称为BGP Peer。

6.6 Route Reflector模式(RR)

Calico维护的网络在默认是(Node-to-Node Mesh)全互联模式,Calico集群中的节点之间都会相互建立连接,用于路由交换。但是随着集群规模的扩大,mesh模式将形成一个巨大服务网格,连接数成倍增加。
root@k8s-node02 ~]# netstat -antp |grep bird
tcp        0      0 0.0.0.0:179             0.0.0.0:*               LISTEN      42170/bird          
tcp        0      0 192.168.10.73:179       192.168.10.74:46012     ESTABLISHED 42170/bird          
tcp        0      0 192.168.10.73:38883     192.168.10.71:179       ESTABLISHED 42170/bird          
tcp        0      0 192.168.10.73:179       192.168.10.75:36831     ESTABLISHED 42170/bird          
tcp        0      0 192.168.10.73:53428     192.168.10.72:179       ESTABLISHED 42170/bird          

这时就需要使用Route Reflector(路由器反射)模式解决这个问题。
确定一个或多个Calico节点充当路由反射器,让其他节点从这个RR节点获取路由信息。

具体步骤如下:
1.关闭 node-to-node BGP网格

添加 default BGP配置,调整 nodeToNodeMeshEnabled和asNumber:
cat << EOF | calicoctl create -f -
 apiVersion: projectcalico.org/v3
 kind: BGPConfiguration
 metadata:
   name: default
 spec:
   logSeverityScreen: Info
   nodeToNodeMeshEnabled: false  
   asNumber: 64512
EOF

ASN号可以通过获取 # calicoctl get nodes --output=wide

 calicoctl node status
Calico process is running.

IPv4 BGP status
No IPv4 peers found.

IPv6 BGP status
No IPv6 peers found.

2.配置指定节点充当路由反射器
为方便让BGPPeer轻松选择节点,通过标签选择器匹配。
给路由器反射器节点打标签:

kubectl label node k8s-node01 route-reflector=true

3.配置路由器反射器节点routeReflectorClusterID

calicoctl get node k8s-node01 -o yaml > rr-node.yaml
cat rr-node.yaml
apiVersion: projectcalico.org/v3
kind: Node
metadata:
  annotations:
    projectcalico.org/kube-labels: '{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/arch":"amd64","kubernetes.io/hostname":"k8s-node01","kubernetes.io/os":"linux","route-reflector":"true"}'
  creationTimestamp: 2021-12-02T06:38:05Z
  labels:
    beta.kubernetes.io/arch: amd64
    beta.kubernetes.io/os: linux
    kubernetes.io/arch: amd64
    kubernetes.io/hostname: k8s-node01
    kubernetes.io/os: linux
    route-reflector: "true"
  name: k8s-node01
  resourceVersion: "89283550"
  uid: f297de1c-0dc8-46ad-8ffc-4e52a8438bd3
spec:
  bgp:
    ipv4Address: 192.168.10.72/24
    routeReflectorClusterID: 244.0.0.1 #加集群id
  orchRefs:
  - nodeName: k8s-node01
    orchestrator: k8s

[root@k8s-master01 calico]# calicoctl apply -f rr-node.yaml
Successfully applied 1 'Node' resource(s)

4.使用标签选择器将路由反射器节点与其他非路由反射器节点配置为对等


cat bgppeer.yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: peer-with-route-reflectors spec: nodeSelector: all() peerSelector: route-reflector == 'true' [root@k8s-master01 calico]# calicoctl apply -f bgppeer.yaml Successfully applied 1 'BGPPeer' resource(s) 查看节点的BGP连接状态: calicoctl node status Calico process is running. IPv4 BGP status +---------------+---------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +---------------+---------------+-------+----------+-------------+ | 192.168.10.72 | node specific | up | 09:41:10 | Established | +---------------+---------------+-------+----------+-------------+ IPv6 BGP status No IPv6 peers found. [root@k8s-master01 calico]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES bs1 1/1 Running 7 2d21h 10.244.135.130 k8s-node03 <none> <none> bs8 1/1 Running 7 2d21h 10.244.85.200 k8s-node01 <none> <none> [root@k8s-master01 calico]# ping 10.244.85.200 # 测试网络通信 PING 10.244.85.200 (10.244.85.200) 56(84) bytes of data. 64 bytes from 10.244.85.200: icmp_seq=1 ttl=63 time=3.61 ms

6.7 IPIP模式

Flannel host-gw模式最主要的限制,就是要求集群宿主机之间是二层连通的。而这个限制对于 Calico来说,也同样存在。 采用ipip模式就是为了实现基于三层的连通.
calicoctl get ipPool -o yaml > ipip.yaml
apiVersion: projectcalico.org/v3
items:
- apiVersion: projectcalico.org/v3
  kind: IPPool
  metadata:
    creationTimestamp: 2021-12-02T06:38:05Z
    name: default-ipv4-ippool
    resourceVersion: "89250102"
    uid: 1e273458-f1c0-49df-84a6-a5b4faabdeef
  spec:
    blockSize: 26
    cidr: 10.244.0.0/16
    ipipMode: Always
    natOutgoing: true
    nodeSelector: all()
    vxlanMode: Never
kind: IPPoolList
metadata:
  resourceVersion: "89335249"

calicoctl apply -f ipip.yaml
calicoctl get ippool -o wide

Pod 1 访问 Pod 2大致流程如下:
a.数据包从容器1出到达Veth Pair另一端(宿主机上,以cali前缀开头);
b.进入IP隧道设备(tunl0)由Linux内核IPIP驱动封装在宿主机网络的IP包中(新的IP包目的地之是原IP包的下一跳地址),这样就成了Node1 到Node2的数据包;
c.数据包经过路由器三层转发到Node2;
d.Node2收到数据包后,网络协议栈会使用IPIP驱动进行解包,从中拿到原始IP包;
e.然后根据路由规则,根据路由规则将数据包转发给cali设备,从而到达容器2。
当Calico使用IPIP模式的时候,集群的网络性能会因为额外的封包和解包工作而下降。所以建议你将所有宿主机节点放在一个子网里,避免使用IPIP。
如图
kubernetes集群网络

6.8 CNI 网络方案优缺点及最终选择

先考虑几个问题:
    需要细粒度网络访问控制?
    追求网络性能?
    服务器之前是否可以跑BGP协议?
    集群规模多大?
    是否有维护能力?

6.9 基于calico的网络策略

为什么需要网络隔离 CNI插件插件解决了不同Node节点Pod互通问题,从而形成一个扁平化网络,默认情况下,Kubernetes 网络允许所有 Pod 到 Pod 的流量,在一些场景中,我们不希望Pod之间默认相互访问,例如: a.应用程序间的访问控制。例如微服务A允许访问微服务B,微服务C不能访问微服务A b.开发环境命名空间不能访问测试环境命名空间Pod c.当Pod暴露到外部时,需要做Pod白名单 d.多租户网络环境隔离 所以,我们需要使用network policy对Pod网络进行隔离。支持对Pod级别和Namespace级别网络访问控制。

Pod网络入口方向隔离

基于Pod级网络隔离:只允许特定对象访问Pod(使用标签定义),允许白名单上的IP地址或者IP段访问Pod
基于Namespace级网络隔离:多个命名空间,A和B命名空间Pod完全隔离。

Pod网络出口方向隔离

拒绝某个Namespace上所有Pod访问外部
基于目的IP的网络隔离:只允许Pod访问白名单上的IP地址或者IP段
基于目标端口的网络隔离:只允许Pod访问白名单上的端口

一个NetworkPolicy例子

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

配置解析:
 podSelector:用于选择策略应用到的Pod组。
 policyTypes:其可以包括任一Ingress,Egress或两者。该policyTypes字段指示给定的策略用于Pod的入站流量、还是出站流量,或者两者都应用。如果未指定任何值,则默认值为Ingress,如果网络策略有出口规则,则设置egress。
 Ingress:from是可以访问的白名单,可以来自于IP段、命名空间、Pod标签等,ports是可以访问的端口。
Egress:这个Pod组可以访问外部的IP段和端口。

6.9.1 入站、出站网络流量访问控制案例

Pod访问限制
准备测试环境,一个web pod,两个client pod

kubectl create deployment web --image=nginx
# kubectl run client1 --generator=run-pod/v1 --image=busybox --command -- sleep 36000
# kubectl run client2 --generator=run-pod/v1 --image=busybox --command -- sleep 36000
# kubectl get pods --show-labels
client1                                   1/1     Running       0          58s     run=client1
client2                                   1/1     Running       0          49s     run=client2
web-5dcb957ccc-4dlkf                      1/1     Running       0          78s     app=web,pod-template-hash=5dcb957ccc

需求:将default命名空间携带run=web标签的Pod隔离,只允许default命名空间携带run=client1标签的Pod访问80端口

[root@k8s-master01 calico]# kubectl exec -it client1 -- sh 
/ # wget 10.244.85.202
Connecting to 10.244.85.202 (10.244.85.202:80)
saving to 'index.html'
index.html           100% |**************************************************************************************************************************************************************|   615  0:00:00 ETA
'index.html' saved
/ # ^C
/ # 
command terminated with exit code 130
[root@k8s-master01 calico]# kubectl exec -it client2 -- sh 
/ # wget 10.244.85.202
Connecting to 10.244.85.202 (10.244.85.202:80)
saving to 'index.html'
index.html           100% |**************************************************************************************************************************************************************|   615  0:00:00 ETA
'index.html' saved

cat pod_policy.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          project: default 
    - podSelector:
        matchLabels:
          run: client1
    ports:
    - protocol: TCP
      port: 80

执行
[root@k8s-master01 calico]# kubectl apply  -f pod_policy.yaml 
networkpolicy.networking.k8s.io/test-network-policy created
[root@k8s-master01 calico]# kubectl get networkpolicy
NAME                  POD-SELECTOR   AGE
test-network-policy   app=web        20s

验证
[root@k8s-master01 calico]# kubectl exec -it client1 -- sh 
/ # wget 10.244.85.202
Connecting to 10.244.85.202 (10.244.85.202:80)
saving to 'index.html'
index.html           100% |**************************************************************************************************************************************************************|   615  0:00:00 ETA
'index.html' saved
/ # 
[root@k8s-master01 calico]# kubectl exec -it client2 -- sh 
/ # wget 10.244.85.202
Connecting to 10.244.85.202 (10.244.85.202:80)

结论:
    都不能ping,client1 可以访问,client2 不能访问
    隔离策略配置:
Pod对象:default命名空间携带run=web标签的Pod
允许访问端口:80
允许访问对象:default命名空间携带run=client1标签的Pod
拒绝访问对象:除允许访问对象外的所有对象

6.9.2 命名空间隔离

需求:default命名空间下所有pod可以互相访问,能访问其他命名空间Pod,其他命名空间也不能访问default命名空间Pod。
 kubectl create ns test
 kubectl create deployment web --image=nginx -n test
 kubectl run client -n test --generator=run-pod/v1 --image=busybox --command -- sleep 36000
# vim ns-acl.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-from-other-namespaces 
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector: {}

kubectl apply -f ns-acl.yaml
[root@k8s-master01 calico]# kubectl get pod -n test -o wide
NAME                   READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
client                 1/1     Running   0          65s   10.244.135.131   k8s-node03   <none>           <none>
web-5dcb957ccc-9n9mp   1/1     Running   0          78s   10.244.85.203    k8s-node01   <none>           <none>
[root@k8s-master01 calico]# kubectl get pod  -o wide
NAME                                      READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
client1                                   1/1     Running   0          22m   10.244.58.200   k8s-node02   <none>           <none>
client2                                   1/1     Running   0          22m   10.244.58.199   k8s-
web-5dcb957ccc-4dlkf                      1/1     Running   0          22m   10.244.85.202   k8s-node01   <none>           <none>

验证 
[root@k8s-master01 calico]#  kubectl exec -it client -n test sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.
/ # wget 10.244.85.202
Connecting to 10.244.85.202 (10.244.85.202:80)
^C
/ # wget 10.244.85.203
Connecting to 10.244.85.203 (10.244.85.203:80)
saving to 'index.html'
index.html           100% |**************************************************************************************************************************************************************|   615  0:00:00 ETA
'index.html' saved

podSelector: {}:default命名空间下所有Pod
from.podSelector: {} : 如果未配置具体的规则,默认不允许
  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin
avatar

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: