About the author: Wan Hongming, the core contributor of KubeSphere, focuses on the field of cloud native security.
KubeSphere is a container hybrid cloud management system built on top of Kubernetes for cloud-native applications. It supports multi-cloud and multi-cluster management, provides full-stack automated operation and maintenance capabilities, helps enterprise users simplify DevOps workflow, and provides an operation-friendly wizard-style interface to help enterprises quickly build a powerful and feature-rich container cloud platform.
KubeSphere provides users with multiple functions required to build an enterprise-level Kubernetes environment, such as multi-cloud and multi-cluster management, Kubernetes resource management, DevOps, application lifecycle management, microservice governance (service mesh), log query and collection, service and Network, multi-tenant management, monitoring alarms, event and audit queries, storage management, access control, GPU support, network policies, image warehouse management, and security management.
Thanks to the excellent architecture and design of Kubernetes, KubeSphere adopts a lighter-weight architecture model and integrates resources flexibly, further enriching the K8s ecosystem.
KubeSphere Core Architecture
The core architecture of KubeSphere is shown in the following figure:
There are three main core components:
- ks-console front-end service component
- ks-apiserver backend service component
- ks-controller-manager resource state maintenance component
The back-end design of KubeSphere follows the style of K8s declarative API, and all operable resources are abstracted as CustomResource as much as possible . Compared to the imperative API, the declarative API is more concise to use and provides better abstraction, telling the program the final desired state (what to do), not how to do it.
Typically, in a declarative API:
- Your API contains relatively few objects (resources) of small size.
- Objects define application or infrastructure configuration information.
- Object update operations are less frequent.
- Often a human is required to read or write objects.
- The main operations on objects are CRUD-style (create, read, update, and delete).
- Cross-object transaction support is not required: API objects represent the desired state rather than the exact actual state.
Imperative API (Imperative API) is different from declarative. The following are signs that your API may not be declarative:
- The client issues a "do this operation" command and then gets a synchronous response at the end of the operation.
- The client issues a "do this operation" command and obtains an operation ID, after which it needs to determine whether the request was successfully completed.
- You would compare your API to RPC.
- Store large amounts of data directly.
- Regular operations performed on objects are not CRUD-style.
- APIs are not easy to model with objects.
Use kube-apiserver and etcd to achieve data synchronization and data persistence, and maintain the state of these resources through ks-controller-manager to achieve the consistency of the final state. If you are familiar with K8s, you can well understand the benefits of declarative API, which is also the core part of KubeSphere.
For example, the configuration of pipelines, user credentials, user entities, and alarm notifications in KubeSphere can be abstracted into resource entities. With the mature architecture and tool chain of K8s, it can be easily combined with K8s to reduce the coupling between components and reduce the complexity of the system.
The core architecture of ks-apiserver
ks-apiserver is the core back-end component of KubeSphere, responsible for the interaction of front-end and back-end data, proxy distribution of requests, authentication and authentication. The following figure is the core architecture of ks-apiserver:
The development of ks-apiserver uses the go-restful framework, which can add multiple filters to the request link to dynamically intercept requests and responses, and implement authentication, authentication, audit logic forwarding and reverse proxy functions, KubeSphere's API style Also try to learn the K8s mode as much as possible to facilitate the use of RBAC for permission control.
Decoupling with the help of CRD + controller can greatly simplify the integration with third-party tools and software.
The K8s community also provides a rich toolchain, and with the help of controller-runtime and kubebuiler , development scaffolding can be quickly built.
API Aggregation and Permission Control
API aggregation can be realized by extending ks-apiserver, and functions such as function expansion and aggregation query can be further realized. The following specifications need to be followed in the API development process to integrate with KubeSphere's tenant system and resource system.
- API Aggregation
- Access control
- CRD + controller
API Specification
# 通过 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}
Purpose of the canonical API:
- Better abstraction of resources, abstracting as Object is more suitable for declarative API
- Better management of API, version, grouping, layering, more convenient for API expansion
- Better integration with permission control, can easily obtain metadata from requests, apigroup, scope, version, verb
Access control
The core of KubeSphere permission control is RBAC role-based access control.
The key objects are: Role, User, RoleBinding.
Role defines the resources that a role can access.
Roles are divided according to resource levels. The roles at different levels of cluster role, workspace role, and namespace role define the resources that the role can access at the current level.
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 can bind roles to a subject (Subject). Principals can be groups, users or service accounts.
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 is an extension to the Kubernetes API, which can expand the K8s API through dynamic registration. Users can use kubectl to create and access objects within it, just like manipulating built-in resources.
The resource is abstracted through CRD, and the resource state is maintained by monitoring the resource change through the controller. The core of the controller is Reconcile, which, like his meaning, maintains the resource state through passive and timed triggering until it reaches the declared state.
Taking the User resource as an example, we can define the CRD of the following structure to abstract the 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
The corresponding API is:
# 创建
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 is responsible for writing these data into K8s and then synchronize them to each replica by the informer.
ks-controller-manager maintains the resource status by monitoring data changes. Taking creating a user as an example, after POST /apis/iam.kubesphere.io/v1alpha2/users
creating a user, the user controller will synchronize the user resource status.
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
}
Through the declarative API, the complex logic is placed in the controller for processing, which is convenient for decoupling. It can be easily integrated with other systems and services, such as:
/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
The corresponding permission control policy:
Define a role that can add, delete, modify, and query user resources.
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"]
Define a role that can create pipeline resources.
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"]
This article is published by OpenWrite , a multi-post blog platform !