文章目录
一、四层负载均衡Service概述
1.1、为什么要有Service?
在kubernetes中,pod是有生命周期的,如果pod重启ip地址有可能会发生变化,如果客户端服务中定义的是pod的ip地址的话,那么该pod的ip地址发生变化后客户端服务会找不到之前定义的ip地址从而发生报错。 为了解决这个问题,在kubernetes中定义了service资源对象。Service 定义了一个服务访问的入口,客户端通过这个入口即可访问服务背后的应用集群实例,service是一组Pod的逻辑集合,这一组Pod能够被Service访问到,通常是通过Label Selector(标签选择器)实现的。
总结:
1、pod ip经常发生变化,service是pod的代理,客户端访问,只需要访问service,service就会把请求代理到后端Pod。
2、pod ip在k8s集群之外是无法访问,所以需要创建service,这个service可以在k8s集群外访问的。
1.2、Service是什么?
在Kubernetes平台上,Pod是有生命周期,为了可以给客户端一个固定的访问端点,因此需要在客户端和Pod之间添加一个中间层,这个中间层称之为Service。
在Kubernetes中,每个节点都安装了kube-proxy,kube-proxy通过kubernetes中固有的watch请求方法持续监听apiserver。一旦有service资源发生变动(增删改查)kube-proxy可以及时转化为能够调度到后端Pod节点上的规则,这个规则可以是iptables也可以是ipvs,取决于service实现方式。
1.3、Service实现原理
k8s在创建Service时,会根据标签选择器selector(lable selector)来查找Pod,据此创建与Service同名的endpoint对象,当Pod地址发生变化时,endpoint也会随之发生变化,service接收前端client请求的时候,就会通过endpoint,找到转发到那个Pod进行访问的地址。至于转发到哪个节点的Pod,由kube-proxy负载均衡来决定。如下图所示:
总结:
1、endpoint是k8s集群中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址。
2、只有当service配置selector(选择器),endpoint controller才会自动创建对应的endpoint对象,否则,不会生成endpoint对象。
3、在k8s集群中创建kubernetes的service,就会生成一个同名的endpoint对象,endpoint就是service关联的Pod的ip地址和端口。
4、访问Service的请求,不论是Cluster IP+TargetPort的方式;还是用Node节点IP+NodePort的方式,都被Node节点的Iptables规则重定向到Kube-proxy监听Service服务代理端口。kube-proxy接收到Service的访问请求后,根据负载策略,转发到后端的Pod。
1.4、kube-proxy三种运行模式
二、kubernets中三类IP地址
2.1、Node Network(节点网络)
Node Network(节点网络):物理节点或者虚拟节点的网络,如ens33接口上的网路地址。
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.16 netmask 255.255.255.0 broadcast 192.168.1.255
inet6 fe80::556d:553b:fe97:ab7b prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:43:a9:f0 txqueuelen 1000 (Ethernet)
RX packets 14026340 bytes 3605525779 (3.3 GiB)
RX errors 0 dropped 246096 overruns 0 frame 0
TX packets 4652141 bytes 663629292 (632.8 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
2.2、Pod network(pod 网络)
Pod network(pod 网络):创建的Pod具有的IP地址。
[root@k8s-client-17 state]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
sts-nginx-0 1/1 Running 0 2d4h 10.244.27.43 k8s-worker-21 <none> <none>
sts-nginx-1 1/1 Running 0 2d4h 10.244.115.60 k8s-worker-16 <none> <none>
sts-nginx-2 1/1 Running 0 2d4h 10.244.27.44 k8s-worker-21 <none> <none>
2.3、Cluster Network
Cluster Network(集群地址,也称为service network):这个地址是虚拟的地址(virtual ip),没有配置在某个接口上,只是出现在service的规则当中。
#查看mpmt-calculate-svc服务cluster ip地址
[root@k8s-master-111 jms]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 323d
mpmt-calculate-svc NodePort 10.96.155.70 <none> 8092:31092/TCP 9d
#通过ifconfig过滤10.96.155.70这个ip发现不存在
[root@k8s-worker-112 jms]# ifconfig | grep 10.96.155.70
#通过查看ipvs规则信息找到了10.96.155.70这个ip地址
[root@k8s-worker-112 jms]# ipvsadm -Ln --stats |grep -A 10 10.96.155.70
TCP 10.96.155.70:8092 0 0 0 0 0
-> 10.48.1.194:8092 0 0 0 0 0
-> 10.48.2.206:8092 0 0 0 0 0
总结:Node Network配置在节点接口之上,pod网络地址是配置在pod资源之上的,因此这些地址都是配置在某些设备之上的,这些设备可能是硬件,也可能是软件模拟的。但是Cluster Network是虚拟的地址,没有配置在某个接口上,只是出现在service的规则当中。
三、Service四种类型
K8S官网:Service四种类型,如下图所示:
3.1、ExternalName
应用场景:跨名称空间访问
如下图所示lolaage名称空间下的主机上无法访问default名称空间里的pod服务,测试资源下载链接
需求:default名称空间下的客户端服务想要访问lolaage名称空间下的nginx-svc服务
#1、default名称空间下创建one-test-nginx服务
---
apiVersion: v1
kind: Service
metadata:
name: one-test-nginx
labels:
app: one-test-nginx
namespace: default
spec:
selector:
app: one-test-nginx
type: ClusterIP
ports:
- port: 8088
protocol: TCP
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: one-test-nginx
spec:
selector:
matchLabels:
app: one-test-nginx
replicas: 1
template:
metadata:
labels:
app: one-test-nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80 #pod中的容器需要暴露的端口
---
#2、创建lolaage名称空间
kubectl create ns lolaage
#3、lolaage名称空间下创建two-test-nginx服务
---
apiVersion: v1
kind: Service
metadata:
name: two-test-nginx
namespace: lolaage
spec:
type: ExternalName
externalName: one-test-nginx.default.svc.cluster.local #one-test-nginx服务的全限定域名,即FQDN域名
ports:
- port: 8088
protocol: TCP
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: two-test-nginx
namespace: lolaage
spec:
selector:
matchLabels:
app: two-test-nginx
replicas: 1
template:
metadata:
labels:
app: two-test-nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80 #pod中的容器需要暴露的端口
---
service解析
service只要创建完成,我们就可以直接解析它的服务名,每一个服务创建完成后都会在集群dns中动态添加一个资源记录,添加完成后我们就可以解析了
资源记录格式是:
SVC_NAME.NS_NAME.DOMAIN.LTD.
服务名.命名空间.域名后缀
集群默认的域名后缀是svc.cluster.local.
就像我们上面创建的one-test-nginx这个服务,它的完整名称解析就是:one-test-nginx.default.svc.cluster.local
如下图所示,则表示在lolaage名称空间下可以访问default名称空间下的pod。
说明:externalName: one-test-nginx.default.svc.cluster.local定义了default名称空间下的one-test-nginx服务全限定域名,即FQDN,让使用者感觉就好像调用自己命名空间的服务一样。 如果测试的pod中没有curl命令也可以使用wget -q -O - one-test-nginx.default.svc.cluster.local命令进行测试。
3.2、ClusterIP
ClusterIP:暴露集群内部IP上的服务。选择此值将使服务只能从集群内部访问。这是默认的ServiceType。
示例如下:
---
apiVersion: v1
kind: Service
metadata:
name: test-nginx
labels:
app: test-nginx
namespace: default
spec:
selector:
app: test-nginx
type: ClusterIP
ports:
- port: 8088
protocol: TCP
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: test-nginx
replicas: 3
template:
metadata:
labels:
app: test-nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80 #pod中的容器需要暴露的端口
---
在集群内部(master节点和worker节点)可以通过service的cluster ip:port访问。
在集群外部(非master节点和非worker节点)不可以通过service的cluster ip:port访问。
3.3、NodePort
通过每个Node节点上的IP和静态端口暴露k8s集群内部的服务,请求访问:Client ==> NodeIP:NodePort ==> Service Ip:ServicePort ==> PodIP:ContainerPort
如果不记得k8s对外暴露端口范围,可参考如下命令:
示例如下::
---
apiVersion: v1
kind: Service
metadata:
name: test-nginx
labels:
app: test-nginx
namespace: default
spec:
selector:
app: test-nginx
type: NodePort
ports:
- port: 8088
protocol: TCP
targetPort: 80
nodePort: 33000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: test-nginx
replicas: 3
template:
metadata:
labels:
app: test-nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80 #pod中的容器需要暴露的端口
---
在集群内部(master节点和worker节点)可以通过service的cluster ip:port访问。
在集群外部(非master节点和非worker节点)可以通过每个Node节点上的IP和静态端口暴露k8s集群内部的服务。
3.4、LoadBalancer
使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到NodePort服务和ClusterIP 服务。
四、自定义endpoint实现映射外部服务
需求:kubernetes集群引用外部的Mysql数据库,如下图所示在kubernetes集群外主机上单独部署了一台mysql服务:
创建资源清单文件
---
apiVersion: v1
kind: Service
metadata:
name: external-mysql
spec:
type: ClusterIP
ports:
- port: 3306
---
apiVersion: v1
kind: Endpoints
metadata:
name: external-mysql
subsets:
- addresses:
- ip: 192.168.1.29
ports:
- port: 53000
---
如下图所示:
在kubernetes集群内主机上访问external-mysql服务的cluster ip和port,可以访问mysql,从而实现了将外部IP地址和服务引入到k8s集群内部,由service作为一个代理来达到能够访问外部服务的目的。
总结:整理不易,如果对你有帮助,可否点赞关注一下?
更多详细内容请参考:企业级K8s集群运维实战