当前位置:   article > 正文

K8S中的网络_k8s网络

k8s网络


引言: 再来亿点点知识

一、K8S的网络模型与集群通信

1 K8S网络模型与实现方案

k8s集群中的每一个Pod(最小调度单位)都有自己的IP地址,即ip-per-pod模型。
在ip-per-pod模型中每一个pod在集群中保持唯一性,我们不需要显式地在每个 Pod 之间创建链接, 不需要处理容器端口到主机端口之间的映射。从端口分配、命名、服务发现、 负载均衡、应用配置和迁移的角度来看,Pod 可以被视作独立虚拟机或者物理主机。
如下图,从表面上来看两个容器在docker网络与k8s网络中与client通信形式。

在这里插入图片描述
k8s是一套庞大的分布式系统,为了保持核心功能的精简(模块化)以及适应不同业务用户的网络环境,k8s通过CNI(Container Network Interface)即容器网络接口集成各种网络方案。这些网络方案必须符合k8s网络模型要求:

节点上的 Pod 可以不通过 NAT 和其他任何节点上的 Pod 通信
节点上的代理(比如:系统守护进程、kubelet)可以和节点上的所有Pod通信

备注:仅针对那些支持 Pods 在主机网络中运行的平台(比如:Linux):

那些运行在节点的主机网络里的 Pod可以不通过 NAT 和所有节点上的 Pod 通信

如此操作,是不是有点像美团?将配送业务外包(CNI)给三方公司(实现方案),骑手是通过哪种飞机大炮(网络)送餐的我不管,只要符合准时、不撒漏(模型要求)等相关规矩这就是一次合格的配送。

CNI 做两件事,容器创建时的网络分配,和当容器被删除时释放网络资源。 常用的 CNI 实现方案有 Flannel、Calico、Weave以及各种云厂商根据自身网络推出的CNI插件如华为的 CNI-Genie、阿里云Terway。

2 pod 内容器通信

Pod内容器非常简单,在同一个 Pod 内,所有容器共享存储、网络即使用同一个 IP 地址和端口空间,并且可以通过localhost发现对方。Pod 使用了一个中间容器 Infra,Infra 在 Pod 中首先被创建,而其他容器则通过 Join Network Namespace 的方式与 Infra 容器关联在一起。

在这里插入图片描述

我们有一个pod包含busybox、nginx这两个容器

kubectl get pod -n training
NAME                             					READY   	STATUS    	RESTARTS   	AGE
pod-localhost-765b965cfc-8sh76   2/2     		Running   	0          			2m56s
  • 1
  • 2
  • 3

在busybox中使用telnet连接nginx容器的 80端口看看。

kubectl exec -it  pod-localhost-765b965cfc-8sh76 -c container-si1nrb -n training -- /bin/sh
​
# telnet localhost 80
Connected to localhost
  • 1
  • 2
  • 3
  • 4

一个pod有多个容器时可以通过-c指定进入的容器名(通过describe查看容器名称),显然通过localhost就可以轻松访问到同一个pod中的nginx容器80端口。这也是在许多关系密切的应用中通常会部署在同一个pod中。

3 pod与pod通信

3.1 pod在同一主机

我们通过node选择器将两个pod调度到同一个node中

 ...
 nodeSelector:
        kubernetes.io/hostname: node2
 ...
  • 1
  • 2
  • 3
  • 4

两个容器分别获得一个IP地址,同样通过IP地址双方网络正常互通。

# kubectl get pod -o wide -n training 
NAME                                  READY   STATUS    RESTARTS   AGE     IP              NODE                    NOMINATED NODE   READINESS GATES
​
pod-to-pod-64444686ff-w7c4g           1/1     Running   0          6m53s   100.82.98.206   node2        <none>           <none>
pod-to-pod-busybox-7b9db67bc6-tl27c   1/1     Running   0          5m3s    100.82.98.250   node2        <none>           <none>
# kubectl exec -it  pod-to-pod-busybox-7b9db67bc6-tl27c  -n training -- /bin/sh
/# telnet 100.82.98.206 80
Connected to 100.82.98.206
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

同一主机网络的pod互通和我们之前学习的docker bridge相似,通过linux网桥添加虚拟设备对 veth pair 连接容器和主机主机命名空间。
我们把之前的图拿过来,在k8s中只不过把灰色部分替换成CNI方案实现。

在这里插入图片描述

3.2 pod 在不同主机下

此时pod分布如下

kubectl get pod -o wide -n training 
NAME                                        							`	READY   		STATUS    	RESTARTS   AGE    IP              			NODE                    NOMINATED NODE   READINESS GATES
​
pod-to-pod-64444686ff-w7c4g                 				1/1     			Running   	0          		104m   100.82.98.206   	node2        			<none>           
​
pod-to-pod-busybox-node2-6476f7b7f9-mqcw9   	1/1     			Running  	 	0          		42s    100.91.48.208   	node3        			<none>    
​
# kubectl exec -it  pod-to-pod-busybox-node2-6476f7b7f9-mqcw9  -n training -- /bin/sh
/ # telnet 100.82.98.206 80
Connected to 100.82.98.206
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

pod在不同主机的通信依赖于CNI插件,这里我们以Calico为例的做简单了解,从Calico架构图中可以看到每个node节点的自身依然采用容器网络模式,Calico在每个节点都利用Linux 内核实现了一个高效的虚拟路由器vRouter来负责数据转发。每个虚拟路由器将路由信息广播到网络中,并添加路由转发规则。同时基于iptables还提供了丰富的网络策略,实现k8s的Network Policy策略,提供容器间网络可达性限制的功能。

简单理解就是通过在主机上启动虚拟路由器(calico node),将每个主机作为路由器使用实现互联互通的网络拓扑。

Calico节点组网时可以直接利用数据中心的网络结构(L2或者L3),不需要额外的NAT、隧道或者Overlay Network,没有额外的封包解包,能够节约CPU运算,提高网络效率。

在这里插入图片描述

4 pod 与service通信

我们知道在k8s中容器随时可能被摧毁,pod的IP显然不是持久的,会随着扩展或缩小应用规模、或者应用程序崩溃以及节点重启等而消失和出现。service 设计就是来处理这个问题。service可以管理一组 Pod 的状态,允许我们跟踪一组随时间动态变化的 Pod IP 地址。而客户端只需要知道service这个不变的虚拟IP就可以了。

我们先来看看典型的service与pod使用,我们创建了一个service,标签选择器为app:nginx,将会路由到app=nginx标签的Pod上。

在这里插入图片描述

# kubectl get service -n training
NAME               		TYPE        	CLUSTER-IP      EXTERNAL-IP   	PORT(S)    	AGE
training-service   		ClusterIP   	10.96.229.238   	<none>        		8881/TCP   10m
  • 1
  • 2
  • 3

Service对外暴露的端口8881,这样在集群的中的pod即可通过8881访问到与service 绑定的label为app=nginx的pod

kubectl run -it --image nginx:alpine curl --rm /bin/sh
/ # curl 10.96.229.238:8881
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

其实大多数时候在自动化部署服务时并不知道service ip,所以另一种常见方式通过DNS进行域名解析后,可以使用 “ServiceName:Port” 访问Service,可以自己尝试一下。

5 service 是如何做到服务发现的?

Endpoints是k8s中的一种资源对象,k8s通过Endpoints监控到Pod的IP,service又关联Endpoints从而实现Pod的发现。大致如下图所示,service的发现机制我们会在后面文章中做深入了解。
在这里插入图片描述

6 外网与service通信

其实所谓外网通信也是service的表现形式。
service几种类型和不同用途。

ClusterIP: 用于在集群内部互相访问的场景,通过ClusterIP访问Service,即我们上面所说的pod与service。
NodePort: 用于从集群外部访问的场景,通过节点上的端口访问Service。
LoadBalancer: 用于从集群外部访问的场景,其实是NodePort的扩展,通过一个特定的LoadBalancer访问Service,这个LoadBalancer将请求转发到节点的NodePort,而外部只需要访问LoadBalancer。
None: 用于Pod间的互相发现,这种类型的Service又叫Headless Service。

我们先来看NodePort:

在这里插入图片描述
我们在service中指定type: NodePort创建出的service将会包含一个在所有node 开放的端口30678,这样我们访问任意节点IP:30678即可访问到我们的pod

# kubectl get service -n training
NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
training-service   NodePort   10.96.229.238   <none>        8881:30678/TCP   55m
​
# curl 192.168.1.86:30678
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
....
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

LoadBalancer类型和它名字一样,为负载均衡而生。它的结构如下图所示,
在这里插入图片描述
LoadBalancer本身不是属于Kubernetes的组件,如果使用云厂商的容器服务。通常会提供一套他们的负载均衡服务比如阿里云ACK的SLB、华为云的ELB等等。Service是基于四层TCP和UDP协议转发的,而k8s 另外一种资源对象Ingress可以基于七层的HTTP和HTTPS协议转发,可通过域名和路径做到更细粒度的划分,这是后话。

二、k8s与各网络插件集成(flannel calico canal kube-router romana cni-genie)

通用说明
如果多次换不同网络插件实验,每次实验前先把 /etc/cni/net.d/ 目录下文件清空

rm -rf /etc/cni/net.d/*

1 flannel

# 创建flannel目录下载相关文件
mkdir flannel && cd flannel
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

# 修改配置
# 此处的ip配置要与kubeadm的pod-network参数配置的一致
  net-conf.json: |
    {
      "Network": "192.168.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }

# 修改镜像
image: registry.cn-shanghai.aliyuncs.com/gcr-k8s/flannel:v0.10.0-amd64

# 如果Node有多个网卡的话,参考flannel issues 39701,
# https://github.com/kubernetes/kubernetes/issues/39701
# 目前需要在kube-flannel.yml中使用--iface参数指定集群主机内网网卡的名称,
# 否则可能会出现dns无法解析。容器无法通信的情况,需要将kube-flannel.yml下载到本地,
# flanneld启动参数加上--iface=<iface-name>
    containers:
      - name: kube-flannel
        image: registry.cn-shanghai.aliyuncs.com/gcr-k8s/flannel:v0.10.0-amd64
        command:
        - /opt/bin/flanneld
        args:
        - --ip-masq
        - --kube-subnet-mgr
        - --iface=eth1

# 启动
kubectl apply -f kube-flannel.yml

# 查看
kubectl get pods --namespace kube-system
kubectl get svc --namespace kube-system

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

2 calico

配置启动etcd集群

本次实验使用与k8s一个etcd集群

生境环境建议使用单独的一套集群

配置启动calico

# 创建calico目录下载相关文件
mkdir calico && cd calico
wget https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/rbac.yaml
wget https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/calico.yaml

# 如果启用了RBAC(默认k8s集群启用),配置RBAC
kubectl apply -f rbac.yaml

# 修改calico.yaml文件中名为calico-config的ConfigMap中的etcd_endpoints参数为自己的etcd集群
etcd_endpoints: "http://11.11.11.111:2379,http://11.11.11.112:2379,http://11.11.11.113:2379"

# 修改镜像为国内镜像
sed -i 's@image: quay.io/calico/@image: registry.cn-shanghai.aliyuncs.com/gcr-k8s/calico-@g' calico.yaml

# 启动
kubectl apply -f calico.yaml

# 查看
kubectl get pods --namespace kube-system
kubectl get svc --namespace kube-system
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

参考文档

https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/calico#installing-with-the-etcd-datastore

3 canal

# 创建flannel目录下载相关文件
mkdir canal && cd canal
wget https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/canal/rbac.yaml
wget https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/canal/canal.yaml

# 修改配置
# 此处的ip配置要与kubeadm的pod-network参数配置的一致
  net-conf.json: |
    {
      "Network": "192.168.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }

# 修改calico镜像
sed -i 's@image: quay.io/calico/@image: registry.cn-shanghai.aliyuncs.com/gcr-k8s/calico-@g' canal.yaml

# 修改flannel镜像
image: registry.cn-shanghai.aliyuncs.com/gcr-k8s/flannel:v0.10.0-amd64

# 如果Node有多个网卡的话,参考flannel issues 39701,
# https://github.com/kubernetes/kubernetes/issues/39701
# 目前需要在kube-flannel.yml中使用--iface参数指定集群主机内网网卡的名称,
# 否则可能会出现dns无法解析。容器无法通信的情况,需要将kube-flannel.yml下载到本地,
# flanneld启动参数加上--iface=<iface-name>
    containers:
      - name: kube-flannel
        image: registry.cn-shanghai.aliyuncs.com/gcr-k8s/flannel:v0.10.0-amd64
   	    command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr", "--iface=eth1" ]


# 启动
kubectl apply -f rbac.yaml
kubectl apply -f canal.yaml

# 查看
kubectl get pods --namespace kube-system
kubectl get svc --namespace kube-system
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

4 kube-router

# 本次实验重新创建了集群,使用之前测试其他网络插件的集群环境没有成功
# 可能是由于环境干扰,实验时需要注意

# 创建kube-router目录下载相关文件
mkdir kube-router && cd kube-router
wget https://raw.githubusercontent.com/cloudnativelabs/kube-router/master/daemonset/kubeadm-kuberouter.yaml
wget https://raw.githubusercontent.com/cloudnativelabs/kube-router/master/daemonset/kubeadm-kuberouter-all-features.yaml

# 以下两种部署方式任选其一

# 1. 只启用 pod网络通信,网络隔离策略 功能
kubectl apply -f kubeadm-kuberouter.yaml

# 2. 启用 pod网络通信,网络隔离策略,服务代理 所有功能
# 删除kube-proxy和其之前配置的服务代理
kubectl apply -f kubeadm-kuberouter-all-features.yaml
kubectl -n kube-system delete ds kube-proxy

# 在每个节点上执行
docker run --privileged --net=host registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy-amd64:v1.10.2 kube-proxy --cleanup

# 查看
kubectl get pods --namespace kube-system
kubectl get svc --namespace kube-system
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

5 romana

# 创建flannel目录下载相关文件
mkdir romana && cd romana
wget https://raw.githubusercontent.com/romana/romana/master/containerize/specs/romana-kubeadm.yml

# 修改镜像
sed -i 's@gcr.io/@registry.cn-hangzhou.aliyuncs.com/@g' romana-kubeadm.yml
sed -i 's@quay.io/romana/@registry.cn-shanghai.aliyuncs.com/gcr-k8s/romana-@g' romana-kubeadm.yml

# 启动
kubectl apply -f romana-kubeadm.yml

# 查看
kubectl get pods --namespace kube-system
kubectl get svc --namespace kube-system
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

6 CNI-Genie

# CNI-Genie是华为开源的网络组件,可以使k8s同时部署多个网络插件

# 在k8s集群中安装calico组件

# 在k8s集群中安装flannel组件

# 在k8s集群中安装Genie组件
mkdir CNI-Genie && cd CNI-Genie
wget  https://raw.githubusercontent.com/Huawei-PaaS/CNI-Genie/master/conf/1.8/genie.yaml
sed -i 's@image: quay.io/cnigenie/v1.5:latest@image: registry.cn-shanghai.aliyuncs.com/gcr-k8s/cnigenie-v1.5:latest@g' genie.yaml
kubectl apply -f genie.yaml

# 查看
kubectl get pods --namespace kube-system
kubectl get svc --namespace kube-system

# 测试
cat >nginx-calico.yml<<EOF
apiVersion: v1
kind: Pod
metadata:
  name: nginx-calico
  labels:
    app: web
  annotations:
    cni: "calico"
spec:
  containers:
    - name: nginx
      image: nginx:alpine
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
EOF
cat >nginx-flannel.yml<<EOF
apiVersion: v1
kind: Pod
metadata:
  name: nginx-flannel
  labels:
    app: web
  annotations:
    cni: "flannel"
spec:
  containers:
    - name: nginx
      image: nginx:alpine
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
EOF
kubectl apply -f nginx-calico.yml
kubectl apply -f nginx-flannel.yml

# 查看
kubectl get pods -o wide

# 测试网络通信
kubectl exec nginx-calico -i -t -- ping -c4 1.1.1.1
kubectl exec nginx-flannel -i -t -- ping -c4 1.1.1.1

# 由于先启动的flannel,然后k8s创建了coredns,所以使用flannel cni的能正常使用dns
# 使用calico cni无法使用正常dns

# 测试dns
kubectl exec nginx-calico -i -t -- ping -c4 www.baidu.com
kubectl exec nginx-flannel -i -t -- ping -c4 www.baidu.com

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

7 组件小结

kube-router性能损失最小,时延最小,其他网络插件性能差距不大。除了flannel没有网络隔离策略,其他均支持网络隔离策略。CNI-Genie是一个可以让k8s使用多个cni网络插件的组件,暂时不支持隔离策略。
理论结果: kube-router > calico > canal = flannel = romana

总结:

不写了,睡觉,狗命要紧,未完 待续

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/128308
推荐阅读
相关标签
  

闽ICP备14008679号