Kubernetes 的 DiscoveryClient

本專案為 Kubernetes 提供了 Discovery Client 的實現。此客戶端允許您按名稱查詢 Kubernetes 端點(請參閱 服務)。服務通常由 Kubernetes API 伺服器公開為一系列端點,這些端點代表 httphttps 地址,客戶端可以從作為 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

這樣的配置使發現客戶端只在 ns1ns2 兩個名稱空間中搜索服務。

  • 所有名稱空間.

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 有兩個方法:getServicesgetInstances

getServices 將返回服務 名稱,如 metadata.name 中所示。

此方法將返回唯一的服務名稱,即使在您選擇用於搜尋的不同名稱空間中存在重複項。

getInstances 返回一個 List<ServiceInstance>。除了 ServiceInstance 具有的常規欄位外,我們還添加了一些資料,例如名稱空間或 pod 元資料(有關這些的更多解釋將在文件中隨後提供)。這是我們目前返回的資料

  1. instanceId - 服務例項的唯一 ID

  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. schemehttp 還是 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,用於儲存服務型別,例如 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 啟用


要發現未被 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"]
© . This site is unofficial and not affiliated with VMware.