01 引言
声明:本文为《Kubernetes权威指南:从Docker到Kubernetes实践全接触(第5版)》的读书笔记
Service 主要用于提供网络服务,通过Servicel的定义,能够 为客户端应用提供稳定的访问地址(域名或IP地址)和负载均衡功能,以及屏蔽后端Endpoint的变化,是Kubernetes实现微服务的核心资源。
本文详细讲解下Service的相关概念及原理。
02 Service的概念
2.1 Service的概念
下面演示在没有Service之前,是如何访问一个多副本的应用容器组提供的服务。
以Tomcat容器为例,其Deployment资源文件定义如下:
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: - containerPort: 8080
创建完成后,查看每个pod的ip地址:
客户端应用可以直接通过这两个Pod的IP地址和端口号8080访问Web服务,例如:curl 10.0.95.22:8080 :
但是,提供服务的容器应用通常是分布式的,通过多个Pod副本共同提供服务(还需要考虑扩缩容的问题),要实现动态感知服务后端实例的变化,会大大增加客户端系统实现的复杂度,为了解决这个问题,Kubernetes引入 Service资源类型 。
2.2 概念
Service实现的是微服务架构中的几个核心功能:全自动的服务注册、服务发现、 服务负载均衡等。
2.2.1 创建Service的方式
2.2.1.1 使用kubectl expose命令创建
命令如下:
kubectl expose deployment webapp service/webapp exposed
查看新创建的Service,可以看到系统为它分配了一个虚拟IP
地址(ClusterIP
地址),Service的端口号则从Pod中的containerPort复制而来:
通过curl 169.169.140.242:8080 也是可以访问的。访问时,会被自动负载分发到了后端两个Pod之一:10.0.95.22:8080或10.0.95.23:8080。
2.2.1.2 资源文件创建
除了使用命令,还可以使用yaml资源文件的方式来创建:
apiVersion: v1 kind: Service metadata: name: webapp spec: ports: - protocol: TCP port: 8080 targetPort: 8080 selector: app: webapp
Service定义中的关键字段是ports和selector:
- ports:定义部分指定了Service本身的端口号为8080;
- targetPort:指定后端Pod的容器端口号;
- selector:定义部分设置的是后端Pod所拥有的
label:app=webapp
。
使用kubectl create
命令创建后,能看到和使用kubectl expose
命令创建Service的效果一样。
2.2.2 Endpoint
一个Service对应的 “后端” 由Pod的IP和容器端口号组成,这在k8s系统中称为Endpoint。
可以通过kubectl descirbe svc
命令查看Endpoint列表,如:
Kubernetes自动创建了与Service关联的Endpoint资源对象,这可以通过查询Endpoint对象讲行查看:
03 负载均衡机制
当一个 Service 对象在 Kubernetes 集群中被定义出来时,集群内的客户端应用就可以通过服务IP访问到具体的Pod容器提供的服务了。
从服务IP到后端Pod的负载均衡机制,则是由每个Node上的kube-proxy负责实现的。通过Service的负载均衡机制,Kubernetes实现了一种分布式应用的统一入口,免去了客户端应用获知后端服务实例列表和变化的复杂度。。
3.1 kube-proxy的代理模式
目前kube-proxy提供了以下代理模式(通过启动参数--proxy-mode
设置):
模式 | 描述 |
userspace模式 | 用户空间模式,由kube-proxy完成代理的实现,效率最低,不再推荐使用 |
iptables模式 | kube-proxy通过设置Linux Kernel的iptablesi规则,实现从Service到后端Endpoint列表的负载分发规则,效率很高。 —————————— 但是,如果某个后端Endpoint在转发时不可用,此次客户端请求就会得到失败的响应,相对于 userspace模式来说更不可靠,此时应该通过为Pod设置readinessprobe(服务可用性健康检查)来保证只有达到ready状态的Endpoint才会被设置为Service的后端Endpoint。 |
ipvs模式 | 在Kubernetes1.11版本中达到Stable阶段,kube-proxy通过设置Linux Kernel的netlink接口设置IPVS规则,转发效率和支持的吞吐率都是最高的。 ipvs模式要求Linux Kernel启用IPVS模块,如果操作系统未启用IPVS内核模块,kube-proxy则会自动切换至iptables模式。同时ipvs模式支持更多的负载均衡策略,如下所述: —————————— rr(round-robin):轮询; lc(least connection):最小连接数; dh(destination hashing):目的地址哈希; sh(source hashing):源地址哈希; sed(shortest expected delay):最短期望延时; nq(never queue):永不排队。 |
kernelspace模式 | Windows Server上的代理模式 |
3.2 会话保持模式
Service支持通过设置sessionAffinity实现基于客户端IP的会话保持机制,即:首次将某个客户端来源IP发起的请求转发到后端的某个Pod上,之后从相同的客户端 IP发起的请求都将被转发到相同的后端Pod上。
配置参数为 service.spec.sessionAffinity
,也可以设置会话保持的最长时间(service.spec.sessionAffinityConfig.clientIP.timeoutSeconds
),例如下面的服务将会话保持时间设置为10800s(3h):
apiVersion: v1 kind: Service metadata: name: webapp spec: sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSecondes: 10080 ports: - protocol: TCP port: 8080 targetPort: 8080 selector: app: webapp
04 Service的多端口设置
一个容器应用可以提供多个端口的服务,在Service的定义中也可以相应地设置多个端口号。
在下面的例子中,Service设置了两个端口号来分别提供不同的服务,如web服务和management服务(下面为每个端口号都进行了命名,以便区分):
apiversion: v1 kind: Service metadata: name: webapp spec: ports: - port: 8080 targetPort: 8080 name: web - port: 8005 targetPort: 8005 name: management selector: app: webapp
另一个例子是同一个端口号使用的协议不同,如TCP和UDP,也需要设置为
多个端口号来提供不同的服务:
apiVersion: v1 kind: Service metadata: name: kube-dns namespace: kube-system labels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" kubernetes.io/name: "KubeDNS" spec: selector: k8s-app: kube-dns clusterIP: 169.169.0.100 ports: - name: dns port: 53 protocol: UDP - name: dns-tcp port: 53 protocol: TCP
05 将外部服务定义为Service
普通的Service通过Label Selector对后端Endpoint列表进行了一次抽象,如果后端的Endpoint不是由Pod副本集提供的,则Service还可以抽象定义任意其他服务,将一个Kubernetes集群外部的已知服务定义为Kubernetes内的一个Service, 供集群内的其他应用访问。
5.1 场景
常见的应用场景包括:
- 已部署的一个集群外服务:例如数据库服务、缓存服务等;
- 其他Kubernetes集群的某个服务;
- 迁移过程中对某个服务进行Kubernetes内的服务名访问机制的验证。
Service指向外部服务如下图所示:
5.2 定义
对于这种应用场景,用户在创建Service
资源对象时不设置Label Selector
(后端Pod
也不存在),同时再定义一个与Service
关联的Endpoint
资源对象,在Endpoint
中设置外部服务的IP
地址和端口号,例如:
apiVersion: v1 kind: Service metadata: name: my-service spec: ports: - protocol: TCP port: 80 targetPort: 80 ----------- apiversion: v1 kind: Endpoints metadata: name: my-service subsets: - addresses: - IP: 1.2.3.4 ports: - port: 80