Spring Cloud Kubernetes 移除 Eureka 中間件
Kubernetes 通過 Kube-proxy 組件柬脸、Service 對象實現(xiàn)了 Pod 的服務發(fā)現(xiàn)、負載均衡問題毙驯,在 Spring Cloud 體系中是通過 Eureka倒堕、Nacos 等中間件來實現(xiàn)的,既然我們的微服務是基于 Kubernetes 來部署的爆价,那這部分功能就可以下沉到基礎設施層垦巴,由 Kubernetes 來提供。
在 Spring-Cloud-Dependencies 中已經(jīng)引入了 Kubernetes 客戶端操作的相關包铭段,來解決微服務在 K8s 體系中服務發(fā)現(xiàn) Discovery(Service) 和配置中心 Config(ConfigMap) 的問題骤宣。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-dependencies</artifactId>
<version>${spring-cloud-kubernetes.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
下面還以 https://github.com/14032/cloud 這個 Demo 程序為例,來看下服務中如何使用 K8s 的服務發(fā)現(xiàn)功能來替代掉 Eureka序愚。
在 Kubernetes 的實現(xiàn)版本中憔披,首先去除掉 Eureka、Ribbon 客戶端的依賴爸吮。
引入 Spring Cloud Kubernetes 相關依賴做適配芬膝,Spring Cloud Kubernetes 本身引入了 Fabbric8 的 Kubernetes Client 作為客戶端來操作 Kubernetes API Server。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes</artifactId>
</dependency>
<!--<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-loadbalancer</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
</dependency>
上面提供了兩種負載均衡實現(xiàn)拗胜,ribbon 和 loadbalancer 選擇一個即可蔗候,再看下配置文件如下:
spring:
cloud:
kubernetes:
discovery:
service-name: ${spring.application.name}
all-namespaces: true
ribbon:
enabled: true
mode: service
cluster-domain: cluster.local
spring.cloud.kubernetes.ribbon.mode 提供了兩種模式:service 和 pod。
這兩種模式埂软,其實也就對應了 Kubernetes 的兩個 API 接口:
/api/v1/namespaces/cloud/endpoints
/api/v1/namespaces/cloud/services
@Bean
@ConditionalOnMissingBean
public ServerList<?> ribbonServerList(KubernetesClient client, IClientConfig config,...) {
KubernetesServerList serverList;
if (properties.getMode() == KubernetesRibbonMode.SERVICE) {
serverList = new KubernetesServicesServerList(client, properties);
}
else {
serverList = new KubernetesEndpointsServerList(client, properties);
}
return serverList;
}
如果選用 service 模式锈遥,Ribbon 的客戶端負載均衡也就不在有效纫事,而是使用 Kubernetes Service 本身具有的基于 DNS 的負載均衡功能。例如:auth-server.cloud.svc.cluster.local:9096
所灸,這種方式也不會再出現(xiàn)使用Eureka (服務端緩存丽惶、客戶端心跳、客戶端更新頻率)時因服務更新而導致的服務間短暫調(diào)用失敗問題爬立。
因為只有處于就緒狀態(tài)(readliness)的服務才會出現(xiàn)在 Service 的 Endpoints 站點列表中钾唬。pod 模式,就是去獲取 Service 代理的 Endpoints 站點侠驯,由 Ribbon 來提供負載均衡功能抡秆。
下面再看下 Spring Cloud Kubernetes 是如何獲取 K8s 集群服務列表的?答案就是:Fabbric8吟策。
Fabbric8 通過默認的 Kubernetes API Server 的代理 Service 域名來訪問 API Server儒士。
當你從 Pod 中訪問 API 時,定位和驗證 apiserver 會有些許不同檩坚。
在 Pod 中定位 apiserver 的推薦方式是通過kubernetes.default.svc
這個 DNS 名稱着撩,該名稱將會解析為服務 IP,然后服務 IP 將會路由到 apiserver匾委。
向 apiserver 進行身份驗證的推薦方法是使用 服務帳戶 憑據(jù)拖叙。 通過 kube-system,Pod 與服務帳戶相關聯(lián)赂乐,并且該服務帳戶的憑證(token) 被放置在該 Pod 中每個容器的文件系統(tǒng)中薯鳍,位于/var/run/secrets/kubernetes.io/serviceaccount/token
。
DEFAULT_MASTER_URL = "https://kubernetes.default.svc"
default 空間下沪猴,名稱為 kubernetes 的 Service 的 Endpoints 即為 Master 節(jié)點上 6443 端口的服務辐啄,下圖可以看到 6443 端口既是 kube-apiserver 組件。
經(jīng)過上面的改造后运嗜,我們就可以重新構建鏡像將微服務部署到 Kubernetes 集群中,當服務啟動悯舟,相互訪問的時候担租,會出現(xiàn)如下錯誤,Message: Forbidden!Configured service account doesn't have access
Pod 內(nèi)沒有權限去訪問 apiserver抵怎,如果你了解過 Kubernetes 基于角色的權限控制 (RBAC)的功能奋救,你會立即想到要給 Pod 重新綁定一個 ServiceAccount 來授權操作 kube-apiserver 接口。
2021-07-25 17:42:21.000 ERROR 1 [ scheduling-1] [o.s.c.k.d.KubernetesCatalogWatch ] - Error watching Kubernetes Services
io.fabric8.kubernetes.client.KubernetesClientException: Failure executing: GET at: https://10.96.0.1/api/v1/endpoints. Message: Forbidden!Configured service account doesn't have access. Service account may have been revoked. endpoints is forbidden: User "system:serviceaccount:cloud:default" cannot list resource "endpoints" in API group "" at the cluster scope.
at io.fabric8.kubernetes.client.dsl.base.OperationSupport.requestFailure(OperationSupport.java:589)
at io.fabric8.kubernetes.client.dsl.base.OperationSupport.assertResponseCode(OperationSupport.java:526)
at io.fabric8.kubernetes.client.dsl.base.OperationSupport.handleResponse(OperationSupport.java:492)
參考文檔:spring-cloud-kubernetes/docs/current/reference/html/#service-account
我這里直接給了 Kubernetes 默認的集群只讀角色 view反惕,更細粒度的可參看官方文檔尝艘。
apiVersion: v1
kind: ServiceAccount
metadata:
name: cloud-account
namespace: cloud
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cloud-account
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
subjects:
- kind: ServiceAccount
name: cloud-account
namespace: cloud
然后再為微服務的每個 Pod 綁定此賬號,這樣容器里的應用就可以使用這個 ServiceAccount 來訪問 API Server 了姿染。
spec:
serviceAccountName: cloud-account
這樣基于 kubernetes 平臺部署的微服務背亥,就不再需要引入 Eureka 中間件來解決服務發(fā)現(xiàn)/注冊的功能,而是在基礎設施層替代原有的應用層面的技術組件。
同樣的狡汉,配置中心也可以下沉到基礎設施層娄徊,由 kubernetes 中的 ConfigMap 對象來提供。
網(wǎng)關層盾戴,前面有介紹寄锐,Ingress 并不能勝任 API 網(wǎng)關的角色。
如果引入 Istio 服務網(wǎng)格尖啡,Istio 將會接管 Kube-proxy 的代理能力橄仆,以及 Kubernetes 中 Service 服務發(fā)現(xiàn)的能力。Istio 控制平面會和 Kubernetes 的 API 對接衅斩,將集群內(nèi)部所有的服務沿癞、站點信息下發(fā)到每一個 Sidecar 代理中。
也即 Pod 中的所有請求被 Envoy 代理攔截后矛渴,直接根據(jù)本地的服務列表信息進行路由負載轉發(fā)椎扬。
這時 Spring Cloud Feign、Discovery具温、Ribbon 等都可以移除蚕涤,服務之間直接以服務名稱(svc 域名)進行訪問。
~ END ~