DiscoveryClient for Kubernetes
此项目针对 Kubernetes 提供了 Discovery Client 的实现。此客户端允许您按名称查询 Kubernetes 端点(参见 services)。通常,Kubernetes API 服务器将服务显示为端点集合,这些端点表示 http
和 https
地址,并且客户端可以从作为 Pod 运行的 Spring Boot 应用程序对其进行访问。
This project provides an implementation of Discovery Client
for Kubernetes.
This client lets you query Kubernetes endpoints (see services) by name.
A service is typically exposed by the Kubernetes API server as a collection of endpoints that represent http
and https
addresses and that a client can
access from a Spring Boot application running as a pod.
DiscoveryClient 还可以找到类型为 ExternalName
的服务(请参阅 ExternalName services)。目前,如果将 spring.cloud.kubernetes.discovery.include-external-name-services
设置为 true
(默认为 false
),只有当服务类型支持外部名称时才可用。
DiscoveryClient can also find services of type ExternalName
(see ExternalName services). At the moment, external name support type of services is only available if the following property spring.cloud.kubernetes.discovery.include-external-name-services
is set to true
(it is false
by default).
我们支持 3 种类型的发现客户端:
There are 3 types of discovery clients that we support:
1.
Fabric8 Kubernetes Client
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8</artifactId>
</dependency>
2.
Kubernetes Java Client
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-client</artifactId>
</dependency>
3.
基于 HTTP 的 DiscoveryClient
HTTP Based DiscoveryClient
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-discoveryclient</artifactId>
</dependency>
|
|
要启用 DiscoveryClient
的加载,请对相应的配置或应用程序类添加 @EnableDiscoveryClient
,如下例所示:
To enable loading of the DiscoveryClient
, add @EnableDiscoveryClient
to the according configuration or application class, as the following example shows:
@SpringBootApplication
@EnableDiscoveryClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
然后,您可以通过自动注入的方式在代码中注入客户端,如下例所示:
Then you can inject the client in your code simply by autowiring it, as the following example shows:
@Autowired
private DiscoveryClient discoveryClient;
您应该问自己的第一个问题是 DiscoveryClient
应该在哪里查找服务。在 Kubernetes 环境中,这意味着哪些命名空间。这里有 3 个选项:
The first question you should ask yourself is where a DiscoveryClient
supposed to discover services. In the kubernetes world, this means what namespace(s). There are 3 options here:
-
selective namespaces
. For example:
spring.cloud.kubernetes.discovery.namespaces[0]=ns1
spring.cloud.kubernetes.discovery.namespaces[1]=ns2
这种配置使发现客户端仅搜索命名空间 ns1
和 ns2
中的服务。
Such a configuration makes discovery client only search for services in two namespaces ns1
and ns2
.
-
all-namespaces
.
spring.cloud.kubernetes.discovery.all-namespaces=true
虽然有这样的选项,但这可能会给 kube-api 和您的应用程序带来负担。很少需要这样的设置。
While such an option exists, this can be a burden on both kube-api and your application. It is rare to need such a setting.
-
one namespace
. This is the default setting, if you do not specify any of the above. It works on the rules outlined in Namespace Resolution.
上述选项以与 fabric8 和 k8s 客户端完全相同的方式执行。对于基于 HTTP 的客户端,需要在 server 上启用这些选项。可以通过在 deployment.yaml
中设置这些选项来实现,该选项用于在集群中部署图像,即使用环境变量。
The above options work exactly as written for fabric8 and k8s clients. For the HTTP based client, you need to enable those options on the server. That can be achieved by setting them in deployment.yaml
used to deploy the image in the cluster, using env variable(s).
例如:
For example:
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"
配置命名空间后,要回答的下一个问题是发现哪些服务。考虑将其视为要应用的筛选器。默认情况下,不应用任何筛选,并且发现所有服务。如果您需要缩小发现客户端所能找到的范围,您有两个选择:
Once namespaces have been configured, the next question to answer is what services to discover. Think about it as what filter to apply. By default, no filtering is applied at all and all services are discovered. If you need to narrow what discovery client can find, you have two options:
-
Only take services that match certain service labels. This property is specified with:
spring.cloud.kubernetes.discovery.service-labels
. It accepts aMap
and only those services that have such labels (as seen inmetadata.labels
in the service definition) will be taken into account. -
The other option is to use SpEL expression. This is denoted by the
spring.cloud.kubernetes.discovery.filter
property, and its value depends on the client that you chose. If you use the fabric8 client, this SpEL expression must be created againstio.fabric8.kubernetes.api.model.Service
class. One such example could be:
spring.cloud.kubernetes.discovery.filter='#root.metadata.namespace matches "^.+A$"'
这告诉发现客户端仅获取具有以大写“A”结尾的 metadata.namespace
的服务。
which tells discovery client to only get services that have the metadata.namespace
that ends in upper case A
.
如果您的发现客户端基于 k8s 原生客户端,则 SpEL 表达式必须基于 io.kubernetes.client.openapi.models.V1Service
类。上面显示的相同筛选器会在这里起作用。
If your discovery client is based on k8s-native client, then the SpEL expression must be based on io.kubernetes.client.openapi.models.V1Service
class. The same filter showed above would work here.
如果您的发现客户端是基于 http 的,则 SeEL 表达式必须基于相同的 io.kubernetes.client.openapi.models.V1Service
类,唯一的区别在于这需要在部署 yaml 中设置为环境变量:
If your discovery client is the http based one, then the SeEL expression has to be based on the same io.kubernetes.client.openapi.models.V1Service
class, with the only distinction that this needs to be set as an env variable in the deployment 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
。
It’s now time to think what discovery client is supposed to return back. In general, there are two methods that DiscoveryClient
has: getServices
and getInstances
.
getServices
将返回在 metadata.name
中看到的服务名称。
getServices
will return the service names as seen in the metadata.name
.
这种方法将返回唯一的服务名称,即使不同命名空间(您为搜索选择的命名空间)中存在重复。 |
This method will return unique service names, even if there are duplicates across different namespaces (that you chose for the search). |
getInstances
返回 List<ServiceInstance>
。除了 ServiceInstance
所具有的常规字段外,我们还添加了一些数据,如命名空间或 Pod 元数据(本文档中稍后会对此进行更多说明)。以下是我们目前返回的数据:
getInstances
returns a List<ServiceInstance>
. Besides the usual fields that a ServiceInstance
has, we also add some data, like namespace or pod metadata (more explanation about these will follow in the document). Here is the data that we return at the moment:
-
instanceId
- unique id of the service instance -
serviceId
- the name of the service (it is the same as the one reported by callinggetServices
) -
host
- IP of the instance (or name in case of theExternalName
type of service) -
port
- port number of the instance. This requires a bit more explanation, as choosing the port number has its rules:[style="loweralpha"]-
service has no port defined, 0 (zero) will be returned.
-
service has a single port defined, that one will be returned.
-
If the service has a label
primary-port-name
, we will use the port number that has the name specified in the label’s value. -
If the above label is not present, then we will use the port name specified in
spring.cloud.kubernetes.discovery.primary-port-name
to find the port number. -
If neither of the above are specified, we will use the port named
https
orhttp
to compute the port number. -
As a last resort we wil pick the first port in the list of ports. This last option may result in non-deterministic behaviour.
-
-
uri
of the service instance -
scheme
eitherhttp
orhttps
(depending on thesecure
result) -
metadata
of the service:[style="loweralpha"]-
labels
(if requested viaspring.cloud.kubernetes.discovery.metadata.add-labels=true
). Label keys can be "prefixed" with the value ofspring.cloud.kubernetes.discovery.metadata.labels-prefix
if it is set. -
annotations
(if requested viaspring.cloud.kubernetes.discovery.metadata.add-annotations=true
). Annotations keys can be "prefixed" with the value ofspring.cloud.kubernetes.discovery.metadata.annotations-prefix
if it is set. -
ports
(if requested viaspring.cloud.kubernetes.discovery.metadata.add-ports=true
). Port keys can be "prefixed" with the value ofspring.cloud.kubernetes.discovery.metadata.ports-prefix
if it is set. -
k8s_namespace
with the value of the namespace where instance resides. -
type
that holds the service type, for exampleClusterIP
orExternalName
-
-
secure
if the port that was discovered should be treated as secure. We will use the same rules outlined above to find the port name and number, and then:[style="loweralpha"]-
If this service has a label called
secured
with any of the values :["true", "on", "yes", "1"]
, then treat the port that was found as secure. -
If such a label is not found, search for an annotation called
secured
and apply the same above rules. -
If this port number is part of
spring.cloud.kubernetes.discovery.known-secure-ports
(by default this value holds[443, 8443]
), treat port number as secured. -
Last resort is to see if port name matches
https
; if it does treat this port as secured.
-
-
namespace
- the namespace of the found instance. -
pod-metadata
labels and annotations of the service instance (pod), in the form ofMap<String, Map<String, String>>
. This support needs to be enabled viaspring.cloud.kubernetes.discovery.metadata.add-pod-labels=true
and/orspring.cloud.kubernetes.discovery.metadata.add-pod-annotaations=true
为了发现未被 kubernetes api 服务器标记为“已就绪”的服务端点地址,您可以在 application.properties
中设置以下属性(默认值:false):
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):
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
中设置以下属性:
This might be useful when discovering services for monitoring purposes, and would enable inspecting the /health
endpoint of not-ready service instances.
If you want to get the list of ServiceInstance
to also include the ExternalName
type services, you need to enable that support via: spring.cloud.kubernetes.discovery.include-external-name-services=true
. As such, when calling DiscoveryClient::getInstances
those will be returned also. You can distinguish between ExternalName
and any other types by inspecting ServiceInstance::getMetadata
and lookup for a field called type
. This will be the type of the service returned : ExternalName
/ClusterIP
, etc.
If, for any reason, you need to disable the DiscoveryClient
, you can set the following property in application.properties
:
spring.main.cloud-platform=NONE
请注意,发现客户端的支持是自动的,取决于您在何处运行应用程序。因此,可能不需要上述设置。
Note that the support of discovery client is automatic, depending on where you run the application. So the above setting might not be needed.
某些 Spring Cloud 组件使用 DiscoveryClient
来获取有关本地服务实例的信息。要做到这一点,您需要将 Kubernetes 服务名称与 spring.application.name
属性对齐。
Some Spring Cloud components use the DiscoveryClient
in order to obtain information about the local service instance. For
this to work, you need to align the Kubernetes service name with the spring.application.name
property.
|
|
Spring Cloud Kubernetes 还可以监视 Kubernetes 服务目录的更改,并相应地更新 DiscoveryClient
实现。为了启用此功能,您需要在应用程序中的配置类中添加 @EnableScheduling
。通过“监视”,我们指的是我们将每 spring.cloud.kubernetes.discovery.catalog-services-watch-delay
毫秒发布一个心跳事件(默认值为 30000
)。对于 http 发现服务器,这必须是 deployment yaml 中设置的环境变量:
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:
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
方法查看两次连续心跳之间是否存在差异。我们将负责返回一个遵守相等契约的正确实现。将在以下任一位置查询端点:- all-namespaces
(通过 spring.cloud.kubernetes.discovery.all-namespaces=true
启用)
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
)
-
selective namespaces
(enabled viaspring.cloud.kubernetes.discovery.namespaces
), for example: -
one namespace
via Namespace Resolution if the above two paths are not taken.
出于任何原因,如果你想禁用目录监视器,你需要设置`spring.cloud.kubernetes.discovery.catalog-services-watch.enabled=false`。对于 http 发现服务器来说,需要在部署中设置环境变量,例如: |
If, for any reasons, you want to disable catalog watcher, you need to set |
SPRING_CLOUD_KUBERNETES_DISCOVERY_CATALOGSERVICESWATCH_ENABLED=FALSE
目录监视的功能适用于我们支持的所有 3 个发现客户端,但在 http 客户端的情况下,您需要了解一些注意事项。
The functionality of catalog watch works for all 3 discovery clients that we support, with some caveats that you need to be aware of in case of the http client.
-
The first is that this functionality is disabled by default, and it needs to be enabled in two places:
-
in discovery server via an environment variable in the deployment manifest, for example:---- 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"
* in discovery client, via a property in your `application.properties` for example:---- spring.cloud.kubernetes.http.discovery.catalog.watcher.enabled=true
-
The second point is that this is only supported since version
3.0.6
and upwards. -
Since http discovery has two components : server and client, we strongly recommend to align versions between them, otherwise things might not work.
-
If you decide to disable catalog watcher, you need to disable it in both server and client.
我们默认使用 Endpoints
(参见 [role="bare"][role="bare"]https://kubernetes.io/docs/concepts/services-networking/service/#endpoints)API 来了解服务的当前状态。还有另一种方式,即通过 EndpointSlices
([role="bare"][role="bare"]https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/)。可以通过一个属性(默认值为 false
)启用此支持: spring.cloud.kubernetes.discovery.use-endpoint-slices=true
。当然,您的集群也必须支持它。事实上,如果您启用了此属性,但集群不支持,我们将无法启动应用程序。如果您决定启用此支持,还需要设置适当的角色/集群角色。例如:
By default, we use the Endpoints
(see [role="bare"]https://kubernetes.io/docs/concepts/services-networking/service/#endpoints) API to find out the current state of services. There is another way though, via EndpointSlices
([role="bare"]https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/). Such support can be enabled via a property: spring.cloud.kubernetes.discovery.use-endpoint-slices=true
(by default it is false
). Of course, your cluster has to support it also. As a matter of fact, if you enable this property, but your cluster does not support it, we will fail starting the application. If you decide to enable such support, you also need proper Role/ClusterRole set-up. For example:
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"]