通过k8s的调度,我们可以成功的将服务部署到Kubernetes,应用部署后,最重要的就是对用户开放。
在传统的架构中,用户访问公司内的服务可能通过了多层代理、网关、防火墙等。在Kubernetes中,访问Kubernetes中的的应用同样也会经过多层代理、网关、防火墙等。但是在传统架构中,可能是通过Nginx、Haproxy、LVS、公有云的SLB、ELB等实现的负载均衡和域名路由,虽然Kubernetes也是通过诸如此类的技术实现的域名路由或者负载均衡,只不过对应的名字叫法可能有所变化,不要把这些当做个比较新颖的东西,其实内部的实现和之前并无区别
在传统架构配置多个相同服务的负载均衡可能使用的是Nginx的upstream 在Kubernetes中用Service实现。在传统架构配置域名访问应用可能使用Nginx的Server配置,在Kubernetes中用Ingress实现域名路由的配置
一. Service的定义
service用于为一组提供服务的Pod抽象一个稳定的网络访问地址,是k8s实现微服务的核心概念通过Service的定义设置的访问地址是DNS域名格式的服务名称,对于客户端应用来说,网络访问方式并没有改变。Service还提供了负载均衡器的功能,将客户端请求负载分发到后端提供具体服务的各个Pod上。
service主要用于提供网络服务,通过Service的定义,能够为客户端应用提供稳定的访问地址(域名或IP地址)和负载均衡功能,以及屏蔽后端EndPoint的变化,是Kubernetes实现微服务的核心资源
总之,service是kubernetes中一个非常重的概念,service用于将外部请求代理到内部pod上,提供4层负载均衡和服务发现的功能, 使得我们可以建高可用和可扩展的应用程序。
二.Service的概念和原理
service是kubernetes中的一种抽象,用于定义一组pod以及访问这一组pod的策略、service的作用是将一组pod封装为一个虚拟的服务,并提供一个统一的入口,供客户端访问。service支持负载均衡、服务发现、服务暴露等功能。
在kubernetes中,pod的IP地址是动态变化的,因此无法直接通过pod的IP地址进行访问servic的出现正式为了解决找个问题的。service会为一组pod创建一个虚拟的IP地址,通过这个IP地址可以访问这组pod中的任意一个pod。当客户端请求这个虚拟IP地址时,请求会被负载均衡到一组pod中的某一个pod上,从而完成对pod的访问。
service的实现依赖于kube-proxy组件。kube-proxy会在每个节点上监听service的变化,一有service发生变化,kube-proxy会更新本地的iptables规则,从而实现流量的转发和负载均衡。另外,service还与CoreDNS有关。CoreDNS是kubernetes集群中的DNS解析服务在kubernetes中service的虚拟IP地址还会注册到CoreDNS中,从而使得客户端还可以通过service名称访问service的虚拟IP地址。在service的定义中,可以通过spec.selector字段指定哪些pod属于这个service,这样,就可以将请求负载均衡到这些pod中
总之,service是kubernetes中一种非常重要的资源对象,它可以让pod对外提供服务,并提供负载均衡、服务发现等功能service的实现依赖于kube-proxyy和CoreDNS组件,他们共同协作,将servic与pod连接起来,实现对pod的代理访问,如下图所示。
三.Service的负载均衡机制
当一个Service对象在Kubernetes集群中被定义出来时,集群内的客户端应用就可以通过服务IP访问到具体的Pod容器提供的服务了,从服务IP到后端Pod的负载均衡机制,则是由每个node上的kube-proxy代理来负责实现的。
kubproxy代理模式
kube-proxy的代理模式有:userspace,iptables,ipvs和kernelspace
1.userspace
起初,kube-proxy进程是一个真实TCP/UDP代理,当某个pod以ClusterIP方式 访问某个service的时候,这个流量会被pod所在的本机的iptables转发到本机的kube-proxy进程,然后将请求转发到后端某个pod上。
流程:
- kube-proxy为每个service在node上打开一个随机端口作为代理端口
- 建立iptables规则,将Clusterip的请求重定向到代理端口
- 到达代理端口的请求再由kuproxy转发到后端
2.iptables
Kubernetes从1.2版本开始将iptables作为默认模式,这种模式下kube-proxy不再起到proxy的作用。其核心功能:通过API server的Watch接口 实时跟踪service和endpoint的变更信息,并更新对应的iptables规则,client的请求流量通过iptables的NAT机制“直接路由”到目标pod
3.ipvs模式
- 工作原理:
kube-proxy
使用 IPVS(IP Virtual Server)来处理 Service 流量。IPVS 是 Linux 内核提供的负载均衡器。 - 优点:
- 支持更高效的负载均衡算法(如轮询、加权轮询等)。
- 适合处理大量的并发连接和高吞吐量。
- 缺点:
- 配置和管理可能会更复杂。
- 需要内核支持 IPVS 模块。
- 配置:你可以通过
kube-proxy
的配置文件来启用 IPVS 模式。
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-proxy
namespace: kube-system
data:
config.conf: |
mode: "ipvs"
ipvs为负载均衡提供了更多的算法:
- rr:轮询
- lc:最小连接数
- df:目标哈希
- sh:源哈希
- sed:预计延迟最短
- nq:从不排队
4.kernelspace
是WIndow server上的代理模式
四.service的四种类型
1.ClusterIP
(1)介绍
最常用的service类型, 它为pod提供了一个虚拟的IP地址。当其他Pod需要访问该Service时,它们只需要使用该虚拟IP地址即可。Kubernetes会自动将请求路由到相应的pod上。
(2)实验
1.生成用于测试Service的Deployment
编写deployment,用于各种service的验证
在应用service概念之前,先创建一个提供web服务的Pod集合,有两个Tomcat容器副本组成,每个容器提供的服务端口都为8080。(后续的实验都用此来测试)
(1)编写demloyment
[root@k8s-master ~]# vim webapp-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 2
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp
image: kubeguide/tomcat-app:v1
ports:
- name: http
containerPort: 8080
protocol: TCP
(2)生成pod
kubectl create -f webapp-deployment.yaml
kubectl get pod -l app=webapp -o wide
2.service的创建
2.1创建一个Cluster类型service
描述:ClusterIP是默认service类型,它将创建一个虚拟的ClusterIp地址,用于在集群内部访问service
使用场景:适用于集群内部的服务通信,例如将前端服务和后端服务连接起来,供内部其他服务使用。
(1)执行expose 命令暴露端口(创建service)
[root@k8s-master ~]# ku expose deployment webapp
service/webapp exposed
备注:为标签为webapp的deployment暴露一个ClusterIP类型的Kubernetes service。
(2)查看创建的service
[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d6h
webapp ClusterIP 10.108.145.159 <none> 8080/TCP 99s
(3)测试访问
[root@k8s-master ~]# curl 10.108.145.159:8080
(4)删除service
[root@k8s-master ~]# ku delete svc webapp
service "webapp" deleted
[root@k8s-master ~]# ku get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d6h
2.2.使用yaml文件创建service
除了使用expose的命令创建service外,更便于管理的方式是yaml文件来创建service
(1)编写service文件
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- protocol: TCP
port: 8080
targetPort: 8080
selector:
app: webapp
备注:
ports:定义的是service本身的端口号8080(用于提供给集群内部用户访问使用的端口)。
targetport:指定后端pod的容器端口号,selector定义的是后端pod所拥有的label。
(2)创建service,并测试
[root@k8s-master ~]# ku create -f webapp-service.yaml
service/webapp created
[root@k8s-master ~]# ku get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d6h
webapp ClusterIP 10.111.18.173 <none> 8080/TCP 28s
[root@k8s-master ~]# curl 10.111.18.173:8080
(3)查看endpoint列表
一个service对应的后端由pod的IP地址和容器的端口号组成,即一个完整的IP:port访问地址,这在k8s中叫做endpoint,通过查看service 的详细信息,可以看到起后端的Endpoint列表
[root@k8s-master ~]# ku describe svc webapp
Name: webapp
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=webapp
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.111.18.173
IPs: 10.111.18.173
Port: <unset> 8080/TCP
TargetPort: 8080/TCP
Endpoints: 10.244.140.71:8080,10.244.196.135:8080
Session Affinity: None
Events: <none>
(4)查看endpoint资源对象
实际上,k8s自动创建了与service关联的endpoint资源对象
[root@k8s-master ~]# ku get endpoints webapp
NAME ENDPOINTS AGE
webapp 10.244.140.71:8080,10.244.196.135:8080 7m52s
(5)删除这个service
kubectl delete -f webapp-service.yaml
service "webapp" deleted
[root@k8s-master ~]# ku get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d6h
或者
[root@k8s-master ~] ku delete svc webapp
service "webapp" deleted
[root@k8s-master ~]# ku get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d6h
2.NodePort
(1)解释
这种Service类型将pod公开为集群中所有节点上的某个端口,当外部请求到达任何一个节点上的该端口时,Kubernetes会将请求路由到响应的Pod上。
(2)实验
1.基础环境deployment
2.创建nodeport类型的service
描述:NodePort将在每个节点上公开一个端口,并将流量转发到service。他会创建一个clusterIP,并使用端口映射到每个节点的相同端口。
使用场景:适用与需要从外部访问集群中的服务时,可以通过节点的IP地址和映射的端口进行访问。这对于开发和测试环境非常重要。
(1)创建service文件
设置service类型为NodePort,并设置具体的nodePort端口号为30008
[root@k8s-master ~]# vim webapp-svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
type: NodePort
ports:
- port: 8080
targetPort: 8080
nodePort: 30008
selector:
app: webapp
备注:port :设置的是 Service 对象暴露的端口,用于集群内部通信
targetport: 容器内部的端口号。Service 将请求转发到这个端口。这个端口通常是 Pod 内运行的应用服务的端口。
nodePort:Service
类型为 NodePort
时,nodePort
是 Kubernetes 集群的每个节点上暴露的端口号,允许外部流量通过此端口访问 Service
(2)创建和查看与ClusterIP类型相同
[root@k8s-master ~]# ku create -f webapp-svc-nodeport.yaml
service/webapp created
[root@k8s-master ~]# ku get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d7h
webapp NodePort 10.109.129.168 <none> 8080:30008/TCP 6s
(3)测试在WIndow宿主机上用浏览器测试访问
Apache Tomcat/8.0.35http://192.168.10.101:30008/
3.LoadBalancer类型
(1)介绍
LoadBalancer Service:这种Service类型使用云提供商的负载均衡器将请求路由到后端Pod。Kubernetes会自动创建和配置负载均衡器,并将其绑定到service上。
(2)实验
通常在公有云的环境中会使用LoadBalancer的类型,可以将Service映射到公有云提供的某个负载均衡器的IP地址上,客户端通过负载均衡器的IP和Service的端口号就可以访问到具体的服务。
描述:LoadBalancer为Service创建一个外部负载均衡器,并分配一个外部IP地址。它通常由云提供商的负载均衡服务实现
使用场景:适用于需要将流量从外部负载均衡器分发到集群内部的服务,例如在生产环境中暴露web应用程序。
1.编写LoadBalancer
[root@k8s-master ~]# vim webapp-svc-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp
namespace: default
labels:
app: webapp
spec:
type: LoadBalancer
ports:
- port: 8080
targetPort: http
protocol: TCP
name: http
selector:
app: webapp
备注:这个service创建好后,云服务商会在service定义中补充LoadBalancer的IP地址,之后可以在客户端访问该Service
nodePort的端口范围在30000-32767之间
2.创建并查看
[root@k8s-master ~]# ku create -f webapp-svc-loadbalancer.yaml
service/webapp created
[root@k8s-master ~]# ku get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d7h
webapp LoadBalancer 10.109.149.6 <pending> 8080:30287/TCP 6s
因为没有连到云服务商后面的操作不在演示。
4. ExternalName
(1)介绍
externalName Service:这种service类型允许你将service映射到集群外部的某个名称。当Pod需要访问该service时,它们将使用该名称来解析出相应的IP地址
(2)实验
描述:ExternalName允许Service通过返回CNAME记录来引用集群外部的服务。它没有clusterIP,NodePort或 LoadBalancer
使用场景:适用于需要将K8s内部的服务与集群外的现有服务进行关联,例如连接到外部的数据库或其他资源。
ExternalName类型是Service的特例,用于将集群外的服务定义为Kubernetes的集群Service并通过ExternalName字段指定外部服务的地址,可以使用域名或IP格式。集群内客户端应用通过访问这个Service就能访问外部服务了。他没有选择器,没有定义任何端口和Endpoint,他通过返回该外部服务的别名来提供服务。也可以用于两个不同的namespace之间的不同pod可以通过name的形式访问。
注意:
两个不同的命名空间中的pod,可以直接使用他们的IP地址进行通信,但是pod的IP地址是随机分配的,重建pod后其IP地址就改变了,因此我们要用pod对应的service名称进行通信。但是跨命名空间是不能解析其他命名空间中创建的service名称的。这个时候就可以使用ExternalName实现两个不同命名空间中的各个pod间的通信
1.创建两个命名空间
[root@k8s-master ~]# ku create namespace test01
[root@k8s-master ~]# ku create namespace test02
2.第一个pod
(1)编写命名空间为test01的Pod文件并创建pod
[root@k8s-master ~]# vim myapp01.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp01
namespace: test01
spec:
replicas: 1
selector: #标签选择器
matchLabels: #匹配的标签为
app: myapp01
release: canary
template:
metadata:
labels:
app: myapp01 #和上面的myapp要匹配
release: canary
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http01
containerPort: 80
创建该pod
ku create -f myapp01.yaml
(2)创建一个无头(headless)service
[root@k8s-master ~]# vim myapp-svc-headless01.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-svc01
namespace: test01
spec:
selector:
app: myapp01 #挑选的pod还是myapp01。一个pod可以有多个service
release: canary
clusterIP: None #None表示是无头service
ports:
- port: 39320 #service ip中的端口
targetPort: 80 #容器ip中的端口
备注:
(HeadlessService)无头Service:这种服务没有入口地址(没有clusterIP),kube-proxy也不会为其创建负载转发的规则,服务名的解析机制取决于这个无头Service是否设置了标签选择器
注意:
无头服务创建出来之后,对于其他外部的pod来讲,该pod对应的名字就确定了下来,其固定的格式是:无头服务名.命名空间.svc.cluster.local
[root@k8s-master ~]# ku create -f myapp-svc-headless01.yaml
(3)创建extername
[root@k8s-master ~]# vim myapp-svc-extername01.yaml
kind: Service
apiVersion: v1
metadata:
name: myapp-svcname02
namespace: test01
spec:
type: ExternalName
externalName: myapp-svc02.test02.svc.cluster.local
备注:
name:myapp-是vcname02 指的是外部的服务名称
myapp-svc02.test02.svc.cluster.local 对方无头服务的名称.对方命令空间的名称.svc.cluster.local
可用以下命令查看
[root@k8s-master ~]# ku get svc -n test01
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp-svc01 ClusterIP None <none> 39320/TCP 8h
myapp-svcname02 ExternalName <none> myapp-svc02.test02.svc.cluster.local <none> 8h
备注:myapp-svc02.test02.svc.cluster.local为对方nds的名称,是能通过名称访问的
创建
[root@k8s-master ~]# ku create -f myapp-svc-extername01.yaml
3.第二个pod
(1)编写并创建命名空间为test02的pod
[root@k8s-master ~]# vim myapp02.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp02
namespace: test02
spec:
replicas: 1
selector: #标签选择器
matchLabels: #匹配的标签为
app: myapp02
release: canary
template:
metadata:
labels:
app: myapp02 #和上面的myapp要匹配
release: canary
spec:
containers:
- name: myapp02
image: ikubernetes/myapp:v1
ports:
- name: http02
containerPort: 80
创建pod
ku create -f myapp02.yaml
(2)创建无头(headless)service
[root@k8s-master ~]# vim myapp-svc-headless02.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-svc02
namespace: test02
spec:
selector:
app: myapp02 #挑选的pod还是myapp。一个pod可以有多个service
release: canary
clusterIP: None #None表示是无头service
ports:
- port: 39320 #service ip中的端口
targetPort: 80 #容器ip中的端口
创建无头service
ku create -f myapp-svc-headless02.yaml
(3)创键extername
[root@k8s-master ~]# vim myapp-svc-extername02.yaml
kind: Service
apiVersion: v1
metadata:
name: myapp-svcname01
namespace: test02
spec:
type: ExternalName
externalName: myapp-svc01.test01.svc.cluster.local
创建
ku create -f myapp-svc-extername02.yaml
4.验证pod间的通信
[root@k8s-master ~]# ku get pods -n test01
NAME READY STATUS RESTARTS AGE
myapp01-696c886d6b-2p5ks 1/1 Running 0 8h
[root@k8s-master ~]# ku exec -it myapp01-696c886d6b-2p5ks -n test01 -- sh
/ # ping myapp-svcname02
PING myapp-svcname02 (10.244.140.72): 56 data bytes
64 bytes from 10.244.140.72: seq=0 ttl=62 time=1.131 ms
64 bytes from 10.244.140.72: seq=1 ttl=62 time=0.300 ms
[root@k8s-master ~]# ku get pod -n test02 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp02-55ffcd5f64-w9tw4 1/1 Running 0 8h 10.244.140.72 node02 <none> <none>
[root@k8s-master ~]# ku get pod -n test01 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp01-696c886d6b-2p5ks 1/1 Running 0 8h 10.244.196.136 node01 <none> <none>
五。service的其他应用
service的多端口设置
一个容器应用可以提供多个端口的服务,在service的定义中可以相应的设置多个端口号
(1)创建文件
[root@k8s-master ~]# vim service-multiple-ports.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 8080
targetPort: 8080
name: web
protocol: TCP
- port: 8005
targetPort: 8005
name: management
protocol: TCP
selector:
app: webapp
注意:selector中的app:webapp的标签对应的是webapp-deployment.yaml的pod标签
(2)创建service
[root@k8s-master ~]# ku create -f service-multiple-ports.yaml
service/webapp created
(3)查看endpoint列表
[root@k8s-master ~]# ku describe svc webapp
Name: webapp
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=webapp
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.107.175.37
IPs: 10.107.175.37
Port: web 8080/TCP
TargetPort: 8080/TCP
Endpoints: 10.244.140.71:8080,10.244.196.135:8080
Port: management 8005/TCP
TargetPort: 8005/TCP
Endpoints: 10.244.140.71:8005,10.244.196.135:8005
Session Affinity: None
Events: <none>
(4)查看创建的service
[root@k8s-master ~]# ku get svc webapp
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp ClusterIP 10.107.175.37 <none> 8080/TCP,8005/TCP 96s