Kubernetes 的 DiscoveryClient
本專案為 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 種類型的發現客戶端
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
這樣的配置使發現客戶端只在 ns1 和 ns2 兩個名稱空間中搜索服務。
-
所有名稱空間.
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 有兩個方法: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 伺服器標記為“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"]