1 service作用
使用kubernetes集群运行工作负载时,由于Pod经常处于用后即焚状态,Pod经常被重新生成,因此Pod对应的IP地址也会经常变化,导致无法直接访问Pod提供的服务,Kubernetes中使用了Service来解决这一问题,即在Pod前面使用Service对Pod进行代理,无论Pod怎样变化,只要有Label,就可以让Service能够联系上Pod,把PodIP地址添加到Service对应的端点列表(Endpoints)实现对PodIP跟踪,进而实现通过Service访问Pod目的。
- 通过service为pod客户端提供访问pod方法,即可客户端访问pod入口
- 通过标签动态感知podIP地址变化等
- 防止pod失联
- 定义访问pod访问策略
- 通过label-selector相关联通过Service实现Pod的负载均衡(TCP/UDP 4层)
- 底层实现由kube-proxy通过userspace、iptables、ipvs三种代理模式
2 Kube-proxy三种代理模式
2.1 userspace 模式
userspace 模式是 kube-proxy 的最早实现方式,主要工作原理如下:
(1)kube-proxy 监听 Kubernetes API 服务器中 Service 和 Endpoint 的变化。
(2)当有新的 Service 创建时,kube-proxy 会在节点上打开一个端口,并将这个端口映射到 Service 对应的后端 Pod。
(3)任何对这个端口的访问请求,都会被 kube-proxy 捕捉,并转发到后端的 Pod。kube-proxy 使用用户空间程序来进行这些转发操作。
这种模式的优点是实现简单,但缺点是性能较低,因为每个数据包都需要经过用户空间的处理,增加了额外的开销和延迟。
2.2 iptables 模式
iptables 模式是 kube-proxy 的改进版,相比 userspace 模式有显著的性能提升。其工作原理如下:
(1)kube-proxy 同样监听 Kubernetes API 服务器中 Service 和 Endpoint 的变化。
不同的是,kube-proxy 使用 iptables 来设置网络规则。这些规则会直接在内核空间进行处理,而不是通过用户空间。
(2)当有新的 Service 创建时,kube-proxy 会生成相应的 iptables 规则,定义从 Service IP 和端口到后端 Pod 的 NAT 转发规则。
(3)数据包在内核空间直接被转发到相应的后端 Pod,减少了上下文切换,提高了转发性能。
iptables 模式的优点是性能更好,但在处理大量规则时,规则管理和更新可能会变得复杂。
2.3 ipvs 模式
ipvs 模式是 kube-proxy 的最新实现方式,使用 Linux 内核中的 IP Virtual Server (IPVS) 技术。其工作原理如下:
(1)kube-proxy 监听 Kubernetes API 服务器中 Service 和 Endpoint 的变化
(2)kube-proxy 使用 IPVS 来创建和维护负载均衡规则。IPVS 是内核中的一个模块,专门用于负载均衡,支持多种调度算法。
(3)当有新的 Service 创建时,kube-proxy 会使用 IPVS 创建相应的负载均衡规则,定义从 Service IP 和端口到后端 Pod 的转发规则。
(4)数据包在内核空间通过 IPVS 直接转发,性能更高,同时支持更多的负载均衡算法(如轮询、最小连接数、最短延迟等)。
(5)ipvs 模式的优点是性能最佳,支持更多的负载均衡算法和更复杂的网络规则,但需要内核支持 IPVS 模块。
2.4 iptables与ipvs对比
iptables
- 优点:灵活,功能强大(可以在数据包不同阶段对包进行操作)
- 缺点:表中规则过多时,响应变慢,即规则遍历匹配和更新,呈线性时延。
ipvs
- 优点:转发效率高。调度算法丰富:rr,wrr,lc,wlc,ip hash...。
- 缺点:内核支持不全,低版本内核不能使用,需要升级到4.0或5.0以上。
3 service类型
3.1 类型
ClusterlP
- 默认,分配一个集群内部可以访问的虚拟IP
NodePort
- 在每个Node上分配一个端口作为外部访问入口
- nodePort端口范围为:30000-32767
LoadBalancer
- 工作在特定的Cloud Provider上,例如Google Cloud,AWS,OpenStack
ExternalName
- 表示把集群外部的服务引入到集群内部中来,即实现了集群内部pod和集群外部的服务进行通信
3.2 service参数
port:访问service使用的端口
targetPort:Pod中容器端口
nodePort:通过Node实现外网用户访问k8s集群内service(30000-32767)
4 service创建
4.1 ClusterIP类型
ClusterlP根据是否生成ClusterlP又可分为普通service和handless service
普通Service:
为Kubernetes的Service分配一个集群内部可访问的固定虚拟IP(ClusterIP),实现集群内的访问。
Headless Service:
该服务不会分配ClusterIP,也不通过kube-proxy做反向代理和负载均衡。而是通过DNS提供稳定的网络ID来访问,DNS会将headless service的后端直接解析为pod IP列表。
4.1.1 普通ClusterIP service创建
通过命令行从Deployment创建Service
如果你有一个名为my-deployment的Deployment,你可以使用以下命令为其创建一个Service:
kubectl expose deployment my-deployment --port=80 --type=ClusterIP
再使用get和describe查看svc
通过IP地址访问:
service可以实现负载均衡的效果
创建一个拥有三个副本的deployment
[root@easzlab ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-55f598f8d-gcszt 1/1 Running 0 22m
nginx-deployment-55f598f8d-kj5nz 1/1 Running 0 22m
nginx-deployment-55f598f8d-p2dsr 1/1 Running 0 22m
以其中一个pod为例
[root@easzlab ~]# kubectl exec -it nginx-deployment-55f598f8d-p2dsr -- /bin/bash
root@nginx-deployment-55f598f8d-p2dsr:/# cd /usr/share/nginx/html
root@nginx-deployment-55f598f8d-p2dsr:/usr/share/nginx/html# echo "web1" > index.html
root@nginx-deployment-55f598f8d-p2dsr:/usr/share/nginx/html# exit
三个pod设置好后,使用curl访问,可以看到每次访问的都是不同的pod,可以实现出负载均衡的效果。
通过资源清单创建service
1.创建Deployment的YAML文件
apiVersion: apps/v1
kind: Deployment
metadata:name: my-nginx-deployment
spec:replicas: 2selector:matchLabels:app: my-nginxtemplate:metadata:labels:app: my-nginxspec:containers:- name: nginximage: nginx:1.19.0ports:- containerPort: 80
2.创建Service的YAML文件
apiVersion: v1
kind: Service
metadata:name: my-nginx-service
spec:selector:app: my-nginxports:- protocol: TCPport: 80targetPort: 80type: ClusterIP
验证资源是否创建成功
kubectl get deployments
kubectl get services
查看资源详情
kubectl describe deployment my-nginx-deployment
kubectl describe service my-nginx-service
4.1.2 Headless Service
- 普通的ClusterlP service是service name解析为cluster ip,然后cluster ip对应到后面pod ip
- Headless service是指service name直接解析为后面的pod ip
特点和行为
- DNS记录:Headless Service会在Kubernetes DNS中为每个匹配的Pod创建一个A记录(Address Record),直接指向Pod的IP地址。
- 无负载均衡:由于没有ClusterIP,Headless Service不提供内置的负载均衡。
- 适用于StatefulSets:Headless Service非常适合与StatefulSets一起使用,因为StatefulSets为每个Pod提供了一个稳定的网络标识和持久存储。
创建Service的YAML文件
apiVersion: v1
kind: Service
metadata:name: headless-service
spec:clusterIP: None # 设置为None以创建Headless Serviceselector:app: my-app # 设置标签,对应deploymentports:- protocol: TCPport: 80targetPort: 80
DNS解析
在集群内部,可以使用nslookup或dig命令来解析Headless Service的DNS记录:
kubectl run -it --rm --image=tutum/dnsutils dnsutils -- nslookup headless-service.default.svc.cluster.local.
这将显示与Service选择器匹配的Pods的IP地址列表。
4.2 NodePort类型
NodePort Service通常用于以下场景:
- 当你需要从集群外部访问服务,但不想使用外部负载均衡器时。
- 作为临时解决方案,直到设置好外部负载均衡器。
- 允许外部流量直接进入集群,但通过特定的节点端口。
创建NodePort Service
apiVersion: v1
kind: Service
metadata:name: nginx-nodeport
spec:type: NodePort # 指定Service类型为NodePortselector:app: nginxports:- protocol: TCPport: 80 # Service端口,集群内部访问的端口targetPort: 80 # 转发到Pod的端口nodePort: 30080 # 节点上的静态端口,外部访问的端口
应用Service
kubectl apply -f nginx-nodeport.yaml
验证Service
kubectl get services
访问Service
一旦NodePort Service创建成功,你可以通过任何节点的IP地址加上nodePort来访问服务。
http://<任一节点IP>:30080
4.3 LoadBalancer类型
LoadBalancer是Kubernetes Service的一种类型,它为服务提供了一个外部可访问的IP地址。这个IP地址是通过云服务提供商的负载均衡器实现的,它可以将流量分发到服务背后的多个Pod。
创建LoadBalancer Service
apiVersion: v1
kind: Service
metadata:name: nginx-lb-service
spec:type: LoadBalancerselector:app: nginxports:- protocol: TCPport: 80targetPort: 80
应用Service
kubectl apply -f nginx-lb-service.yaml
检查Service状态
kubectl get services
访问LoadBalancer服务
http://<EXTERNAL-IP>
4.4 ExternalName类型
ExternalName 是一种特殊类型的Service,它不是用来创建一个集群内部的服务代理或负载均衡器,而是用于将服务名映射到一个外部的DNS名称。这种服务类型通常用于引用集群外的服务,例如指向一个外部的数据库、API服务或其他基础设施服务。
4.4.1 将公网域名引入
1. 创建ExternalName Service的YAML定义
创建一个名为externalname-service.yaml的YAML文件,定义一个ExternalName类型的Service:
apiVersion: v1
kind: Service
metadata:name: external-service
spec:type: ExternalNameexternalName: www.baidu.com # 外部DNS名称
2. 应用Service定义
kubectl apply -f externalname-service.yaml
3. 验证Service
kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
external-service ExternalName <none> www.baidu.com <none> 15h
4. 访问Service
在集群内的Pod可以通过Service名称访问外部服务。例如,如果集群内的Pod需要访问www.baidu.com,它们可以使用external-service.default.svc.cluster.local来解析DNS。
5. DNS解析
在集群内部,可以使用nslookup或dig命令来解析ExternalName Service的DNS记录:
kubectl run -it expod --image=busybox:1.28
# nslookup www.baidu.com
# nslookup external-service.default.svc.cluster.local.
这将显示外部DNS名称www.baidu.com。
4.5 sessionAffinity
sessionAffinity 是 Service 资源的一个属性,它用来控制在一个客户端的多个请求是否应该始终路由到同一个Pod。这种特性被称为会话亲和性(Session Affinity),它对于需要保持用户会话状态的应用非常有用,例如在Web应用程序中。
apiVersion: v1
kind: Service
metadata:name: my-service
spec:selector:app: my-appports:- protocol: TCPport: 80targetPort: 9376sessionAffinity: ClientIP # 启用基于客户端IP的会话亲和性type: ClusterIP
- sessionAffinity: ClientIP
- 当设置为 ClientIP 时,启用基于客户端IP地址的会话亲和性。这意味着来自同一个客户端IP地址的所有请求将始终被路由到同一个Pod,只要该Pod仍然健康并且可用。
- 这个特性对于需要保持用户会话状态的应用非常有用,例如,用户登录状态或购物车信息。
- 会话亲和性持续时间由 sessionAffinityConfig 中的 timeoutSeconds 属性控制,如果Pod不可用,超时后客户端请求可以被路由到其他Pod。
- sessionAffinity: None
- 当设置为 None 或者不设置 sessionAffinity 属性时,表示不启用会话亲和性。客户端请求将不会被限制到特定的Pod,而是根据Service的负载均衡算法(通常是轮询)分配到可用的Pod。
- 这种方式适用于无状态的应用,其中每个请求都是独立的,不需要保持会话状态。