跳到主要内容

Kubernetes 的 DiscoveryClient

DeepSeek V3 中英对照 DiscoveryClient for Kubernetes

该项目为 Kubernetes 提供了一个 Discovery Client 的实现。该客户端允许你按名称查询 Kubernetes 端点(参见 服务)。服务通常由 Kubernetes API 服务器公开为一组端点,这些端点表示 httphttps 地址,客户端可以从作为 Pod 运行的 Spring Boot 应用程序中访问这些端点。

DiscoveryClient 还可以发现类型为 ExternalName 的服务(参见 ExternalName 服务)。目前,只有在以下属性 spring.cloud.kubernetes.discovery.include-external-name-services 设置为 true 时(默认情况下为 false),才支持外部名称类型的服务。

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

Fabric8 Kubernetes 客户端

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

Kubernetes Java 客户端

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

基于 HTTP 的 DiscoveryClient

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-discoveryclient</artifactId>
</dependency>
xml
备注

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);
}
}
java

然后你可以通过自动装配的方式在你的代码中简单地注入客户端,如下例所示:

@Autowired
private DiscoveryClient discoveryClient;
java

你首先应该问自己的问题是 DiscoveryClient 应该在哪里发现服务。在 Kubernetes 世界中,这意味着要确定哪个命名空间。这里有 3 个选项:

  • 选择性命名空间。例如:
spring.cloud.kubernetes.discovery.namespaces[0]=ns1
spring.cloud.kubernetes.discovery.namespaces[1]=ns2
none

这种配置使得发现客户端仅在两个命名空间 ns1ns2 中搜索服务。

  • all-namespaces(所有命名空间)。
spring.cloud.kubernetes.discovery.all-namespaces=true
none

虽然存在这样的选项,但这可能会对 kube-api 和你的应用程序造成负担。很少需要这样的设置。

  • one namespace。这是默认设置,如果您没有指定上述任何选项,它将按照命名空间解析中概述的规则工作。
备注

上述选项对于 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"
none

在配置好命名空间之后,接下来需要回答的问题是要发现哪些服务。可以将其视为应用什么过滤器。默认情况下,没有任何过滤,所有服务都会被发现。如果你需要缩小发现客户端可以找到的范围,你有两种选择:

  • 仅选择匹配特定服务标签的服务。此属性通过 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$"'
none

这告诉发现客户端只获取那些 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 - 服务实例的唯一标识符

  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. schemehttphttps(取决于 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 启用此支持。



<Translate>
<TranslateTarget>

要发现未被 Kubernetes API 服务器标记为 "ready" 的服务端点地址,你可以在 `application.properties` 中设置以下属性(默认值为 false):

</TranslateTarget>
<TranslateSource>

To discover service endpoint addresses that are not marked as "ready" by the kubernetes api server, you can set the following property in `application.properties` (default: false):

</TranslateSource>
</Translate>

```none
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
none

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

一些 Spring Cloud 组件使用 DiscoveryClient 来获取有关本地服务实例的信息。为了使这一机制正常工作,你需要将 Kubernetes 服务名称与 spring.application.name 属性保持一致。

备注

spring.application.name 对于在 Kubernetes 中注册的应用程序名称没有影响



<Translate>
<TranslateTarget>

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

</TranslateTarget>
<TranslateSource>

Spring Cloud Kubernetes can also watch the Kubernetes service catalog for changes and update the `DiscoveryClient` implementation accordingly. In order to enable this functionality you need to add `@EnableScheduling` on a configuration class in your application. By "watch", we mean that we will publish a heartbeat event every `spring.cloud.kubernetes.discovery.catalog-services-watch-delay` milliseconds (by default it is `30000`). For the http discovery server this must be an environment variable set in deployment yaml:

</TranslateSource>
</Translate>

containers:

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

<Translate>
<TranslateTarget>

心跳事件将包含目标引用(及其命名空间的所有端点的地址(关于将返回的确切详细信息,您可以在 `KubernetesCatalogWatch` 中查看)。这是一个实现细节,心跳事件的监听者不应依赖这些细节。相反,他们应该通过 `equals` 方法查看两个连续心跳之间是否存在差异。我们将确保返回一个符合 equals 契约的正确实现。端点将在以下情况下被查询:

- `all-namespaces`(通过 `spring.cloud.kubernetes.discovery.all-namespaces=true` 启用)

</TranslateTarget>
<TranslateSource>

The heartbeat event will contain the target references (and their namespaces of the addresses of all endpoints (for the exact details of what will get returned you can take a look inside `KubernetesCatalogWatch`). This is an implementation detail, and listeners of the heartbeat event should not rely on the details. Instead, they should see if there are differences between two subsequent heartbeats via `equals` method. We will take care to return a correct implementation that adheres to the equals contract. The endpoints will be queried in either : - `all-namespaces` (enabled via `spring.cloud.kubernetes.discovery.all-namespaces=true`)

</TranslateSource>
</Translate>

<Translate>
<TranslateTarget>

* `selective namespaces`(通过 `spring.cloud.kubernetes.discovery.namespaces` 启用),例如:

* 如果未采用上述两种路径,则通过[命名空间解析](property-source-config/index.md#namespace-resolution)的 `one namespace`。

</TranslateTarget>
<TranslateSource>

* `selective namespaces` (enabled via `spring.cloud.kubernetes.discovery.namespaces`), for example:

* `one namespace` via [Namespace Resolution](property-source-config/index.md#namespace-resolution) if the above two paths are not taken.

</TranslateSource>
</Translate>

<Translate>
<TranslateTarget>

:::note
如果出于任何原因,你想要禁用 catalog watcher,你需要设置 `spring.cloud.kubernetes.discovery.catalog-services-watch.enabled=false`。对于 http 发现服务器,这需要作为环境变量在部署中设置,例如:
:::

</TranslateTarget>
<TranslateSource>

:::note
If, for any reasons, you want to disable catalog watcher, you need to set `spring.cloud.kubernetes.discovery.catalog-services-watch.enabled=false`. For the http discovery server, this needs to be an environment variable set in deployment for example:
:::

</TranslateSource>
</Translate>

```none
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 发现包含两个组件:服务器和客户端,我们强烈建议在它们之间对齐版本,否则可能无法正常工作。

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

默认情况下,我们使用 Endpoints(参见 kubernetes.io/docs/concepts/services-networking/service/#endpoints)API 来发现服务的当前状态。不过还有另一种方式,即通过 EndpointSliceskubernetes.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"]
none