KubeSphere 核心架構淺析

語言: CN / TW / HK

KubeSphere 是在 K8s 之上構建的面向雲原生應用的容器混合雲管理系統。支持多雲與多集羣管理,提供全棧的自動化運維能力,幫助企業用户簡化 DevOps 工作流,提供運維友好的嚮導式操作界面,幫助企業快速構建一個強大和功能豐富的容器雲平台。

KubeSphere 為用户提供構建企業級 K8s 環境所需的多項功能,例如多雲與多集羣管理、K8s 資源管理、DevOps、應用生命週期管理、微服務治理(服務網格)、日誌查詢與收集、服務與網絡、多租户管理、監控吿警、事件與審計查詢、存儲管理、訪問權限控制、GPU 支持、網絡策略、鏡像倉庫管理以及安全管理等。

得益於 K8s 優秀的架構與設計,KubeSphere 取長補短採用了更為輕量的架構模式,靈活的整合資源,進一步豐富了 K8s 生態。

KubeSphere 核心架構

KubeSphere 的核心架構如下圖所示:

file

核心組件主要有三個:

  • ks-console 前端服務組件

  • ks-apiserver 後端服務組件

  • ks-controller-manager 資源狀態維護組件

KubeSphere 的後端設計中沿用了 K8s 聲明式 API 的風格,所有可操作的資源都儘可能的抽象成為 CustomResource[1]。與命令式 API 相比,聲明性 API 的使用更加簡潔,並且提供了更好的抽象性,吿訴程序最終的期望狀態(做什麼),而不關心怎麼做。

在聲明式 API 中:

  1. 你的 API 包含相對而言為數不多的、尺寸較小的對象(資源)。

  2. 對象定義了應用或者基礎設施的配置信息。

  3. 對象更新操作頻率較低。

  4. 通常需要人來讀取或寫入對象。

  5. 對象的主要操作是 CRUD 風格的(創建、讀取、更新和刪除)。

  6. 不需要跨對象的事務支持:API 對象代表的是期望狀態而非確切實際狀態。

命令式 API(Imperative API)與聲明式有所不同。以下跡象表明你的 API 可能不是聲明式的:

  1. 客户端發出“做這個操作”的指令,之後在該操作結束時獲得同步響應。

  2. 客户端發出“做這個操作”的指令,並獲得一個操作 ID,之後需要判斷請求是否成功完成。

  3. 你會將你的 API 類比為 RPC。

  4. 直接存儲大量數據。

  5. 在對象上執行的常規操作並非 CRUD 風格。

  6. API 不太容易用對象來建模。

藉助 kube-apiserver、etcd 實現數據同步和數據持久化,通過 ks-controller-manager 維護這些資源的狀態,以達到最終狀態的一致性。如果你熟悉 K8s,可以很好地理解聲明式 API 帶來的好處,這也是 KubeSphere 最為核心的部分。

例如 KubeSphere 中的流水線、用户憑證、用户實體、吿警通知的配置,都可以抽象為資源實體,藉助 K8s 成熟的架構與工具鏈,可以方便的與 K8s 進行結合,降低各組件之間的耦合,降低系統的複雜度。

ks-apiserver 的核心架構

ks-apiserver 是 KubeSphere 核心的後端組件,負責前後端數據的交互、請求的代理分發、認證與鑑權。下圖是 ks-apiserver 的核心架構:

file

ks-apiserver 的開發使用了 go-restful[2] 框架,可以在請求鏈路中增加多個 Filter 用於動態的攔截請求和響應,實現認證、鑑權、審計邏輯轉發和反向代理功能,KubeSphere 的 API 風格也儘可能的學習 K8s 的模式[3],方便使用 RBAC[4] 進行權限控制。

藉助 CRD + controller 的方式進行解耦,可以極大地簡化與第三方工具、軟件的集成方式。

K8s 社區也提供了豐富的工具鏈,藉助 controller-runtime[5] 和 kubebuiler[6] 可以迅速的搭建起開發腳手架。

API 聚合與權限控制

可以通過拓展 ks-apiserver 實現 API 的聚合,進一步實現功能拓展、聚合查詢等功能。在 API 的開發過程中需要遵循以下規範,以便與 KubeSphere 的租户體系、資源體系進行整合。

  • API 聚合

  • 權限控制

  • CRD + controller

API 規範

	# 通過 api group 進行分組
	/apis/{api-group}/{version}/{resources}
	# 示例
	/apis/apps/v1/deployments
	/kapis/iam.kubesphere.io/v1alpha2/users
	# api core
	/api/v1/namespaces

	# 通過 path 區分不同的動作
	/api/{version}/watch/{resources}
	/api/{version}/proxy/{resources}/{resource}

	# 通過 path 區分不同的資源層級
			/kapis/{api-group}/{version}/workspaces/{workspace}/{resources}/{resource}
			/api/{version}/namespaces/{namespace}/{resource}

規範 API 的目的:

  1. 更好的對資源進行抽象,抽象為 Object 更適合聲明式 API。

  2. 更好的對 API 進行管理,版本、分組、分層,更方便 API 的拓展。

  3. 更好的與權限控制進行整合,可以方便的從請求中獲取元數據、apigroup、scope、version、verb。

權限控制

KubeSphere 權限控制的核心是 RBAC[7] 基於角色的訪問控制。 關鍵的對象有:Role、User、RoleBinding。

Role 定義了一個角色可以訪問的資源。

角色是根據資源層級進行劃分的,cluster role、workspace role、namespace role 不同層級的角色定義了該角色在當前層級可以訪問的資源。

	apiVersion: rbac.authorization.k8s.io/v1
	kind: ClusterRole
	metadata:
		name: role-grantor
	rules:
	- apiGroups: ["rbac.authorization.k8s.io"]
		resources: ["rolebindings"]
		verbs: ["create"]
	- apiGroups: ["rbac.authorization.k8s.io"]
		resources: ["clusterroles"]
		verbs: ["bind"]
		# 忽略 resourceNames 意味着允許綁定任何 ClusterRole
		resourceNames: ["admin","edit","view"]
	- nonResourceURLs: ["/healthz", "/healthz/*"] # nonResourceURL 中的 '*' 是一個全局通配符
		verbs: ["get", "post"]

RoleBinding 可綁定角色到某主體(Subject)上。主體可以是組、用户或者服務賬户。

	apiVersion: rbac.authorization.k8s.io/v1
	kind: ClusterRoleBinding
	metadata:
		name: role-grantor-binding
	roleRef:
		apiGroup: rbac.authorization.k8s.io
		kind: ClusterRole
		name: role-grantor
	subjects:
	- apiGroup: rbac.authorization.k8s.io
		kind: User
		name: user-1

CRD + controller

自定義資源(Custom Resource) 是對 K8s API 的擴展,可以通過動態註冊的方式拓展 K8s API。用户可以使用 kubectl 來創建和訪問其中的對象,就像操作內置資源一樣。

通過 CRD 對資源進行抽象,再通過 controller 監聽資源變化維護資源狀態, controller 的核心是 Reconcile,與它的意思一樣,通過被動、定時觸發的方式對資源狀態進行維護,直至達到聲明的狀態。

file

以 User 資源為例,我們可以定義一下結構的 CRD[8] 對 User 進行抽象:

	apiVersion: iam.kubesphere.io/v1alpha2
	kind: User
	metadata:
		annotations:
			iam.kubesphere.io/last-password-change-time: "2021-05-12T05:50:07Z"
		name: admin
		resourceVersion: "478503717"
		selfLink: /apis/iam.kubesphere.io/v1alpha2/users/admin
		uid: 9e438fcc-f179-4254-b534-e913dfd7a727
	spec:
		email: [email protected]
		lang: zh
		description: 'description'
		password: $2a$10$w312tzLTvXObnfEYiIrk9u5Nu/reJpwQeI66vrM1XJETWtpjd1/q2
	status:
		lastLoginTime: "2021-06-08T06:37:36Z"
		state: Active

對應的 API 為:

	# 創建
	POST /apis/iam.kubesphere.io/v1alpha2/users

	# 刪除
	DELETE /apis/iam.kubesphere.io/v1alpha2/users/{username}

	# 修改
	PUT /apis/iam.kubesphere.io/v1alpha2/users/{username}
	PATCH /apis/iam.kubesphere.io/v1alpha2/users/{username}

	# 查詢
	GET /apis/iam.kubesphere.io/v1alpha2/users
	GET /apis/iam.kubesphere.io/v1alpha2/users/{username}

ks-apiserver 負責將這些數據寫入 K8s 再由 informer 同步到各個副本中。

ks-controller-manager 通過監聽數據變化,對資源狀態進行維護,以創建用户為例, 通過 POST/apis/iam.kubesphere.io/v1alpha2/users 創建用户之後, user controller 會對用户資源狀態進行同步。

	func (c *userController) reconcile(key string) error {
	 // Get the user with this name
	 user, err := c.userLister.Get(key)

					if err != nil {
							// The user may no longer exist, in which case we stop
							// processing.
							if errors.IsNotFound(err) {
									utilruntime.HandleError(fmt.Errorf("user '%s' in work queue no longer exists", key))
									return nil
							}
							klog.Error(err)
							return err
					}

	 if user, err = c.encryptPassword(user); err != nil {
		klog.Error(err)
		return err
	 }

	 if user, err = c.syncUserStatus(user); err != nil {
		klog.Error(err)
		return err
	 }

	 // synchronization through kubefed-controller when multi cluster is enabled
	 if c.multiClusterEnabled {
		if err = c.multiClusterSync(user); err != nil {
		 c.recorder.Event(user, corev1.EventTypeWarning, controller.FailedSynced, fmt.Sprintf(syncFailMessage, err))
		 return err
		}
	 }

	 c.recorder.Event(user, corev1.EventTypeNormal, successSynced, messageResourceSynced)
	 return nil
	}

通過聲明式的 API 將複雜的邏輯放到 controller 進行處理,方便解耦。可以很方便的與其他系統、服務進行集成,例如:

	/apis/devops.kubesphere.io/v1alpha2/namespaces/{namespace}/pipelines
	/apis/devops.kubesphere.io/v1alpha2/namespaces/{namespace}/credentials
	/apis/openpitrix.io/v1alpha2/namespaces/{namespace}/applications
	/apis/notification.kubesphere.io/v1alpha2/configs

對應的權限控制策略: 定義一個可以增刪改查 user 資源的角色。

	apiVersion: rbac.authorization.k8s.io/v1
	kind: ClusterRole
	metadata:
		name: user-manager
	rules:
	- apiGroups: ["iam.kubesphere.io"]
		resources: ["users"]
		verbs: ["create","delete","patch","update","get","list"]

定義一個可以創建 pipeline 資源的角色。

	apiVersion: rbac.authorization.k8s.io/v1
	kind: Role
	metadata:
		name: devops-manager
	rules:
	- apiGroups: ["devops.kubesphere.io"]
		resources: ["pipelines"]
		verbs: ["create","delete","patch","update","get","list"]

引用鏈接

[1]CustomResource: http://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/

[2]go-restful: http://github.com/emicklei/go-restful

[3]K8s 的模式: http://kubernetes.io/docs/reference/using-api/api-concepts/

[4]RBAC: http://kubernetes.io/docs/reference/access-authn-authz/rbac/

[5]controller-runtime: http://github.com/kubernetes-sigs/controller-runtime

[6]kubebuiler: http://github.com/kubernetes-sigs/kubebuilder

[7]RBAC: http://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/

[8]CRD : http://raw.githubusercontent.com/kubesphere/kubesphere/master/config/crds/iam.kubesphere.io_users.yaml

作者

萬宏明 KubeSphere 核心貢獻者,專注於雲原生安全領域

本文由博客一文多發平台 OpenWrite 發佈!