跳到主要内容

Kubernetes 的 DiscoveryClient

DeepSeek V3 中英对照 DiscoveryClient for Kubernetes

该项目为 Kubernetes 提供了一个 Discovery Client 的实现。该客户端允许你按名称查询 Kubernetes 端点(参见 服务)。服务通常由 Kubernetes API 服务器公开为一组端点,这些端点表示 http 和 https 地址,客户端可以从作为 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

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

  • 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 有两个方法:getServices 和 getInstances。

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. 如果上述都没有指定,我们将使用名为 https 或 http 的端口来计算端口号。

    6. 最后,我们将选择端口列表中的第一个端口。这最后一种选择可能会导致非确定性的行为。

  5. 服务实例的 uri

  6. scheme 是 http 或 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 包含服务类型,例如 ClusterIP 或 ExternalName

  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 来发现服务的当前状态。不过还有另一种方式,即通过 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"]
none