Kubernetes DiscoveryClient
本项目为 Kubernetes 提供了 Discovery Client 的实现。此客户端允许您按名称查询 Kubernetes 端点(参见 服务)。服务通常由 Kubernetes API 服务器暴露为一组端点,这些端点代表 http
和 https
地址,客户端可以从作为 pod 运行的 Spring Boot 应用程序访问这些地址。
DiscoveryClient 还可以查找 ExternalName
类型的服务(参见 ExternalName services)。目前,只有将属性 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 个选项
-
selective namespaces
(选择性命名空间)。例如
spring.cloud.kubernetes.discovery.namespaces[0]=ns1
spring.cloud.kubernetes.discovery.namespaces[1]=ns2
这样的配置使得发现客户端只在两个命名空间 ns1
和 ns2
中搜索服务。
-
all-namespaces (所有命名空间)
.
spring.cloud.kubernetes.discovery.all-namespaces=true
虽然存在这样的选项,但这可能会给 kube-api 和您的应用程序都带来负担。很少需要这样的设置。
-
one namespace
(一个命名空间)。如果您未指定上述任何选项,这将是默认设置。它遵循 Namespace Resolution 中概述的规则。
上述选项对于 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 expression。这由
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
有两个方法:getServices
和 getInstances
。
getServices
将返回在 metadata.name
中看到的服务*名称*。
即使在您选择搜索的不同命名空间中存在重复项,此方法也会返回唯一的服务名称。 |
getInstances
返回一个 List<ServiceInstance>
。除了 ServiceInstance
通常具有的字段外,我们还添加了一些数据,例如命名空间或 pod 元数据(文档中将对此进行更多解释)。这是我们目前返回的数据
-
instanceId
- 服务实例的唯一 ID -
serviceId
- 服务的名称(与调用getServices
报告的名称相同) -
host
- 实例的 IP(或ExternalName
类型服务情况下的名称) -
port
- 实例的端口号。这需要更多解释,因为选择端口号有其规则-
如果服务没有定义端口,将返回 0(零)。
-
如果服务定义了单个端口,将返回该端口。
-
如果服务具有标签
primary-port-name
,我们将使用标签值中指定的名称所对应的端口号。 -
如果上述标签不存在,我们将使用
spring.cloud.kubernetes.discovery.primary-port-name
中指定的端口名称来查找端口号。 -
如果以上两者均未指定,我们将使用名为
https
或http
的端口来计算端口号。 -
最后的办法是我们将选择端口列表中的第一个端口。最后一个选项可能会导致非确定性行为。
-
-
服务实例的
uri
-
scheme
为http
或https
(取决于secure
的结果) -
服务的
metadata
(元数据)-
labels
(如果通过spring.cloud.kubernetes.discovery.metadata.add-labels=true
请求)。如果设置了spring.cloud.kubernetes.discovery.metadata.labels-prefix
的值,标签键可以被其“前缀”。 -
annotations
(如果通过spring.cloud.kubernetes.discovery.metadata.add-annotations=true
请求)。如果设置了spring.cloud.kubernetes.discovery.metadata.annotations-prefix
的值,注解键可以被其“前缀”。 -
ports
(如果通过spring.cloud.kubernetes.discovery.metadata.add-ports=true
请求)。如果设置了spring.cloud.kubernetes.discovery.metadata.ports-prefix
的值,端口键可以被其“前缀”。 -
k8s_namespace
,其值为实例所在的命名空间。 -
type
,用于存储服务类型,例如ClusterIP
或ExternalName
-
-
secure
(安全),表示发现的端口是否应被视为安全。我们将使用上面概述的相同规则来查找端口名称和端口号,然后-
如果此服务有一个名为
secured
的标签,其值为["true", "on", "yes", "1"]
中的任何一个,则将找到的端口视为安全。 -
如果未找到此类标签,则搜索名为
secured
的注解并应用上述相同规则。 -
如果此端口号是
spring.cloud.kubernetes.discovery.known-secure-ports
的一部分(默认情况下此值为[443, 8443]
),则将端口号视为安全。 -
最后的办法是查看端口名称是否匹配
https
;如果匹配,则将此端口视为安全。
-
-
namespace
- 找到实例的命名空间。 -
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 服务器标记为“就绪”的服务端点地址,您可以在 application.properties
中设置以下属性(默认为: false)
spring.cloud.kubernetes.discovery.include-not-ready-addresses=true
这在出于监控目的发现服务时可能很有用,并且可以检查未就绪的服务实例的 `/health` 端点。如果您想获取包含 ExternalName 类型服务的 ServiceInstance 列表,您需要通过 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
心跳事件将包含所有端点地址的目标引用(及其命名空间)(关于返回内容的具体细节,您可以查看 KubernetesCatalogWatch
内部)。这是一个实现细节,心跳事件的监听器不应依赖于这些细节。相反,它们应该通过 equals
方法查看两次连续心跳之间是否存在差异。我们将确保返回一个遵循 equals 契约的正确实现。端点将在以下模式之一中被查询:- all-namespaces
(通过 spring.cloud.kubernetes.discovery.all-namespaces=true
启用)
-
selective namespaces
(通过spring.cloud.kubernetes.discovery.namespaces
启用),例如 -
如果上述两种方式均未采用,则通过 Namespace Resolution 使用
one namespace
(一个命名空间)。
如果由于任何原因想禁用目录观察者,您需要设置 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 发现有*两个*组件:服务器和客户端,我们强烈建议它们之间的版本保持一致,否则可能无法正常工作。
-
如果您决定禁用目录观察者,则需要在服务器和客户端中都禁用它。
默认情况下,我们使用 `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"]