- Service?将运行在同一组Pods上的应用程序公开为网络服务的抽象方法。Kubernetes为Pods提供了Ip地址,并为一组Pod提供了相同的DNS,他们之间可以实现负载均衡。
- 为何要使用Service?使用Deployment,Pod能被动态创建/销毁,为了跟踪提供功能的Pod而使用Service。
- Kubernetes Service定义了一种抽象:逻辑上的一组Pod和一种可以访问他们策略——微服务。这一组Pod能够被Service访问,通常是通过选择算符实现。
以上内容,举例:考虑一个图片处理后端,他运行了三个副本,副本之间可以呼唤,前端不需要关心调用了哪个后端副本,但形成一组的后端程序的Pod是可能发生变成的,Service的定义的抽象解耦了前端(Client)和后端(Pods)的管理。 - Service在Kubernetes中本质是REST对象,(Representational State Transfer,表述性状态传递),基于POST方式,请求API server创建实例。
Service配置例子:
在这里插入代码片apiVersion: v1
kind: Service //表明这是一个Service
metadata:
name: my-service //Service对象名
spec:
selector:
app: MyApp //标签为MyApp的**Pod**
ports:
- protocol: TCP
port: 80 //TCP协议使用的端口
targetPort: 9376 //Pod暴露的端口
- 若服务没有选择算符(Selector),则不会自动创建Endpoint对象。可以手动添加Endpoint对象,将服务手动映射到运行该服务的ip地址和端口
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- ip: 192.0.2.42
ports:
- port: 9376
-
kubernetes集群的每个节点都运行了kube-proxy提供了**VIP (virtual IP)**的形式,而不是ExternalName。
-
代理模式:
①userspace代理模式
②iptables代理模式
③IPVS(IP Virtual Server)
-
多端口Service,定义时需要提供所有的端口名乘避免歧义。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
- 服务发现:
①环境变量
Pod运行在Node上,kubernetes为活跃的Service添加一组环境变量
一个名称为 “redis-master” 的 Service 暴露了 TCP 端口 6379, 同时给它分配了 Cluster IP 地址 10.0.0.11,这个 Service 生成了如下环境变量:
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
②DNS
所有 Pod 都应该能够通过其 DNS 名称自动解析服务。Kubernetes DNS 服务器是唯一一种能够访问ExternalName类型的Service方式。
-
Headless Services (无头服务) Cluster IP的值为“None”
①带Selector
Endpoint控制器在API内创建Endpoints记录,修改DNS配置返回的A地址,通过这地址直达Service后端Pod。
②不带Selector
Endpoint控制器不会创建Endpoints记录。DNS系统会查找和配置。 -
发布服务——服务类型
①ClusterIP(默认Service Type)
通过集群内部IP暴露服务,服务只能够在集群内可以访问
②NodePort
通过每个Node上的IP和静态端口暴露服务。该类型服务会路由到ClusterIP Service(自动创建)上。通过**< Node IP >:< NodePort>** 指令,可以在集群外部访问NodePort服务
③LoadBalancer
使用云提供商(Cloud Provider)的负载均衡器,向外暴露服务。外部的负载均衡器可以路由到NodePort和ClusterIP。
④ExternalName
通过返回CNAME和它的值,可以将服务映射到externalName字段的内容。不需要创建任何类型代理。
PS:CNAME 理解为别名,如下
yy.com → http://www.xx.com → 111.111.111.111 -
虚拟IP实施(内部原理)
①避免冲突
Kubernetes最主要的哲学之一,是用户不应该暴露那些导致他们操作失败、但又不是他们的错误的场景。对于Service资源的设计,这意味着如果用户的选择有可能与他人冲突,那就不要让用户自行选择端口号。这是一个隔离性的失败。
为了使用户能够为他们的Service选择一个端口号,我们必须确保不能有2个Service发生冲突。Kubernetes通过为每个Service分配他们自己的IP地址来实现。
为了保证每个Service被分配到唯一的IP,需要一个内部的分配器能够原子地更新etcd数据库中的一个全局分配映射表,这个更新操作要先于创建每一个Service。为了Service能够获取IP,这个映射表对象必须在注册中心存在,否则创建Service将会失败,知识一个IP不能被分配。
在控制平面中,一个后台Controller的职责是创建映射表(需要支持从使用了内存锁的Kubernetes的旧版本迁移过来)。同时Kubernetes会通过控制器检查不合理的分配(如管理员干预导致)以及清理已被分配但不再被任何Service使用的IP地址。
②Service IP 地址
不像Pod的IP地址,Service IP地址实际路由到一个固定的目的地,Service的IP实际上不能通过单个主机进行应答。相反,我们使用iptables(Linux中数据包处理逻辑)来定义一个虚拟IP地址(VIP),它可以根据需求而透明地进行重定向。当客户端连接到VIP时,他们的流量会自动地传输到一个合适的Endpoint。环境变量和DNS,实际上根据Service的VIP和端口进行填充。
kube-proxy支持三种代理模式:userspace,iptables,IPVS。他们各自的操作略有不同。
③Userspace
作为一个例子,考虑前面提到的图片处理应用程序。当创建后端Service时,Kubernetes master(主节点) 指派一个虚拟IP地址,比如10.0.0.1.假设Service的端口是1234,该Service会被集群中所有的kube-proxy实例观察到。当代理看见一个新的Service,它会打开一个新的端口,建立一个从该VIP重定向到新端口的iptables,并开始接受请求连接。当一个客户端连接到一个VIP,iptables规则开始起作用,他会重定向该数据包到服务代理端口。服务代理选择一个后端并将客户端的流量代理到后端上。
这意味着Service所有者能够选择任何他们想要使用的端口而不存在冲突的风险。客户端可以简单连接到一个IP和端口而不需要知道实际访问了哪些Pod
④IPtables
再次考虑前面提到的图片处理应用程序。当创建后端Service时,Kubernetes控制面板会给它指派一个虚拟IP地址比如10.0.0.1,假设Service端口1234,该Service会被集群中所有的kube-proxy实例观察到。当代理看到一个新的Service,它会配置一系列的IPtables规则,从VIP重定向到每个Service规则。该特定于Service的规则连接到特定于Endpoint规则。基于Endpoint的规则会重定向刀后端Pod(目标地址转译)。
当客户端连接到一个VIP,iptable规则开始起作用。一个后端会被选择(要么根据SessionAffinity会话亲和度,要么随机),数据包被重定向到这个后端。不像用户空间带理,数据包从来不拷贝到用户空间,kubeproxy不是必须为了VIP工作而运行,而且客户端IP是不可更改的。当流量打到Node的端口上或通过负载均衡器,会执行相同的基本流程,但是在哪些案例中客户端IP是可以更改的。
⑤IPVS
在大规模集群(10000个服务)中,iptables操作会显著降低速度。IPVS转为负载平衡而设计,并基于内核内哈希表。因此,您可以通过IPVS的kube-proxy在大量服务中实现性能一致性(O(1))。同时,基于IPVS的kube-proxy具有更复杂的负载平衡算法(最小连接,局部性,加权,持久性) -
是