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方式
如下图描述,
文章图片
Client Pod要访问Server Pod时,它先将请求发给本机内核空间中的service规则,由它再将请求,
转给监听在指定套接字上的kube-proxy,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求
递交给内核空间中的service,由service将请求转给指定的Server Pod。
由于其需要来回在用户空间和内核空间交互通信,因此效率很差,接着就有了第二种方式.
第二种: iptables模型
此工作方式是直接由内核中的iptables规则,接受Client Pod的请求,并处理完成后,直接转发给指定ServerPod.
文章图片
第三种: ipvs模型
它是直接有内核中的ipvs规则来接受Client Pod请求,并处理该请求,再有内核封包后,直接发给指定的Server Pod。
文章图片
注:以上不论哪种,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实现服务发现和负载均衡](https://img.it610.com/image/info8/22db3f10c61a4e5d9dd010a67048f66f.jpg)
文章图片
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/afcc774ac3424571bfadfaeacb5c9d2e.jpg)
文章图片
目前该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实现服务发现和负载均衡](https://img.it610.com/image/info8/a13c5fb089d84429b66f0330a0e13cff.jpg)
文章图片
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/deed3a4fc9274cb0b95e995bb043a9f4.jpg)
文章图片
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实现服务发现和负载均衡](https://img.it610.com/image/info8/924670bfb9094c1bb7f010d825c9cae0.jpg)
文章图片
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/68eba57c5f1044f498b283e242fbe814.jpg)
文章图片
以DNS记录的方式解析出被代理Pod的IP地址。
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/8797e604abb54ddfae44b403e79e697b.jpg)
文章图片
通过这种方式实现访问,即使在后端pod滚动更新之后依然可以通过DNS进行解 析
6. 将service的工作模式改为lvs(ipvs)
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/528ac7d3a059498e90adc427d3acc68b.jpg)
文章图片
当pod创建的越来越多时,iptables的刷新频率就会越来越大,刷新策略会对cpu造成一定 的压力
使用lvs来实现pod的负载均衡,直接使用linux内核
在所有节点安装ipvsadm
当前没有任何策略
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/83181c148a0749b0a17cf2230589ed05.jpg)
文章图片
将模式改为ipvs模式
kubectl -n kube-system cm kube-proxy
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/70378d80779540c0a973399812107c2d.jpg)
文章图片
删除原来的 kube-system pod
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/a19af5ab88aa4af3b07d30d37032bd21.jpg)
文章图片
发现又重新新建了三个kube-system 的pod
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/4f9024c26eb4495abb01d42c721c32b8.jpg)
文章图片
在每个节点上发现,lvs的策略自动出现
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/90eeb0f8a97b496782eeea534be4ba33.jpg)
文章图片
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/794c94b784e242749fcd2b597ad60469.jpg)
文章图片
创建一个pod,内部访问web-service的虚拟VIP实现了对pod的负载
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/376d8008bf6a4d949dc8f26afea0c752.jpg)
文章图片
7. Flannel vxlan模式跨主机通信原理
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/a80a0a85c181427187f646eda4f61b76.jpg)
文章图片
在每个node节点上发现kube-ipvs0的接口,只要创建一个service,就会加一个,10.100.84.9就是server2上创建的service的VIP地址
且三个节点相同
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/f06de55d9069492b96dfba50bcac30ab.jpg)
文章图片
2个container需要跨主机通信
那么,server2和server3两个node是如何实现跨主机通信的呢?
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/3e385dd01cf94ce7aff95112f4176ffe.jpg)
文章图片
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实现服务发现和负载均衡](https://img.it610.com/image/info8/4196432203a749edbf649c6c899a7905.jpg)
文章图片
进入到flannel.1如何知道应该发向哪个物理机呢。这个时候,其实是通过arp来获取。可以通过arp命令查看到对应的mac地址。
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/a18f118b46954326aecacb95fbf9c432.jpg)
文章图片
这个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实现服务发现和负载均衡](https://img.it610.com/image/info8/0622e20ce3304e7b9347717b25502d4b.jpg)
文章图片
在172.25.254.3上,首先经过了iptables链,而后在flannel.1的Iface上则接收到该数据包。这里我们可以看到,flannel.1的mac地址就是ae:39:2b:5e:b1:d0。
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/7ca8871b787240a9aa9bd333585f4dc1.jpg)
文章图片
到达flannel.1后,根据路由表,查看10.244.2.6的路由应送到server3的cni0的网桥上。
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/384792f7980b4a6295e77da81e2fc797.jpg)
文章图片
这里我们查看cni0的网桥信息。
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/b445a6764c8044f7a0355935aced196d.jpg)
文章图片
到达网桥后,就可以根据地址将数据送到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实现服务发现和负载均衡](https://img.it610.com/image/info8/4fbade83fead48b3b0f78c3ca8537b4c.jpg)
文章图片
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/8a58531668624240b275fbd990ba8fe9.jpg)
文章图片
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/08e6c91637324c71a4064dd7048a555a.jpg)
文章图片
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/1f0622b9bd994b5fa647b5d502ba121a.jpg)
文章图片
9. ExternalName
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type:ExternalName
externalName: www.baidu.com
用于让pod去访问集群外部的资源
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/1ad38f31fa5e4539a1ffb87184b695fd.jpg)
文章图片
pod内部可以正常访问
![k8s之service实现服务发现和负载均衡](https://img.it610.com/image/info8/6b7ccb1d10024ae28858d07e38023d77.jpg)
文章图片