Kubernetes 的 DiscoveryClient

本项目为 Kubernetes 提供了 Discovery Client 的实现。此客户端允许您按名称查询 Kubernetes 端点(请参阅 服务)。服务通常由 Kubernetes API 服务器公开为一系列端点,这些端点代表 httphttps 地址,客户端可以从作为 pod 运行的 Spring Boot 应用程序访问这些地址。

DiscoveryClient 还可以找到 ExternalName 类型的服务(请参阅 ExternalName 服务)。目前,仅当以下属性 spring.cloud.kubernetes.discovery.include-external-name-services 设置为 true 时(默认为 false),才支持外部名称类型的服务。

我们支持 3 种类型的发现客户端

1.

Fabric8 Kubernetes 客户端

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-kubernetes-fabric8</artifactId>
</dependency>

2.

Kubernetes Java 客户端

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-kubernetes-client</artifactId>
</dependency>

3.

基于 HTTP 的 DiscoveryClient

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-kubernetes-discoveryclient</artifactId>
</dependency>
spring-cloud-starter-kubernetes-discoveryclient 旨在与 Spring Cloud Kubernetes DiscoveryServer 一起使用。

要启用 DiscoveryClient 的加载,请将 @EnableDiscoveryClient 添加到相应的配置或应用程序类中,如以下示例所示

@SpringBootApplication
@EnableDiscoveryClient
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

然后,您只需通过自动装配将其注入到代码中,如以下示例所示

@Autowired
private DiscoveryClient discoveryClient;

您应该问自己的第一个问题是 DiscoveryClient 应该在 何处 发现服务。在 kubernetes 世界中,这意味着哪些命名空间。这里有 3 个选项

  • 选择性命名空间。例如

spring.cloud.kubernetes.discovery.namespaces[0]=ns1
spring.cloud.kubernetes.discovery.namespaces[1]=ns2

这样的配置使发现客户端只在 ns1ns2 两个命名空间中搜索服务。

  • 所有命名空间.

spring.cloud.kubernetes.discovery.all-namespaces=true

虽然存在这样的选项,但这可能对 kube-api 和您的应用程序都是一种负担。很少需要这样的设置。

  • 一个命名空间。这是默认设置,如果您未指定以上任何一项。它按照 命名空间解析 中概述的规则工作。

上述选项对于 fabric8 和 k8s 客户端来说完全适用。对于基于 HTTP 的客户端,您需要在 服务器 上启用这些选项。这可以通过在用于将镜像部署到集群中的 deployment.yaml 中使用环境变量来完成。

例如:

      containers:
        - name: discovery-server
          image: springcloud/spring-cloud-kubernetes-discoveryserver:3.0.5-SNAPSHOT
          env:
            - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0
              value: "namespace-a"

配置好命名空间后,下一个要回答的问题是要发现哪些服务。可以将其视为要应用什么过滤器。默认情况下,不应用任何过滤,并且发现所有服务。如果您需要缩小发现客户端可以找到的范围,您有两个选项

  • 只选择与某些服务标签匹配的服务。此属性通过 spring.cloud.kubernetes.discovery.service-labels 指定。它接受一个 Map,并且只有那些具有此类标签(在服务定义中的 metadata.labels 中可见)的服务才会被考虑在内。

  • 另一个选项是使用 SpEL 表达式。这由 spring.cloud.kubernetes.discovery.filter 属性表示,其值取决于您选择的客户端。如果您使用 fabric8 客户端,此 SpEL 表达式必须针对 io.fabric8.kubernetes.api.model.Service 类创建。一个这样的示例可能是

spring.cloud.kubernetes.discovery.filter='#root.metadata.namespace matches "^.+A$"'

它告诉发现客户端只获取 metadata.namespace 以大写 A 结尾的服务。

如果您的发现客户端基于 k8s-native 客户端,则 SpEL 表达式必须基于 io.kubernetes.client.openapi.models.V1Service 类。上面所示的相同过滤器在这里也适用。

如果您的发现客户端是基于 http 的,那么 SeEL 表达式必须基于相同的 io.kubernetes.client.openapi.models.V1Service 类,唯一的区别是这需要作为环境变量在部署 yaml 中设置

      containers:
        - name: discovery-server
          image: springcloud/spring-cloud-kubernetes-discoveryserver:3.0.5-SNAPSHOT
          env:
            - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_FILTER
              value: '#root.metadata.namespace matches "^.+A$"'

现在是时候考虑发现客户端应该返回什么了。通常,DiscoveryClient 有两个方法:getServicesgetInstances

getServices 将返回服务 名称,如 metadata.name 中所示。

此方法将返回唯一的服务名称,即使在您选择用于搜索的不同命名空间中存在重复项。

getInstances 返回一个 List<ServiceInstance>。除了 ServiceInstance 具有的常规字段外,我们还添加了一些数据,例如命名空间或 pod 元数据(有关这些的更多解释将在文档中随后提供)。这是我们目前返回的数据

  1. instanceId - 服务实例的唯一 ID

  2. serviceId - 服务名称(与调用 getServices 报告的名称相同)

  3. host - 实例的 IP(或 ExternalName 类型服务的名称)

  4. port - 实例的端口号。这需要更多的解释,因为选择端口号有其规则

    1. 如果服务未定义端口,则返回 0(零)。

    2. 如果服务定义了单个端口,则返回该端口。

    3. 如果服务具有标签 primary-port-name,我们将使用标签值中指定的名称的端口号。

    4. 如果上述标签不存在,我们将使用 spring.cloud.kubernetes.discovery.primary-port-name 中指定的端口名称来查找端口号。

    5. 如果以上两者均未指定,我们将使用名为 httpshttp 的端口来计算端口号。

    6. 作为最后手段,我们将选择端口列表中的第一个端口。此最后一个选项可能会导致不确定的行为。

  5. 服务实例的 uri

  6. schemehttp 还是 https(取决于 secure 结果)

  7. 服务的 metadata

    1. labels(如果通过 spring.cloud.kubernetes.discovery.metadata.add-labels=true 请求)。如果设置了 spring.cloud.kubernetes.discovery.metadata.labels-prefix 的值,则标签键可以带有“前缀”。

    2. annotations(如果通过 spring.cloud.kubernetes.discovery.metadata.add-annotations=true 请求)。如果设置了 spring.cloud.kubernetes.discovery.metadata.annotations-prefix 的值,则注解键可以带有“前缀”。

    3. ports(如果通过 spring.cloud.kubernetes.discovery.metadata.add-ports=true 请求)。如果设置了 spring.cloud.kubernetes.discovery.metadata.ports-prefix 的值,则端口键可以带有“前缀”。

    4. k8s_namespace,其值为实例所在的命名空间。

    5. type,用于保存服务类型,例如 ClusterIPExternalName

  8. secure,如果发现的端口应被视为安全。我们将使用上面概述的相同规则来查找端口名称和编号,然后

    1. 如果此服务有一个名为 secured 的标签,其值为:["true", "on", "yes", "1"] 中的任何一个,则将找到的端口视为安全。

    2. 如果未找到此类标签,则搜索名为 secured 的注解并应用上述相同规则。

    3. 如果此端口号是 spring.cloud.kubernetes.discovery.known-secure-ports 的一部分(默认情况下,此值为 [443, 8443]),则将端口号视为安全。

    4. 最后一种方法是查看端口名称是否与 https 匹配;如果匹配,则将此端口视为安全。

  9. namespace - 找到实例的命名空间。

  10. pod-metadata 服务实例(pod)的标签和注解,形式为 Map<String, Map<String, String>>。此支持需要通过 spring.cloud.kubernetes.discovery.metadata.add-pod-labels=true 和/或 spring.cloud.kubernetes.discovery.metadata.add-pod-annotaations=true 启用


要发现未被 kubernetes api 服务器标记为“ready”的服务端点地址,您可以在 application.properties 中设置以下属性(默认值:false)

spring.cloud.kubernetes.discovery.include-not-ready-addresses=true
这可能在出于监控目的发现服务时很有用,并且可以检查未就绪服务实例的 /health 端点。如果您想获取 ServiceInstance 列表以包含 ExternalName 类型的服务,您需要通过 spring.cloud.kubernetes.discovery.include-external-name-services=true 启用该支持。因此,当调用 DiscoveryClient::getInstances 时,这些服务也将被返回。您可以通过检查 ServiceInstance::getMetadata 并查找名为 type 的字段来区分 ExternalName 和任何其他类型。这将是返回的服务类型:ExternalName/ClusterIP 等。如果由于任何原因需要禁用 DiscoveryClient,您可以在 application.properties 中设置以下属性
spring.main.cloud-platform=NONE

请注意,发现客户端的支持是 自动的,具体取决于您运行应用程序的位置。因此,上述设置可能不是必需的。

一些 Spring Cloud 组件使用 DiscoveryClient 来获取有关本地服务实例的信息。为此,您需要将 Kubernetes 服务名称与 spring.application.name 属性对齐。

就应用程序在 Kubernetes 中注册的名称而言,spring.application.name 没有影响

Spring Cloud Kubernetes 还可以监视 Kubernetes 服务目录的更改,并相应地更新 DiscoveryClient 实现。要启用此功能,您需要在应用程序的配置类上添加 @EnableScheduling。通过“监视”,我们指的是我们将每隔 spring.cloud.kubernetes.discovery.catalog-services-watch-delay 毫秒(默认值为 30000)发布一次心跳事件。对于 http 发现服务器,这必须是在部署 yaml 中设置的环境变量

      containers:
        - name: discovery-server
          image: springcloud/spring-cloud-kubernetes-discoveryserver:3.0.5-SNAPSHOT
          env:
            - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_CATALOGSERVICESWATCHDELAY
              value: 3000

心跳事件将包含目标引用(以及所有端点地址的命名空间(有关将返回的精确详细信息,您可以查看 Fabric8CatalogWatch 内部))。这是一个实现细节,心跳事件的侦听器不应依赖于这些细节。相反,它们应该通过 equals 方法查看两个后续心跳之间是否存在差异。我们将确保返回符合 equals 契约的正确实现。端点将在以下任一情况下进行查询:- 所有命名空间(通过 spring.cloud.kubernetes.discovery.all-namespaces=true 启用)

  • 选择性命名空间(通过 spring.cloud.kubernetes.discovery.namespaces 启用),例如

  • 如果未采用上述两种路径,则通过 命名空间解析 选择 一个命名空间

如果由于任何原因您想禁用目录监视器,您需要设置 spring.cloud.kubernetes.discovery.catalog-services-watch.enabled=false。对于 http 发现服务器,这需要在部署中设置为环境变量,例如
SPRING_CLOUD_KUBERNETES_DISCOVERY_CATALOGSERVICESWATCH_ENABLED=FALSE

目录监视功能适用于我们支持的所有 3 种发现客户端,但对于 http 客户端,您需要注意一些注意事项。

  • 首先,此功能默认是禁用的,需要在两个地方启用

    • 在发现服务器中,通过部署清单中的环境变量,例如

      containers:
              - name: discovery-server
                image: springcloud/spring-cloud-kubernetes-discoveryserver:3.0.5-SNAPSHOT
                env:
                  - name: SPRING_CLOUD_KUBERNETES_HTTP_DISCOVERY_CATALOG_WATCHER_ENABLED
                    value: "TRUE"
    • 在发现客户端中,通过 application.properties 中的属性,例如

      spring.cloud.kubernetes.http.discovery.catalog.watcher.enabled=true
  • 第二点是,这仅自版本 3.0.6 及更高版本才受支持。

  • 由于 http 发现有 两个 组件:服务器和客户端,我们强烈建议使它们之间的版本保持一致,否则可能会出现问题。

  • 如果您决定禁用目录监视器,则需要在服务器和客户端中都禁用它。

自版本 5.0.0 以来,可以缓存发现客户端的响应(我们通过 @Cacheable 注解实现)。这里需要记住两个属性

spring.cloud.kubernetes.discovery.cacheable.reactive.enabled

spring.cloud.kubernetes.discovery.cacheable.blocking.enabled

第一个启用可缓存的响应式客户端,第二个启用可缓存的阻塞式客户端。默认情况下,创建不可缓存的发现客户端;如果您想要可缓存的客户端,您需要切换上述属性之一。例如

spring.cloud.kubernetes.discovery.cacheable.reactive.enabled=true

将为您提供可缓存的响应式发现客户端。

默认情况下,我们使用 Endpoints(参见 kubernetes.io/docs/concepts/services-networking/service/#endpoints)API 来找出服务的当前状态。但是,还有另一种方式,通过 EndpointSlices (kubernetes.io/docs/concepts/services-networking/endpoint-slices/)。此支持可以通过属性启用:spring.cloud.kubernetes.discovery.use-endpoint-slices=true(默认为 false)。当然,您的集群也必须支持它。事实上,如果您启用此属性但您的集群不支持它,我们将无法启动应用程序。如果您决定启用此支持,您还需要正确的 Role/ClusterRole 设置。例如

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: namespace-reader
rules:
  - apiGroups: ["discovery.k8s.io"]
    resources: ["endpointslices"]
    verbs: ["get", "list", "watch"]
© . This site is unofficial and not affiliated with VMware.