K8s Service(3)

2020-05-25  本文已影响0人  jaymz明

Service

概念介绍

虽然每个Pod都会被分配一个单独的IP地址,但这个IP地址会随着Pod的销毁而消失。引出的一个问题是:如果有一组Pod组成一个应用集群来提供服务,那么该如何访问它们呢?

Service就是用来解决这个问题的,一个Service可以看作一组提供相同服务的Pod的对外接口,Service是通过LabelSelector选择一组Pod作为后端服务提供者。

定义

一个 Service 在 Kubernetes 中是一个 REST 对象,和 Pod 类似。
例如,假定有一组 Pod,它们对外暴露了 3000 端口,同时还被打上 app=jaymz 标签。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: jaymz
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000

上述配置创建一个名称为 “my-service” 的 Service 对象,它会将请求代理到使用 TCP 端口 3000,并且具有标签 "app=jaymz"Pod 上。 Kubernetes 为该服务分配一个 IP 地址(有时称为 “集群IP” )。

多端口 Service

对于某些服务,需要公开多个端口。 Kubernetes允许在Service对象上配置多个端口定义。 当使用多个端口时,必须提供所有端口名称,以使它们无歧义。 例如:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 3000
    - name: https
      protocol: TCP
      port: 443
      targetPort: 9000

服务代理

在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。

为什么不使用 DNS 轮询?

时不时会有人问道,就是为什么 Kubernetes 依赖代理将入站流量转发到后端。 那其他方法呢? 例如,是否可以配置具有多个A值(或IPv6为AAAA)的DNS记录,并依靠轮询名称解析?

使用服务代理有以下几个原因:

版本兼容性

从Kubernetes v1.0开始,您已经可以使用 用户空间代理模式。 Kubernetes v1.1添加了 iptables 模式代理,在 Kubernetes v1.2 中,kube-proxy 的 iptables 模式成为默认设置。 Kubernetes v1.8添加了 ipvs 代理模式。

虚拟IP

Service一般会提供一个虚拟IP(Virtual IP),它在Service创建之后就保持不变,并且能够被同一集群中的Pod资源所访问。Service端口用于接收客户端请求并将其转发给后端的Pod应用程序,这种代理机制称为“端口代理”或四层代理,工作于TCP/IP协议栈的传输层。

API Server会持续监听(watch)Pod的变化(增加、删除),并实时更新Endpoint,Endpoint是一个由Pod的IP地址和端口组成的列表,Service会从Endpoint列表中选择一条记录进行流量调度。

服务代理 - userspace(用户空间)、iptables、ipvs

在众多的Pod对象中,Service资源是如何选择正确的Pod进行流量调度的呢?

这就是我们要具体介绍的,Kubernetes提供的“端口代理”的几种实现方式。

简单来说,一个Service对象就是Node上的一些iptables或ipvs规则,用于将到达Service对象的流量调度到相应的Pod上。Kubernetes集群中的每个Node上都会有产生一个Kube-proxy程序,它通过API-Server持续监控各Service及其关联的Pod对象,并将其变化实时反映至当前节点的iptables或ipvs规则上。

kube-Proxy将请求代理至相应端点的方式有三种:userspace(用户空间)、iptables、ipvs
userspace代理模型

这里的userspace是指Linux操作系统的用户空间。对于每个Service对象它会打开一个本地端口(运行于用户空间的kube-proxy进程负责监听此端口),任何到达此代理端口的连接请求都会被代理至当前Service资源后端的各Pod对象上,至于会挑选哪个Pod取决于Service资源的调度方式,默认为轮询(roundrobin)

这种代理模式请求流量到达内核空间后经由套接字送往用户空间的kube-proxy,而后由它送回内核空间,并调度至后端pod。这种方式请求会在内核和用户空间之间来回转发导致效率不高。

iptables代理模型

iptables代理模式和前一种代理模式是类似的,都是由kube-proxy来跟踪监听API-server上的service和Endpoints的变更。但是有一点不同的是iptables规则直接捕获到达cluster IP和port的流量,并将其重定向至当前Service的后端,对于每个Endpoints对象,Service资源会为其创建iptables规则并关联至挑选的后端pod资源,默认算法是随机调度(random)。iptables代理模式在Kubernetes1.1版本引入,并在1.2版开始成为默认类型。

在创建service资源时,集群中每个节点上的kube-proxy都会接受到通知并将其定义为当前节点上的iptables规则,用于转发接收到的iptables,进行调度和目标地址转换(DNAT)后再转发至集群内部的pod对象上。

默认的策略是,kube-proxy 在 iptables 模式下随机选择一个 backend。

相对于用户空间来讲,iptables模型无须将流量在用户空间和内核空间来回切换,因更加高效和可靠。

ipvs代理模型

Kubernetes从1.9版本引入ipvs代理模型,且从1.11版本起成为默认设置。它和iptables模型很类似,唯一一点不同的是在其请求流量的调度功能由ipvs实现,余下的功能仍由iptables完成。

ipvs是建立在netfilter的钩子函数上,但它使用hash表作为底层数据结构并工作于内核空间,因此流量转发速度特别快、规则同步性很好,而且它支持众多调度算法,rr(轮询)、lc(最小连接数)、dh(目标哈希)、sh(源哈希)、sed(最短期望延迟)、nq(不排队调度)。

服务发现

Kubernetes 支持2种基本的服务发现模式 —— 环境变量和 DNS。

环境变量

Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量, {SVCNAME}_SERVICE_HOST{SVCNAME}_SERVICE_PORT 变量,这里 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

注意: 必须在Pod出现 之前 创建Service, 否则,这些Pod将不会设定其环境变量。 如果仅使用DNS查找Service的群集IP,则无需担心环境变量问题。

DNS

您可以使用附加组件为Kubernetes集群设置DNS服务。

支持群集的DNS服务器(例如CoreDNS)监视 Kubernetes API 中的新Service,并为每个Service创建一组 DNS 记录。 如果在整个集群中都启用了 DNS,则所有 Pod 都应该能够通过其 DNS 名称自动解析出Service对应的IP地址。

Headless Services

有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 "None" 来创建 Headless Service。

您可以使用 headless Service 与其他服务发现机制进行接口,而不必与 Kubernetes 的实现捆绑在一起。

对这 headless Service 并不会分配 Cluster IP,kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。 DNS 如何实现自动配置,依赖于 Service 是否定义了 selector。

配置 Selector

对定义了 selector 的 Headless Service,Endpoint 控制器在 会创建 Endpoints 记录,并且修改 DNS 配置返回 A 记录(地址),通过这个地址直接到达 Service 的后端 Pod 上。

配置 Selector的Headless Services应用场景
  1. 自主选择权,有时候client想自己来决定使用哪个Endpoints记录,可以通过查询DNS来获取Endpoints的信息。

  2. Headless Services还有一个用处(PS:也就是我们需要的那个特性)。Headless Service的对应的每一个Endpoints,即每一个Pod,都会有对应的DNS域名;这样Pod之间就可以互相访问。

不配置 Selector

对没有定义 selector 的 Headless Service,Endpoint 控制器不会创建 Endpoints 记录。 然而 DNS 系统会查找和配置,无论是:

不配置 Selector的Headless Services应用场景

Service最常见的应用是作为Pod的负载均衡器(反向代理)使用,抽象化对 Kubernetes Pod 的访问,但是Service也可以作为其他应用程序的反向代理来使用。 实例:

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

注意: 端点 IPs 必须不可以 : 环回( IPv4 的 127.0.0.0/8 , IPv6 的 ::1/128 )或本地链接(IPv4 的 169.254.0.0/16 和 224.0.0.0/24,IPv6 的 fe80::/64)。 端点 IP 地址不能是其他 Kubernetes Services 的群集 IP,因为 kube-proxy 不支持将虚拟 IP 作为目标。

访问没有 selector 的 Service,与有 selector 的 Service 的原理相同。 请求将被路由到用户定义的 Endpoint, YAML中为: 192.0.2.42:9376 (TCP)。

ExternalName ServiceService 的特例,它没有 selector,也没有使用 DNS 名称代替。

类型ExternalName

类型为 ExternalName 的服务将Service 映射到 DNS 名称,而不是典型的选择器,例如 my-service , 您可以使用 spec.externalName 参数指定这些服务。

例如,以下 Service 定义将 prod 名称空间中的 my-service 服务映射到 my.database.example.com

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

注意: ExternalName 接受 IPv4 地址字符串,但作为包含数字的 DNS 名称,而不是 IP 地址。 类似于 IPv4 地址的外部名称不能由 CoreDNS 或 ingress-nginx 解析,因为外部名称旨在指定规范的 DNS 名称。 要对 IP 地址进行硬编码,请考虑使用 headless Services

当查找主机 my-service.prod.svc.cluster.local 时,群集DNS服务返回 CNAME 记录,其值为 my.database.example.com。 访问 my-service 的方式与其他服务的方式相同,但主要区别在于重定向发生在 DNS 级别,而不是通过代理或转发。 如果以后您决定将数据库移到群集中,则可以启动其 Pod,添加适当的选择器或端点以及更改Service的类型。

外部 IP

如果外部的 IP 路由到集群中一个或多个 Node 上,Kubernetes Service 会被暴露给这些 externalIPs。 通过外部 IP(作为目的 IP 地址)进入到集群,打到 Service 的端口上的流量,将会被路由到 Service 的 Endpoint 上。 externalIPs 不会被 Kubernetes 管理,它属于集群管理员的职责范畴。

发布服务 —— 服务类型

有一些Pod可能希望将服务暴露给外部应用调用,可以通过Service,选择合适的类型来实现。

Service的默认值是 ClusterIP 类型。

Type 的可选值以及行为如下:

注意: 您需要 CoreDNS 1.7 或更高版本才能使用 ExternalName 类型。

您也可以使用 Ingress 来暴露自己的服务。 Ingress 不是服务类型,但它充当集群的入口点。 它可以将路由规则整合到一个资源中,因为它可以在同一IP地址下公开多个服务。

保留源 IP

各种类型的 Service 对源 IP 的处理方法不同:

上一篇 下一篇

猜你喜欢

热点阅读