k8s之service实现服务发现和负载均衡

1.Service概述 service是kubernetes中最核心的资源对象之一,service和pod之间是通过Label串起来,相同的Service的pod的Label是一样的.同一个service下的所有pod是通过kube-proxy实现负载均衡.而每个service都会分配一个全局唯一的虚拟ip,也就cluster ip.
在该service整个生命周期内,cluster ip保持不变,而在kubernetes中还有一个dns服务,它会把service的name解析为cluster ip.
Service可以看作是一组提供相同服务的Pod对外的访问接口。借助Service,应用可以方便地实现服务发现和负载均衡。
service默认只支持4层负载均衡能力,没有7层功能。(可以通过Ingress实现)
2. service的三种工作模式 第一种: 是Userspace方式
如下图描述,
k8s之service实现服务发现和负载均衡
文章图片

Client Pod要访问Server Pod时,它先将请求发给本机内核空间中的service规则,由它再将请求,
转给监听在指定套接字上的kube-proxy,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求
递交给内核空间中的service,由service将请求转给指定的Server Pod。
由于其需要来回在用户空间和内核空间交互通信,因此效率很差,接着就有了第二种方式.
第二种: iptables模型
此工作方式是直接由内核中的iptables规则,接受Client Pod的请求,并处理完成后,直接转发给指定ServerPod.
k8s之service实现服务发现和负载均衡
文章图片

第三种: ipvs模型
它是直接有内核中的ipvs规则来接受Client Pod请求,并处理该请求,再有内核封包后,直接发给指定的Server Pod。
k8s之service实现服务发现和负载均衡
文章图片

注:
以上不论哪种,kube-proxy都通过watch的方式监控着kube-APIServer写入etcd中关于Pod的最新状态信息,
它一旦检查到一个Pod资源被删除了 或 新建,它将立即将这些变化,反应再iptables 或 ipvs规则中,以便iptables和ipvs在调度Clinet Pod请求到Server Pod时,不会出现Server Pod不存在的情况
自k8s1.1以后,service默认使用ipvs规则,若ipvs没有被激活,则降级使用iptables规则. 但在1.1以前,service
使用的模式默认为userspace.
Service 是由 kube-proxy 组件,加上 iptables 来共同实现的.
kube-proxy 通过 iptables 处理 Service 的过程,需要在宿主机上设置相当多的 iptables 规则,如果宿主机有大量的Pod,不断刷新iptables规则,会消耗大量的CPU资源。
3. service的四种类型 1. ExternalName:

用于将集群外部的服务引入到集群内部,在集群内部可直接访问来获取服务。它的值必须是 FQDN, 此FQDN为集群内部的FQDN, 即: ServiceName.Namespace.Domain.LTD.然后CoreDNS接受到该FQDN后,能解析出一个CNAME记录, 该别名记录为真正互联网上的域名.如: www.test.com, 接着CoreDNS在向互联网上的根域DNS解析该域名,获得其真实互联网IP.
2. ClusterIP:
用于为集群内Pod访问时,提供的固定访问地址,默认是自动分配地址,可使用ClusterIP关键字指定固定IP.
3. NodePort:
【k8s之service实现服务发现和负载均衡】用于为集群外部访问Service后面Pod提供访问接入端口.
这种类型的service工作流程为:

Client----->NodeIP:NodePort----->ClusterIP:ServicePort----->PodIP:ContainerPort

4. LoadBalancer
用于当K8s运行在一个云环境内时,若该云环境支持LBaaS,则此类型可自动触发创建一个软件负载均衡器用于对Service做负载均衡调度.
因为外部所有Client都访问一个NodeIP,该节点的压力将会很大, 而LoadBalancer则可解决这个问题。而且它还直接动态监测后端Node是否被移除或新增了,然后动态更新调度的节点数。
4. 创建service:(ClusterIP方式)
apiVersion: v1 kind: Service metadata: name: web-service spec: ports: - name: http port: 80 targetPort: 80 selector: app: nginx type: ClusterIP

k8s之service实现服务发现和负载均衡
文章图片

k8s之service实现服务发现和负载均衡
文章图片

目前该web-service还没有后端 Endpoints
创建两个后端的pod
标签必须和web-service的保持一致 app: nginx
apiVersion: apps/v1 kind: Deployment metadata: name: deployment-nginx spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: reg.westos.org/k8s/myapp:v1 ports: - containerPort: 80

[kubeadm@server1 manifest]$ vim de.yaml [kubeadm@server1 manifest]$ kubectl apply -f de.yaml deployment.apps/deployment-nginx created [kubeadm@server1 manifest]$ kubectl get pod NAMEREADYSTATUSRESTARTSAGE deployment-nginx-84f7d65dcf-5bf9q1/1Running06s deployment-nginx-84f7d65dcf-qjknh1/1Running06s test1/1Running13m42s [kubeadm@server1 manifest]$

此时,我们的web-service有了两个后端
k8s之service实现服务发现和负载均衡
文章图片

k8s之service实现服务发现和负载均衡
文章图片

pod间内部通过访问ClusterIP分配的虚拟VIP负载到了后端的两个pod容器
ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType
5.Headless Service “无头服务” Headless Service不需要分配一个VIP,而是直接以DNS记录的方式解析出被代理Pod的IP地址。
域名格式:$(servicename).$(namespace).svc.cluster.local

无头服务,不分配ip地址,使用域名
apiVersion: v1 kind: Service metadata: name: nginx-svc spec: ports: - name: http port: 80 targetPort: 80 selector: app: nginx clusterIP: None

k8s之service实现服务发现和负载均衡
文章图片
k8s之service实现服务发现和负载均衡
文章图片

以DNS记录的方式解析出被代理Pod的IP地址。
k8s之service实现服务发现和负载均衡
文章图片

通过这种方式实现访问,即使在后端pod滚动更新之后依然可以通过DNS进行解 析
6. 将service的工作模式改为lvs(ipvs) k8s之service实现服务发现和负载均衡
文章图片

当pod创建的越来越多时,iptables的刷新频率就会越来越大,刷新策略会对cpu造成一定 的压力
使用lvs来实现pod的负载均衡,直接使用linux内核
在所有节点安装ipvsadm
当前没有任何策略
k8s之service实现服务发现和负载均衡
文章图片

将模式改为ipvs模式
kubectl -n kube-system cm kube-proxy

k8s之service实现服务发现和负载均衡
文章图片

删除原来的 kube-system pod
k8s之service实现服务发现和负载均衡
文章图片

发现又重新新建了三个kube-system 的pod
k8s之service实现服务发现和负载均衡
文章图片

在每个节点上发现,lvs的策略自动出现
k8s之service实现服务发现和负载均衡
文章图片

k8s之service实现服务发现和负载均衡
文章图片

创建一个pod,内部访问web-service的虚拟VIP实现了对pod的负载
k8s之service实现服务发现和负载均衡
文章图片

7. Flannel vxlan模式跨主机通信原理 k8s之service实现服务发现和负载均衡
文章图片

在每个node节点上发现kube-ipvs0的接口,只要创建一个service,就会加一个,10.100.84.9就是server2上创建的service的VIP地址
且三个节点相同
k8s之service实现服务发现和负载均衡
文章图片

2个container需要跨主机通信
那么,server2和server3两个node是如何实现跨主机通信的呢?
k8s之service实现服务发现和负载均衡
文章图片
cni0容器网络接口(k8s)和docker0差不多
vxlan模式跨主机通信过程:
通过ip add命令查看到容器在物理机上的veth卡。
因为这里使用了cni接口标准,这个veth卡会桥接在cni0的网桥上。这个我们可以通过brctl show进行查看
数据包走到了cni0的网桥后,根据已知的目标ip,10.244.2.6,可以查找路由表,根据路由和掩码,选择对应的iface,也就是flannel.1。且下一跳,也就是10.244.2.0。
k8s之service实现服务发现和负载均衡
文章图片

进入到flannel.1如何知道应该发向哪个物理机呢。这个时候,其实是通过arp来获取。可以通过arp命令查看到对应的mac地址。
k8s之service实现服务发现和负载均衡
文章图片

这个mac地址在vxlan中,可以通过bridge fdb show来进行查看。可以看到,如果是发向ae:39:2b:5e:b1:d0 的地址,则目标机器在172.25.254.3机器上。则数据就会流转到172.25.254.3上了。经过vxlan封包后的数据包就会经过eth0设备发向到172.25.254.3上。
k8s之service实现服务发现和负载均衡
文章图片

在172.25.254.3上,首先经过了iptables链,而后在flannel.1的Iface上则接收到该数据包。这里我们可以看到,flannel.1的mac地址就是ae:39:2b:5e:b1:d0。
k8s之service实现服务发现和负载均衡
文章图片

到达flannel.1后,根据路由表,查看10.244.2.6的路由应送到server3的cni0的网桥上。
k8s之service实现服务发现和负载均衡
文章图片

这里我们查看cni0的网桥信息。
k8s之service实现服务发现和负载均衡
文章图片

到达网桥后,就可以根据地址将数据送到10.244.2.26的对应的veth上,进而在容器中收到对应的数据包了。
以上就是两个处于不同物理机上的容器间发送数据包的流程。相比较来说,从容器到物理机的ping就简单多了。这个流程就是veth->cni0->eth0->对端物理机ip。这里就不详细叙述了。
同一台物理机上不同容器的ping只需要经过cni0的网桥就可以了。
8. nodeport
[kubeadm@server1 manifest]$ kubectl edit svc web-service service/web-service edited [kubeadm@server1 manifest]$

k8s之service实现服务发现和负载均衡
文章图片

k8s之service实现服务发现和负载均衡
文章图片

k8s之service实现服务发现和负载均衡
文章图片
k8s之service实现服务发现和负载均衡
文章图片

9. ExternalName
apiVersion: v1 kind: Service metadata: name: my-service spec: type:ExternalName externalName: www.baidu.com

用于让pod去访问集群外部的资源
k8s之service实现服务发现和负载均衡
文章图片

pod内部可以正常访问
k8s之service实现服务发现和负载均衡
文章图片

    推荐阅读