首页
学习
活动
专区
工具
TVP
发布

Kubernetes-核心资源之Service

有三种模式:userspace、iptables、ipvs

在Kubernetes中,Pods是有生命周期的。它们被创建、被终止,但不能被复活。在Kubernetes中通过ReplicationControllers动态的创建和删除Pod。然后,每一个Pod都拥有自己的IP地址,但是这些IP地址随着时间会发生变化。这会导致一个问题:如果在Kubernetes集群中,前端的Pod需要调用后端的Pod的功能,那么这些前端的Pod如何发现和跟踪后端的Pod?

在Kubernetes中,Service是一个抽象的概念,它定义了Pod逻辑集合和访问这些Pod的策略。Service通过Label Selector选择Pod。例如,在后端运行着有3个副本的Pod,这些副本是可互相替换的,前端不需要关注使用那个副本。Service抽象就用来实现此解耦的能力的。对于 Kubernetes-native的应用,当Service中的Pod发生变化时,Kubernetes通过了Endpoints API类进行更新。对于non-native applications, Kubernetes 提供了virtual-IP-based桥,通过它重定向后端的Pod。在本文中,描述如何定义Service、发布Service和发现Serivce的整个过程。

1、虚拟IP和服务代理

在Kubernetes的每一个Node中,都运行着一个kube-proxy,kube-proxy负责为服务(ExternalName除外)实现虚拟IP的格式。在Kubernetes v1.0中,服务是一个4层(IP之上的TCP/UDP)结构,纯粹在userspace实现代理;在Kubernetes v1.1,增加了Ingress API,它表达了7层(HTTP)服务;也增加了iptables代理,此代理是Kubernetes v1.2后的默认代理模式;在Kubernetes v1.8.0-beta.0, 增加了ipvs代理。

在iptables模式中,kube-proxy通过创建iptables规则,将访问Service虚拟IP的请求重定向到Endpoints上,iptables代码模式方式利用linux的iptables nat转发进行实现。kube-proxy监控Kubernetes master中的Service和Endpoints对象,并进行添加和移除,以更新iptables规则。

  • 对于每一个Service,它将会安装iptable规则,此规则获取流量至Service‘s clusterIP和端口,并将这些流量传递给Service后端集。
  • 对于每一个Endpoints对象,它将安装iptable规则,用于选择后端的Pod。

显然,iptable不需要在userspace和kernelspace之间进行转换,它比userspace更快更可靠。不像userspace代理器,如果一个Pod被提供服务,iptable代理器不能自动的使用另外一个Pod。需要注意下图中:clusterIP被显示为ServiceIP。

2、定义服务

在Kubernetes中,服务是一个REST对象,类似于Pod。想其他所有的REST对象一样,服务定义能够被传递给apiserver来创建一个新的实例,例如,这里有一组Pod,对外暴露的端口为9367,标签为app:MyApp:

kind:Service apiVersion:v1 metadata: name:my-service spec: selector: app:MyApp ports: - protocol:TCP port:80 #暴露在cluster Ip上的端口,供集群内部使用 targetPort:9376 #pod上的端口

在此配置文件中,创建了一个名为“my-service”的服务对象,在每一个标签带有“app=MyApp”的Pod上,它的targetPort为9376。此服务将被指派一个IP地址(有时也称为“cluster IP”),服务选择器将被持续的评估,评估的结果将被传递给名称也为“my-service”的Endpoints对象。

需要注意的是,服务能够映射一个输入端口至任意的targetPort。在默认情况下,targetPort将被设置成与port的值一样。targetPort可以是一个字符串,可以引用后端Pod中的port名称。基于后端Pod的不同,实际的port值也会不同。这就为部署服务提供大量的灵活性。Kubernetes服务支持TCP和UDP协议,默认为TCP协议。

2.1 无选择器的服务

Service一般被用来代理访问Pod,但也能够代理后端的其他类型,例如:

  • 在生产环境中使用外部的数据库,但在测试环境中使用集群内的数据;
  • 服务将需要被另外的命名空间或者另外的集群上的服务调用;
  • 正在迁移应用至Kubernetes,并且一些后端在Kubernetes外运行。

在上述的这些场景中,可以定义无选择器的Service:

kind:Service apiVersion:v1 metadata: name:my-service spec: ports: - protocol:TCP port:80 #Cluster IP上的端口为80,此端口仅可以在集群内访问 targetPort:9376 #Pod上的端口

因为此Service没有选择器,则不会创建对应的Endpoints对象,您可以手工将服务映射至指定的endpoints中:

kind:Endpoints apiVersion:v1 metadata: name:my-service subsets: - addresses: - ip:1.2.3.4 ports: - port:9376

注意:Endpoint IP不可以是loopback(127.0.0.0/8), link-local (169.254.0.0/16), 或者link-local multicast (224.0.0.0/24)。 访问无选择器的Service与访问有选择器的Service是一样。流量将通过用户定义的endpoints进行路由。

2.2 ExternalName服务

ExternalName Service是Service的一个特例,它没有选择器,也没有定义任何端口或Endpoints。它的作用是返回集群外Service的外部别名。

kind:Service apiVersion:v1 metadata: name:my-service namespace:prod spec: type:ExternalName #服务类型为外部服务 externalName:my.database.example.com #外部服务

当查找my-service.prod.svc.CLUSTER时,集群DNS服务将会返回一条CNAME记录,此记录的值为my.database.example.com。当然后续也可以将此数据库迁移到集群中,这样就可以通过Pod启动,并为其添加合适的选择器或者Endpoints,并修改服务类型。

2.3 headless服务

在有些场景下,服务可能不需要作为负载均衡代理,而仅仅需要一个单一的cluster ip。这时就可以通过设置“spec.clusterIP”的值为None,来创建一个”headless“类型的服务。此类型的服务允许开发者减少对Kubernetes系统的依赖,开发者可以通过自己的方式实现对服务的自动发现。应用也能够使用其他的服务发现系统,进行服务的自注册和适配器,实现服务的自动发现。对于这样的服务:

  • Kubernetes未指派cluster IP;
  • kube-proxy将不处理这些服务;
  • 因此也就没有负载均衡和代理。
  • 但会依赖服务是否拥有选择器进行DNS的配置。

对于定义了选择器的headless service,Endpoints控制器在API中创建Endpoints记录,并通过修改DNS的配置信息返回一条记录,此记录指向服务后端的Pod。对于没有定义选择器的headless service,Endpoints控制器不会创建Endpoints记录,然而,DNS系统将会进行寻址和配置。

  • CNAME记录:ExternalName类型的服务
  • Endpoints记录:任意与service共享一个名称的Endpoints。

2.4、多端口服务

在实际的应用场景中,有一些服务需要暴露多个端口。在Kubernetes中,支持在Service对象上定义多个端口。当使用多个端口时,则需要为每个端口设置一个名称。例如,下面名称为my-service的服务YAML配置文件,它暴露了一个http的端口和一个https的端口。

kind:Service apiVersion:v1 metadata: name:my-service spec: selector: app:MyApp ports: - name:http #名称为http的端口 protocol:TCP port:80 targetPort:9376 - name:https #名称为https的端口 protocol:TCP #端口协议为TCP port:443 #ClusterIP端口号为443 targetPort:9377 #Pod上的端口为9377

2.5 代理外部的服务

另外,在一些场景下,Kubernetes中的容器化应用需要调用集群外的应用。Service也可以代理任意其它的后端应用,比如运行在Kubernetes集群外部的Oracle、MySQL和Redis等。在Kubernetes中,通过定义同名的Service和EndPoints来实现对于外部应用的代理,以实现其能够被集群内部的应用调用。

下面是接入外部Oralce端点YAML文件:

apiVersion: v1
kind: Endpoints
metadata:
  name: oracle-service
subsets:
  - addresses:
    - ip: 192.168.8.159
    ports:
    - port: 1521
      protocol: TCP

下面是代理外部Oralce的服务YAML文件:

apiVersion: v1
kind: Service
metadata:
  name: oracle-service
spec:
  ports:
  - port: 1521
    targetPort: 1521
    protocol: TCP

3、发现服务

在Kubernetes中,支持两种服务发现的模式:

  • 环境变量
  • DNS

3.1 环境变量

当一个Pod运行在Node上时,kubelet将为每一个活动的Service添加环境变量,环境变量有两类:

  • DockerLink 环境变量:相当于Docker的–link参数实现容器连接时设置的环境变量。
  • Kubernetes Service环境变量:Kubernetes为Service设置的环境变量形式,{SVCNAME}_SERVICE_HOST 和{SVCNAME}_SERVICE_PORT变量,环境变量的名称为大写字母和下划线。

例如:存在一个名称为“redies-master”的Service(它的cluster ip地址为10.0.0.11,端口号为6379,协议为TCP),它的环境变量如下:

#Kubernetes Service环境变量: REDIS_MASTER_SERVICE_HOST=10.0.0.11 REDIS_MASTER_SERVICE_PORT=6379 #Docker Link环境变量: 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

在这里,可以看到环境变量中记录了”redies-master”服务的IP地址和端口,以及协议信息。因此,Pod中的应用就可以通过环境变量来发现此服务。但是,环境变量方式存在如下的限制:

1)环境变量只能在相同的命名空间中使用;

2)另外,Service必须在Pod创建之前被创建,否则Service变量不会被设置到Pod中;

3)DNS服务发现机制则没有这些限制。

3.2 DNS

DNS服务发现是基于Cluster DNS的,DNS服务器会对新服务进行监控,并为每一个服务创建DNS记录,用于域名解析。在集群中,如果启用DNS,则所有的Pod都可以自动通过名称解析服务。

例如,如果在“my-ns”命名空间下拥有一个名为“my-serivce”的服务,则会有一个名为“my-service.my-ns”的DNS记录被创建。

  • 在“my-ns”命名空间下,Pod将能够通过名称“my-service”来发现此服务。
  • 在其它命名空间,Pod必须通过“my-serivce.my-ns”来发现此服务,此名称选址的结果即为cluster IP。

Kubernetes也支持端口的DNS SRV(serivce)记录。如果 “my-service.my-ns”服务拥有一个TCP协议名称为”http”的端口,就能够通过”_http._tcp.my-service.my-ns”名称来发现”http”端口的值。Kubernetes DNS服务器是发现ExternalName类型服务的唯一途径。

4、发布服务-服务类型

对于某些应用(例如:前端)的一部分功能,您可能需要暴露一个使用外部IP地址的Sevice。通过Kubernetes的ServiceType,能够指定所使用的service类型。默认情况下使用ClusterIP。Kubernetes的服务类型如下:

  • ClusterIP (default) – 将服务暴露在集群内部的IP,此类型仅支持在集群内服务。
  • NodePort – 将服务暴露在所选定每一个Node的同一端口,集群外可以通过<NodeIP>:<NodePort>方式访问服务。
  • LoadBalancer – 在当前的集群中创建一个外部的负载均衡,并为服务(service)指派一个固定的、外部的。
  • ExternalName – 使用一个随意的名称(在规格中指定)来暴露服务,并会返回一个带有名称的CNAME记录。此类型不使用代理,这种类型只在kube-dns v1.7上才支持。

4.1 主机端口(NodePort)类型

如果Service的type为“NodePort”,则Kubernetes master将会在每一个Node为此Service暴露一个对外的端口(默认:30000-32767)。外部网络将能够通过[NodeIP]:[NodePort]对服务进行访问。也可以通过nodePort指定此端口,但此端口的值必须在“30000-32767”范围内,手动指定的话需要注意端口存在冲突的可能性。此类型使开发者能够自由的设置自己的负载均衡,即也可以采用Kubernetes未支持的负载均衡技术。

kind:Service apiVersion:v1 metadata: name:my-service spec: type:NodePort #指定Service类型为NodePort selector: app:MyApp ports: - protocol:TCP port:80 targetPort:9376

4.2 负载均衡(LoadBalancer)类型

负载均衡服务是类型为LoadBalance的服务,它建立在NodePord类型服务的基础上。Kubernetes会分配给LoadBalance服务一个内部的虚拟IP,并且暴露NodePort。通过LoadBalanceIP进来的请求,将会被转发给NodePort。

kind:Service apiVersion:v1 metadata: name:my-service spec: selector: app:MyApp ports: - protocol:TCP port:80 targetPort:9376 clusterIP:10.0.171.239 loadBalancerIP:78.11.24.19 type:LoadBalancer #指定Service的类型为LoadBalancer status: loadBalancer: ingress: - ip:146.148.47.155

来自于外部负载均衡的流量将被直接引到后端的Pod中。

4.3 外部IP

如果这里有一些外部IP,通过它们能够路由至一个或者多个集群的Node,Kubernetes服务将可以被暴露在这些externalIPs上。通过外部IP(作为目标IP),ingress导入到集群流量的将被路由到其中的一个服务endpoint上。externalIPs由集群管理员进行管理。所有的服务类型都可以指定externalIPs,在下面的“my-service”服务中,客户端口可以通过“80.11.12.10:80”外部端口来访“my-service”服务。

kind:Service apiVersion:v1 metadata: name:my-service spec: selector: app:MyApp ports: - name:http protocol:TCP port:80 targetPort:9376 externalIPs: #定义外部ip地址 - 80.11.12.10

4.4ClusterIP

参考资料

1.《Services》地址:https://kubernetes.io/docs/concepts/services-networking/service/

2.《kubectl-commands service》地址:https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#-em-service-em-

下一篇
举报
领券