(七)Kubernetes Service资源
#
#
Kubernetes Pod
是平凡的,由Deployment
等控制器管理的Pod
对象都是有生命周期的,它们会被创建,也会意外挂掉。虽然它们可以由控制器自动重建或者滚动更新,但是重建或更新之后的Pod
对象的IP地址等都会发生新的变化。这样就会导致一个问题,如果一组Pod
(称为backend
)为其它Pod
(称为frontend
)提供服务,那么那些frontend
该如何发现,并连接到这组Pod
中的哪些backend
呢? 这时候就用到了:Service
示例说明为什么要使用Service
如下图所示,当Nginx Pod
作为客户端访问Tomcat Pod
中的应用时,IP
的变动或应用规模的缩减会导致客户端访问错误。而Pod
规模的扩容又会使得客户端无法有效的使用新增的Pod
对象,从而影响达成规模扩展之目的。为此,Kubernetes
特地设计了Service
资源来解决此类问题。
#
#
一个
Service
对象就是工作节点上的一些iptables
或ipvs
规则,用于将到达Service
对象IP
地址的流量调度转发至相应的Endpoints
对象指定的IP
地址和端口之上。kube-proxy
组件通过API Server
持续监控着各Service
及其关联的Pod
对象,并将其创建或变动实时反映到当前工作节点上的iptables
规则或ipvs
规则上。
ipvs
是借助于Netfilter
实现的网络请求报文调度框架,支持rr
、wrr
、lc
、wlc
、sh
、sed
和nq
等十余种调度算法,用户空间的命令行工具是ipvsadm
,用于管理工作与ipvs
之上的调度规则。
Service IP
事实上是用于生成iptables
或ipvs
规则时使用的IP
地址,仅用于实现Kubernetes
集群网络的内部通信,并且能够将规则中定义的转发服务的请求作为目标地址予以相应,这也是将其称为虚拟IP
的原因之一。
#
#
相对userspace
模式来说,iptables
模式无须将流量在用户空间和内核空间来回切换,因而更加高效和可靠。其缺点是iptables
代理模型不会在被挑中的Pod
资源无响应时自动进行重定向;而userspace
模式则可以。
#
#
[root@k8s-master ~]# kubectl run nginx --image=nginx:1.12 --replicas=3 #创建pod资源指定副本数量为3个 [root@k8s-master ~]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-67685f79b5-688s7 1/1 Running 0 5s 10.244.2.61 k8s-node2 <none> <none> nginx-67685f79b5-gpc2f 1/1 Running 0 5s 10.244.1.63 k8s-node1 <none> <none> nginx-67685f79b5-grlrz 1/1 Running 0 5s 10.244.2.60 k8s-node2 <none> <none> [root@k8s-master ~]# kubectl get deployment #查看deployment控制器资源 NAME READY UP-TO-DATE AVAILABLE AGE nginx 3/3 3 3 35s
#下面这条命令表示为deployment控制器资源nginx创建一个service对象,并取名为nginx、端口为80、pod内暴露端口80、协议为TCP协议。 [root@k8s-master ~]# kubectl expose deployment nginx --name=nginx --port=80 --target-port=80 --protocol=TCP service/nginx exposed [root@k8s-master ~]# kubectl get service #查看创建的service资源 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 27d nginx ClusterIP 10.104.116.156 <none> 80/TCP 9s
[root@k8s-master ~]# kubectl get endpoints NAME ENDPOINTS AGE kubernetes 192.168.1.31:6443 27d nginx 10.244.1.63:80,10.244.2.60:80,10.244.2.61:80 29s
二、资源清单定义
[root@k8s-master ~]# vim manfests/service-demo.yaml apiVersion: apps/v1 kind: Deployment metadata: name: service-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: service-deploy-demo template: metadata: name: svc-deploy labels: app: service-deploy-demo spec: containers: - name: svc-pod image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 --- #定义service apiVersion: v1 kind: Service metadata: name: service-demo #service名称 spec: selector: #用于匹配后端的Pod资源对象,需和上面定义pod的标签一致 app: service-deploy-demo ports: - port: 80 #service端口号 targetPort: 80 #后端Pod端口号 protocol: TCP #使用的协议
2)创建并查看
[root@k8s-master ~]# kubectl apply -f manfests/service-demo.yaml #创建资源对象 deployment.apps/service-deploy created service/service-demo created [root@k8s-master ~]# kubectl get svc #查看service资源对象,"kubectl get svc"等于"kubectl get service" NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 27d nginx ClusterIP 10.104.116.156 <none> 80/TCP 80m service-demo ClusterIP 10.98.31.157 <none> 80/TCP 7s [root@k8s-master ~]# kubectl get pods -o wide -l app=service-deploy-demo #查看pod资源对象 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES service-deploy-66548cc57f-982cd 1/1 Running 0 15s 10.244.2.63 k8s-node2 <none> <none> service-deploy-66548cc57f-blnvg 1/1 Running 0 15s 10.244.1.67 k8s-node1 <none> <none> service-deploy-66548cc57f-vcmxb 1/1 Running 0 15s 10.244.2.62 k8s-node2 <none> <none> [root@k8s-master ~]# kubectl get endpoints service-demo 查看生成的endpoints对应关系 NAME ENDPOINTS AGE service-demo 10.244.1.67:80,10.244.2.62:80,10.244.2.63:80 43s
3)节点访问测试(这里使用创建一个新的pod资源模拟客户端进行访问)
[root@k8s-master ~]# kubectl run busybox --image=busybox --rm -it -- /bin/sh #使用busybox创建一个临时pod客户端 / # wget -O - -q http://10.98.31.157/ #访问上面创建的service对象的Cluster IP Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a> / # for i in 1 2 3 4; do wget -O - -q http://10.98.31.157/hostname.html; done #循环访问测试站点下的hostname.html页面,可以看出是轮循的分配给后端的pod资源。 service-deploy-66548cc57f-982cd service-deploy-66548cc57f-blnvg service-deploy-66548cc57f-982cd service-deploy-66548cc57f-982cd #说明:myapp容器中的“/hostname.html"页面能够输出当前容器的主机名。
#
Service
资源支持Session affinity
(粘性会话或会话粘性)机制,能够将来自同一个客户端的请求始终转发至同一个后端的Pod
对象。这意味着会影响调度算法的流量分发功能,进而降低其负载均衡的效果。所以,当客户端访问pod
中的应用程序时,如果有基于客户端身份保存某些私有信息,并基于这些私有信息追踪用户的活动等一类的需求时,就可以启用session affinity
机制。
Session affinity
的效果仅在一段时间期限内生效,默认值为10800
秒,超出此时长之后,客户端的再次访问会被调度算法重新调度。Service
资源的Session affinity
机制仅能基于客户端的IP
地址识别客户端身份,把经由同一个NAT
服务器进行源地址转换的所有客户端识别为同一个客户端,便导致调度效果不佳,所以,这种方法并不常用。
Service
资源通过service.spec.sessionAffinity
和service.spec.sessionAffinityConfig
两个字段配置粘性会话。sessionAffinity
字段用于定义要使用的粘性会话的类型,仅支持使用“None”
和“ClientIp”
两种属性值。
None:不使用
sessionAffinity
,默认值。ClientIP:基于客户端
IP
地址识别客户端身份,把来自同一个源IP
地址的请求始终调度至同一个Pod
对象。
示例
[root@k8s-master ~]# vim manfests/service-demo.yaml ...... spec: selector: app: service-deploy-demo ports: - port: 80 targetPort: 80 protocol: TCP sessionAffinity: ClientIP #指定使用ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 10 #配置session超时时间,这里为了看效果设置的比较短 [root@k8s-master ~]# kubectl apply -f manfests/service-demo.yaml deployment.apps/service-deploy unchanged #同样使用pod客户端访问测试 / # for i in 1 2 3 4; do wget -O - -q http://10.98.31.157/hostname.html; done service-deploy-66548cc57f-blnvg service-deploy-66548cc57f-blnvg service-deploy-66548cc57f-blnvg service-deploy-66548cc57f-blnvg #等待10秒过后再次访问 / # for i in 1 2 3 4; do wget -O - -q http://10.98.31.157/hostname.html; done service-deploy-66548cc57f-vcmxb service-deploy-66548cc57f-vcmxb service-deploy-66548cc57f-vcmxb service-deploy-66548cc57f-vcmxb
#
ClusterIP:通过集群内部
IP
地址暴露服务,此地址仅在集群内部可进行通行,无法被集群外部的客户端访问。NodePort:通过每个
Node
上的IP
和静态端口(NodePort
)暴露服务,会自动为Service
分配集群IP
地址,并将此作为NodePort
的路有目标。通过请求<NodePort>:<NodePort> --> <ClusterIP>:<ClusterPort> --> <PodIP>:<ContainerPort>
访问到一个NodePort
服务。LoadBalancer:构建在
NodePort
之上,并创建一个外部负载均衡器,路由到ClusterIP
。因此LoadBalancer
一样具有NodePort
和ClusterIP
。EXternalName:通过返回
CNAME
和它的值,可以将服务映射到externalName
字段的内容。换言之,此种类型并非定义由Kubernetes
集群提供的服务,而是把集群外部的某服务以DNS CNAME
记录的方式映射到集群内,从而让集群内的Pod
资源能够访问外部的Service
的一种实现方式。这种类型的Service
没有ClusterIP
和NodePort
,也没有标签选择器用于选择Pod
资源,因此也不会有Endpoints
存在。
#
[root@k8s-master ~]# vim manfests/redis-svc.yaml #编写yaml格式的清单文件 apiVersion: apps/v1 kind: Deployment metadata: name: redis-deploy namespace: default spec: replicas: 2 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis-pod image: redis ports: - name: redis containerPort: 6379 --- apiVersion: v1 kind: Service metadata: name: redis-svc #service对象名 spec: type: ClusterIP #这里指定使用ClusterIP,默认也是ClusterIP,这里可有可无 selector: app: redis #匹配上面定义的pod资源 ports: - port: 6379 #service端口 targetPort: 6379 #后端pod端口 protocol: TCP #协议 [root@k8s-master ~]# kubectl apply -f manfests/redis-svc.yaml #创建资源对象 deployment.apps/redis-deploy created service/redis-svc created
2)查看创建的资源对象
[root@k8s-master ~]# kubectl get svc #查看service资源 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 27d nginx ClusterIP 10.104.116.156 <none> 80/TCP 17h redis-svc ClusterIP 10.102.44.127 <none> 6379/TCP 8s service-demo ClusterIP 10.98.31.157 <none> 80/TCP 16h [root@k8s-master ~]# kubectl get pods -l app=redis -o wide #查看标签app=redis的pod资源 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES redis-deploy-6559cc4c4c-5v7kx 1/1 Running 0 33s 10.244.2.65 k8s-node2 <none> <none> redis-deploy-6559cc4c4c-npdtf 1/1 Running 0 33s 10.244.1.69 k8s-node1 <none> <none> [root@k8s-master ~]# kubectl describe svc redis-svc #查看redis-svc资源对象详细信息 Name: redis-svc Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"redis-svc","namespace":"default"},"spec":{"ports":[{"port":6379,"... Selector: app=redis Type: ClusterIP IP: 10.102.44.127 Port: <unset> 6379/TCP TargetPort: 6379/TCP Endpoints: 10.244.1.69:6379,10.244.2.65:6379 #可以看出这里已经和上面的pod资源绑定 Session Affinity: None Events: <none>
3)集群内部进行测试
#(1)集群内部的节点上面测试 [root@k8s-master ~]# redis-cli -h 10.102.44.127 10.102.44.127:6379> ping PON #(2)在后端pod上面进行测试 [root@k8s-master ~]# kubectl exec redis-deploy-6559cc4c4c-5v7kx -it -- /bin/sh # redis-cli -h 10.102.44.127 10.102.44.127:6379> ping PONG
#
[root@k8s-master ~]# vim manfests/nginx-svc.yaml #编写yaml格式的清单文件 apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy namespace: default spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx-pod image: nginx:1.12 ports: - name: nginx containerPort: 6379 --- apiVersion: v1 kind: Service metadata: name: nginx-svc #service对象名 spec: type: NodePort #这里指定使用ClusterIP,默认也是ClusterIP,这里可有可无 selector: app: nginx #匹配上面定义的pod资源 ports: - port: 80 #service端口 targetPort: 80 #后端pod端口 nodePort: 30080 #节点端口 protocol: TCP #协议 [root@k8s-master ~]# kubectl apply -f manfests/nginx-svc.yaml #创建资源对象 deployment.apps/nginx-deploy created service/nginx-svc created
2)查看创建的资源对象
[root@k8s-master ~]# kubectl get svc #查看service资源 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 27d nginx-svc NodePort 10.105.21.137 <none> 80:30080/TCP 4s redis-svc ClusterIP 10.102.44.127 <none> 6379/TCP 55m service-demo ClusterIP 10.98.31.157 <none> 80/TCP 16h [root@k8s-master ~]# kubectl get pods -l app=nginx -o wide #查看标签app=nginx的pod资源 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-deploy-b6f876447-nlv6h 1/1 Running 0 33s 10.244.1.71 k8s-node1 <none> <none> nginx-deploy-b6f876447-xmn2t 1/1 Running 0 33s 10.244.2.66 k8s-node2 <none> <none> [root@k8s-master ~]# kubectl describe svc nginx-svc #查看nginx-svc资源对象详细信息 Name: nginx-svc Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"nginx-svc","namespace":"default"},"spec":{"ports":[{"nodePort":30... Selector: app=nginx Type: NodePort IP: 10.105.21.137 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 30080/TCP #这里可以看到多了NodePort且端口为30080 Endpoints: 10.244.1.71:80,10.244.2.66:80 Session Affinity: None External Traffic Policy: Cluster Events: <none>
3)集群外部进行测试
[root@courtoap ~]# curl 192.168.1.31:30080 #访问集群master节点的30080端口 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> [root@courtoap ~]# curl 192.168.1.32:30080 #访问集群node节点的30080端口 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style>
#
Service
对象隐藏了各Pod
资源,并负责将客户端请求流量调度至该组Pod
对象之上,但也可能存在客户端直接访问Service
资源后端的所有Pod
资源,这时就应该向客户端暴露每个Pod
资源的IP
地址,而不是中间层Service
对象的ClusterIP
,这种类型的Service
资源便称为Headless Service
(无头服务)。
Headless Service
对象没有ClusterIP
,因此便没有相关负载均衡或代理问题,其如何为此类Service
配置IP
地址,其取决于标签选择器的定义。
具有标签选择器:端点控制器(
Endpoints Controller
)会在API
中为其创建Endpoints
记录,并将ClusterDNS
服务中的A
记录直接解析到此Service
后端的各Pod
对象的IP
地址上。没有标签选择器:端点控制器(
Endpoints Controller
)不会再API
中为其创建Endpoints
记录,ClusterDNS
的配置分为两种情形,对ExternalName
类型的服务创建CNAME
记录,对其他三种类型来说,为那些与当前Service
共享名称的所有Endpoints
对象创建一条记录。
#
[root@k8s-master ~]# vim manfests/httpd-svc-headless.yaml apiVersion: apps/v1 kind: Deployment metadata: name: httpd-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: httpd template: metadata: labels: app: httpd spec: containers: - name: httpd-pod image: httpd ports: - name: httpd containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: httpd-svc #service对象名 spec: clusterIP: None #将ClusterIP字段设置为None即表示为headless类型的service资源对象 selector: app: httpd #匹配上面定义的pod资源 ports: - port: 80 #service端口 targetPort: 80 #后端pod端口 protocol: TCP #协议 [root@k8s-master ~]# kubectl apply -f manfests/httpd-svc-headless.yaml deployment.apps/httpd-deploy created service/httpd-svc created
2)查看创建的资源对象
[root@k8s-master ~]# kubectl get svc #查看service资源 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd-svc ClusterIP None <none> 80/TCP 4s kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 27d nginx-svc NodePort 10.105.21.137 <none> 80:30080/TCP 112m redis-svc ClusterIP 10.102.44.127 <none> 6379/TCP 168m service-demo ClusterIP 10.98.31.157 <none> 80/TCP 18h [root@k8s-master ~]# kubectl get pods -l app=httpd -o wide #查看标签app=httpd的pod资源 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-deploy-5494485b74-4vx64 1/1 Running 0 27s 10.244.2.72 k8s-node2 <none> <none> httpd-deploy-5494485b74-j6hwm 1/1 Running 0 27s 10.244.2.71 k8s-node2 <none> <none> httpd-deploy-5494485b74-jn48q 1/1 Running 0 27s 10.244.1.74 k8s-node1 <none> <none> [root@k8s-master ~]# kubectl describe svc/httpd-svc #查看httpd-svc资源对象详细信息 Name: httpd-svc Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"httpd-svc","namespace":"default"},"spec":{"clusterIP":"None","por... Selector: app=httpd Type: ClusterIP IP: None Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 10.244.1.74:80,10.244.2.71:80,10.244.2.72:80 Session Affinity: None Events: <none>
3)测试资源发现
#(1)通过创建一个专用的测试Pod资源对象,而后通过其交互式接口进行测试 [root@k8s-master ~]# kubectl run cirror-$RANDOM --rm -it --image=cirros -- /bin/sh / # nslookup httpd-svc Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: httpd-svc Address 1: 10.244.2.71 10-244-2-71.httpd-svc.default.svc.cluster.local Address 2: 10.244.1.74 10-244-1-74.httpd-svc.default.svc.cluster.local Address 3: 10.244.2.72 10-244-2-72.httpd-svc.default.svc.cluster.local #(2)直接在kubernetes集群上解析 [root@k8s-master ~]# dig -t A httpd-svc.default.svc.cluster.local. @10.96.0.10 ...... ;; ANSWER SECTION: httpd-svc.default.svc.cluster.local. 26 IN A 10.244.2.72 httpd-svc.default.svc.cluster.local. 26 IN A 10.244.2.71 httpd-svc.default.svc.cluster.local. 26 IN A 10.244.1.74 ......
作者:别来无恙-
出处:https://www.cnblogs.com/yanjieli/p/11855825.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类