服务注册发现与负载均衡 - Service

如果使用 Deployment 来运行应用服务,Deployment 可以动态的创建和销毁 Pod。每个 Pod 获取其自己的 IP 地址(K8S期待网络插件确保IP地址分配)。
对于集群中给定的 Deployment,这一刻运行的这组 Pod 可能不同于下一刻运行应用程序的另一组 Pod。
由此引出的的问题是:

  • 如果一组Pod(称为”后端“)为同一集群内的其它Pod(称为”前端”)提供功能,那么前端如何找出并跟踪要连接的IP地址,以便前端可以使用提供能力的后端部分?
  • 如果一组前端Pod要调用不同集群的一组后端Pod呢?

Kubernetes 中 Service 是一种抽象,通过网络暴露Pod组合。每个Service对象定义一组Pod以及如何访问这些Pod的策略。

Kubernetes Service 是集群中提供相同功能的一组 Pod 的抽象表达。 当每个 Service 创建时,会被分配一个唯一的 IP 地址(也称为 clusterIP)。 这个 IP 地址与 Service 的生命周期绑定在一起,只要 Service 存在,它就不会改变。 可以配置 Pod 使它与 Service 进行通信,Pod 知道与 Service 通信将被自动地负载均衡到该 Service 中的某些 Pod 上。

定义Service

Service 在 Kubernetes 中是一个对象 (与 Pod 或 ConfigMap 类似的对象)。可以使用 Kubernetes API 创建、查看或修改 Service 定义。 通常使用 kubectl 这类工具来进行这些 API 调用。

EndpointPoints & EndpointSlices

Endpoint: pod和service之间的关联关系,是通过endpoint实现的。
Endpoints表示了一个Service对应的所有Pod副本的访问地址;而Endpoints Controller负责生成和维护所有Endpoints对象的控制器,它负责监听Service和对应的Pod副本的变化。

在 Kubernetes API 中,Endpoints (该资源类别为复数)定义了网络端点的列表,通常由 Service 引用,以定义可以将流量发送到哪些 Pod。
推荐用 EndpointSlice API 替换 Endpoints。

服务发现

Kubernetes 支持两种查找服务的主要模式:环境变量和 DNS。前者开箱即用,而后者则需要 CoreDNS 集群插件。

环境变量

当 Pod 在节点上运行时,kubelet 会针对每个活跃的 Service 为 Pod 添加一组环境变量。
kubelet 为 Pod 添加环境变量 {SVCNAME}_SERVICE_HOST{SVCNAME}_SERVICE_PORT

使用环境变量方法将端口和集群 IP 发布到客户端 Pod 时,必须在客户端 Pod 出现 之前 创建服务。 否则,这些客户端 Pod 将不会设定其环境变量。
如果仅使用 DNS 查找服务的集群 IP,则无需担心此设定问题。

DNS

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

例如,如果你在 Kubernetes 命名空间 my-ns 中有一个名为 my-service 的服务, 则控制平面和 DNS 服务共同为 my-service.my-ns 创建 DNS 记录。 my-ns 命名空间中的 Pod 应该能够通过按名检索 my-service 来找到服务 (my-service.my-ns 也可以工作)。
同一集群下其他命名空间中的 Pod 必须将名称限定为 my-service.my-ns这些名称将解析为为服务分配的集群 IP

kube-dns记录service和为服务分配的集群IP的对应关系。

Kubernetes 提供了一个自动为其它Service分配DNS名字的DNS插件Service:kube-dns。
可以通过如下命令检查它是否在工作:

1
2
3
4
kubectl get services kube-dns --namespace=kube-system

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.0.0.10 <none> 53/UDP,53/TCP 8m

发布服务(服务类型)

对一些应用的某些部分(如前端),可能希望将其暴露给K8S集群外部的IP地址从而能够外网访问。

Kubernetes ServiceTypes 允许指定所需要的 Service 类型。

  • ClusterIP
    通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。
    这也是没有为服务显式指定 type 时使用的默认值
    可以使用 Ingress 或者 Gateway API 向公众暴露服务。
  • NodePort
    通过每个节点上的 IP 和静态端口(NodePort)暴露服务。
    为了让节点端口可用,Kubernetes 设置了集群 IP 地址,这等同于请求 type: ClusterIP 的服务。
  • LoadBalancer
    使用云提供商的负载均衡器向外部暴露服务。
    外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。
  • ExternalName
    通过返回 CNAME 记录和对应值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。 无需创建任何类型代理。
    要使用 kube-dns 1.7 及以上版本或者 CoreDNS 0.0.8 及以上版本才能使用 ExternalName 类型。

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

ClusterIP类型

使用 type: ClusterIP 的 Service时,Service 可以具有集群作用域的虚拟 IP 地址。
客户端可以使用该虚拟 IP 地址进行连接,Kubernetes 通过不同的后台 Pod 对该 Service 的流量进行负载均衡。

Service ClusterIP 是如何分配的?

  • 动态分配
    集群的控制面自动从所配置的 IP 范围内为 type: ClusterIP 选择一个空闲 IP 地址。
  • 静态分配
    根据为 Service 所配置的 IP 范围,选定并设置你的 IP 地址。

在整个集群中,每个 Service 的 ClusterIP 都必须是唯一的。 尝试使用已分配的 ClusterIP 创建 Service 将返回错误。

NodePort类型

服务注册发现与负载均衡 - Ingress

Ingress

Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。
Ingress 可以提供负载均衡、SSL 终结和基于名称的虚拟托管。

Ingress 公开从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。

下面是一个将所有流量都发送到同一 Service 的简单 Ingress 示例:

Ingress 可为 Service 提供外部可访问的 URL、负载均衡流量、终止 SSL/TLS,以及基于名称的虚拟托管。
Ingress 控制器 通常负责通过负载均衡器来实现 Ingress,尽管它也可以配置边缘路由器或其他前端来帮助处理流量。
Ingress 不会公开任意端口或协议。 将 HTTP 和 HTTPS 以外的服务公开到 Internet 时,通常使用 Service.Type=NodePort 或 Service.Type=LoadBalancer 类型的 Service。

Ingress资源

一个Ingress资源示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-wildcard-host
spec:
rules:
- host: "foo.bar.com"
http:
paths:
- pathType: Prefix
path: "/bar"
backend:
service:
name: service1
port:
number: 80
- host: "*.foo.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: service2
port:
number: 80

Ingress 对象的命名必须是合法的 DNS 子域名名称。

Ingress类

Ingress 可以由不同的控制器实现,通常使用不同的配置。 每个 Ingress 应当指定一个类,也就是一个对 IngressClass 资源的引用。 IngressClass 资源包含额外的配置,其中包括应当实现该类的控制器名称。

IngressClass的作用域

取决于 Ingress 控制器,可以使用集群范围设置的参数或某个名字空间范围的参数。

  • 集群作用域
    IngressClass 的参数默认是集群范围的。
  • 命名空间作用域
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    apiVersion: networking.k8s.io/v1
    kind: IngressClass
    metadata:
    name: external-lb-2
    spec:
    controller: example.com/ingress-controller
    parameters:
    # 此 IngressClass 的配置定义在一个名为 “external-config” 的
    # IngressParameter(API 组为 k8s.example.com)资源中,
    # 该资源位于 “external-configuration” 命名空间中。
    scope: Namespace
    apiGroup: k8s.example.com
    kind: IngressParameter
    namespace: external-configuration
    name: external-config

Ingress Controller

为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。

与作为 kube-controller-manager 可执行文件的一部分运行的其他类型的控制器不同, Ingress 控制器不是随集群自动启动的。
Kubernetes 作为一个项目,目前支持和维护 AWS、 GCE 和 Nginx Ingress 控制器。