目录
k8s网络通信
NodePort方式
headless service "无头服务"
LoadBalancer方式
ExternalName 方式
Service可以看作是一组提供相同服务的Pod对外的访问接口。
借助Service应用可以方便地实现服务发现和负载均衡。 service默认只支持4层负载均衡能力,没有7层功能。(可以通过Ingress实现)
Service 是由 kube-proxy 组件,加上 iptables 来共同实现的. kube-proxy 通过 iptables 处理 Service 的过程,需要在宿主机上设置相当多的 iptables 规则,如果宿主机有大量的Pod,不断刷新iptables规则,会消耗大量的CPU资源。
IPVS模式的service,可以使K8s集群支持更多量级的Pod
开启kube-proxy的ipvs模式
iptables -t nat -nL #查看iptable策略
kubectl get svc
yum install -y ipvsadm
ipvsadm -ln #查看策略,目前为空
kubectl get pod -n kube-system
kubectl get cm -n kube-system ##所有服务的配置都存在cm中
kubectl edit cm kube-proxy -n kube-system #//修改IPVS模式,mode: "ipvs"
kubectl get pod -n kube-system |grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}' ##//更新kube-proxy pod(删除pod,控制器自动创建一份,类似于刷新)
kubectl get cm -n kube-system
kubectl get pod -n kube-system #运行可以看到已经生效
ipvsadm -ln #已经查看到策略
cd
ip addr #系统中出现kube-ipvs0接口,策略内每个节点都有,出现相应vip
ls
cd init/
ls
cat svc.yaml
cd pod/
ls
vim deploy.yml #用deploy控制器创建3个pod
kubectl apply -f deploy.yml
kubectl get pod
kubectl get svc #可以看到创建的svc获得的集群地址10.108.242.106
kubectl describe svc myservice##service选择的后端是通过标签匹配的,endpoint有三个ip
ipvsadm -ln ##创建出相应的策略【10.108.242.106地址下面有三个pod】
kubectl get svc #ClusterIP仅限集群内部
kubectl get pod -o wide
ip addr #PVS模式下,kube-proxy会在service创建后,在宿主机上添加一个虚拟网卡:kube-ipvs0,并分配service IP
curl 10.108.242.106 #访问集群地址
curl 10.108.242.106/hostname.html #可以看到负载均衡
ipvsadm -ln #可以看到调度次数
ls
kube-proxy通过linux的IPVS模块,以rr轮询方式调度service中的Pod
vim deploy.yml #设置副本数为6
kubectl apply -f deploy.yml
kubectl get pod
kubectl describe svc myservice ####endpoint有6个ip
ipvsadm -ln
k8s通过CNI接口接入其他插件来实现网络通讯。目前比较流行的插件有flannel,calico等。 CNI插件存放位置:# cat /etc/cni/net.d/10-flannel.conflist
容器间通信:同一个pod内的多个容器间的通信,通过lo即可实现;
pod之间的通信: 同一节点的pod之间通过cni网桥转发数据包。
不同节点的pod之间的通信需要网络插件支持。 pod和service通信: 通过iptables或ipvs实现通信,ipvs取代不了iptables,因为ipvs只能做负载均衡,而做不了nat转换。
pod和外网通信:iptables的MASQUERADE。
Service与集群外部客户端的通信;(ingress、nodeport、loadbalancer)
Flannel vxlan模式跨主机通信原理
flannel网络原理
当容器发送IP包,通过veth pair 发往cni网桥,再路由到本机的flannel.1设备进行处理。 VTEP设备之间通过二层数据帧进行通信,源VTEP设备收到原始IP包后,在上面加上一个目的MAC地址,封装成一个内部数据帧,发送给目的VTEP设备。 内部数据桢,并不能在宿主机的二层网络传输,Linux内核还需要把它进一步封装成为宿主机的一个普通的数据帧,承载着内部数据帧通过宿主机的eth0进行传输。 Linux会在内部数据帧前面,加上一个VXLAN头,VXLAN头里有一个重要的标志叫VNI,它是VTEP识别某个数据桢是不是应该归自己处理的重要标识。 flannel.1设备只知道另一端flannel.1设备的MAC地址,却不知道对应的宿主机地址是什么。在linux内核里面,网络设备进行转发的依据,来自FDB的转发数据库,这个flannel.1网桥对应的FDB信息,是由flanneld进程维护的。 linux内核在IP包前面再加上二层数据帧头,把目标节点的MAC地址填进去,MAC地址从宿主机的ARP表获取。 此时flannel.1设备就可以把这个数据帧从eth0发出去,再经过宿主机网络来到目标节点的eth0设备。目标主机内核网络栈会发现这个数据帧有VXLAN Header,并且VNI为1,Linux内核会对它进行拆包,拿到内部数据帧,根据VNI的值,交给本机flannel.1设备处理,flannel.1拆包,根据路由表发往cni网桥,最后到达目标容器。
使用cni0接口通信
Cni0: 网桥设备,每创建一个pod都会创建一对 veth pair。其中一端是pod中的eth0,另一端是Cni0网桥中的端口(网卡)在server3上也是通过cni0,分配的ip10.244.1.1
Flanneld:flannel在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。同时Flanneld监听K8s集群数据库,为flannel.1设备提供封装数据时必要的mac、ip等网络数据信息。
VXLAN,即Virtual Extensible LAN(虚拟可扩展局域网),是Linux本身支持的一网种网络虚拟化技术。VXLAN可以完全在内核态实现封装和解封装工作,从而通过“隧道”机制,构建出覆盖网络(Overlay Network)。
vxlan //报文封装,默认
Directrouting //直接路由,跨网段使用vxlan,同网段使用host-gw模式。
host-gw: //主机网关,性能好,但只能在二层网络中,不支持跨网络, 如果有成千上万的Pod,容易产生广播风暴,不推荐
UDP: //性能差,不推荐 配置flannel
kubectl -n kube-system get cm
kubectl -n kube-system edit cm kube-flannel-cfg #配置flannel
kubectl get pod -n kube-system |grep flannel | awk '{system("kubectl delete pod "$1" -n kube-system")}' ##加载
kubectl -n kube-system get pod
kubectl -n kube-system get all ##有daemonset控制器在后台自动创建
route -n #查看路由。已经无flannel接口
kubectl get pod
kubectl get svc
kubectl describe svc myservice
curl 10.108.242.106
curl 10.108.242.106/hostname.html #改变网络类型后可以访问,实现负载均衡,同一个vlan更加方便
kubectl edit svc myservice #编辑改为nodeport方式
kubectl get svc #NodePort实际上是cluster ip基础之上绑定了一个端口
kubectl describe svc myservice #可以看到暴露的端口
dig -t A myservice.default.svc.cluster.local. @10.96.0.10 #服务的A记录。service是10.96.0.10
kubectl -n kube-system get svc
kubectl -n kube-system describe svc kube-dns #可以看到dns解析服务
kubectl -n kube-system get pod -o wide
dig -t A myservice.default.svc.cluster.local. @10.244.0.6 #访问的svc实际上负载到后端的pod上
dig -t A myservice.default.svc.cluster.local. @10.96.0.10
vim headless.yml
kubectl apply -f headless.yml
kubectl get svc #可以看到服务没有ip
kubectl -n kube-system describe svc nginx-svc #但是后端还有pod
kubectl describe svc nginx-svc #有标签
kubectl get pod --show-labels
dig -t A nginx-svc.default.svc.cluster.local. @10.96.0.10 #有解析记录
vim deploy.yml #修改副本数为3
kubectl apply -f deploy.yml
kubectl get svc
dig -t A nginx-svc.default.svc.cluster.local. @10.96.0.10 #解析的ip发生变化
kubectl run -it demo --image=busyboxplus --restart=Never #直接可以访问svc名称做到负载均衡
kubectl delete pod demo
从外部访问 Service 的第二种方式,适用于公有云上的 Kubernetes 服务。这时候,你可以指定一个 LoadBalancer 类型的 Service
kubectl get node
kubectl get pod
cd
cd service/
ls
vim lb.yml
kubectl apply -f lb.yml
kubectl get svc #发现lb-nginx 一直处于pending状态
在service提交后,Kubernetes就会调用 CloudProvider 在公有云上为你创建一个负载均衡服务,并且把被代理的 Pod 的 IP地址配置给负载均衡服务做后端。
vim lb.yml
kubectl apply -f lb.yml
kubectl get pod
kubectl describe svc lb-nginx
cd
ls
kubectl edit configmap -n kube-system kube-proxy #编辑strictARP: true
kubectl get pod -n kube-system |grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}' #加载使之生效
kubectl get pod -n kube-system #重新自动创建
mkdir metallb
cd metallb/
wget https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/namespace.yaml
cat namespace.yaml
kubectl apply -f namespace.yaml
kubectl get ns
wget https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/metallb.yaml
ls
vim metallb.yaml #修改镜像的路径
kubectl apply -f metallb.yaml
kubectl -n metallb-system get all #查看状态是否就绪
kubectl get svc #
vim config.yaml #设置IP池
kubectl apply -f config.yaml
kubectl get svc
cd
cd service/
ls
cp lb.yml lb-2.yml
vim lb-2.yml
kubectl apply -f lb-2.yml
kubectl get svc #会看到分配的ip
这个 IP 池必须专供 MetalLB 使用,不能重复使用 Kubernetes 节点 IP 或 DHCP 服务器分发的 IP。
kubectl delete -f lb-2.yml
kubectl get svc
从外部访问的第三种方式叫做ExternalName,这种方式也可以使pod能够访问外部的地址
vim ex-svc.yaml
kubectl apply -f ex-svc.yaml 创建service
kubectl get svc
dig -t A my-service.default.svc.cluster.local. @10.96.0.10 #可以看出并没有分配ip地址,只是在dns里面更新了以下CNAME
externalName: test.westos.org #指定外部有效地址
这样的做法有一个好处,即使需要访问的外部地址变了,也只需要更改service的yaml文件即可,依然可以通过访问myservice.default.svc.cluster.local
来访问外部地址,如下变动外部网站
vim ex-svc.yaml
kubectl apply -f ex-svc.yaml
dig -t A my-service.default.svc.cluster.local. @10.96.0.10
kubectl get ns
kubectl create namespace demo
kubectl get ns
vim ex-svc.yaml
kubectl apply -f ex-svc.yaml
kubectl get svc
kubectl get svc -n demo
dig -t A my-service.demo.svc.cluster.local. @10.96.0.10
kubectl delete -f ex-svc.yaml
kubectl delete namespaces demo
kubectl get ns
ls
ExternalName方式的service允许为其分配一个公有IP
vim ex-ip.yaml
kubectl apply-f ex-ip.yaml
kubectl apply -f ex-ip.yaml
kubectl get svc #查看svc的状态说明指定成功
kubectl describe svc ex-service
ip addr add 172.25.254.200/24 dev eth0 #集群外访问时需要添加ip
ip addr del 172.25.254.200/24 dev eth0
kubectl get svc
kubectl delete -f ex-ip.yaml
kubectl get all
kubectl get svc
kubectl delete svc my-service