KubeCube v1.1 版本发布

KubeCube 迎来了 v1.1 版本的发布,新增了 OAuth2 的 GitHub 登录支持、租户配额的算法优化、Warden 热插拔安装包的本地和远端拉取支持等新的特性,也修复了若干已知问题,详见 ChangeLog

v1.1 版本中最主要的特性是 Auth-Proxy 能力的支持,使得部署更加轻量,无需侵入修改 kube-apiserver 的配置。用户可以使用 RESTful、client-go、kubectl 等方式访问被 KubeCube 纳管的 K8s 集群,享受统一的认证能力。

使用 Auth-Webhook 的困境

Auth-Webhook

在 KubeCube v1.1 版本之前,KubeCube 使用 K8s 提供的 Auth-Webook 方式来拓展认证能力,该方式通过为 kube-apiserver 指定认证后端来达到认证拓展的目的,kube-apiserver 会使用认证 Webhook 返回的 UserInfo 去进行下一步的鉴权流程。

该方式虽然能够使用 K8s 原生的方式拓展认证能力,但是在实际使用中存在一定的不足。如 Issues#62 中所指出的,该方式需要修改 kube-apiserver 的启动参数,为其指定额外的认证 Webhook 后端,当我们的 K8s 集群是多 Master 节点的高可用集群时,需要修改每一个 Master 节点的 kube-apiserver 的配置,这在很多场景几乎是无法接受的。另外在一些云厂商的托管 K8s 场景下,往往只对用户提供工作节点,此时想修改 kube-apiserver 的配置是非常困难的。

Warden-Auth-Proxy

Auth-Proxy

对于 Auth-Webhook 面临的困境,我们设计了 Warden-Auth-Proxy 模块来解决问题。Warden-Auth-Proxy 即 K8s 集群的认证代理,它对外提供了类似 kubectl proxy 的代理能力。不同的是,它会解析 request 中的 Bearer Token 为 UserInfo,然后使用 K8s 的 impersonation 能力进行用户伪装。

值得一提的是,Auth-Proxy 模块之所以集成在作为 Cluster Agent 的 Warden 中,而不是集成在管控集群的 KubeCube 中,是因为在设计上希望做到两点:

  1. 对于各个集群的请求能够就近访问本集群的代理服务,而不是跨集群访问 KubeCube 中的代理服务。
  2. 即使当 KubeCube 发生 Crash 的时候,各个集群的认证鉴权能力依然能够保持正常。

Auth-Proxy 的原理并不复杂,但是在实现中,需要注意以下几个问题:

1. kubectl exec 命令代理协议问题

对于代理 kubectl exec 的场景,使用普通的 HTTP 代理并不可行,究其原因是因为通信协议不匹配。

不同于其他的 HTTP RESTful 请求,kubectl exec 命令实际是使用的 SPDY 协议,SPDY 协议是 google 开发的 TCP 会话层协议, SPDY 协议中将 HTTP 的 request/response 称为 Stream,并支持 TCP 的链接复用,同时多个 stream之间通过 stream-id 来进行标记,简单来说就是支持在单个链接同时进行多个请求响应的处理,并且互不影响 。

在代理 kubectl exec 请求时,需要 Upgrade HTTP 协议,即通过 101(switching protocal) 状态码切换至 SPDY 协议来继续与下游服务通信。

func (h *UpgradeAwareHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  // 尝试协议 upgrade
  if h.tryUpgrade(w, req) {
		return
	}
	if h.UpgradeRequired {
		h.Responder.Error(w, req, errors.NewBadRequest("Upgrade request required"))
		return
	}

  ...

  // 构建 golang 经典的 ReverseProxy
	proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: h.Location.Scheme, Host: h.Location.Host})
	proxy.Transport = h.Transport
	proxy.FlushInterval = h.FlushInterval
	proxy.ErrorLog = log.New(noSuppressPanicError{}, "", log.LstdFlags)
	if h.Responder != nil {
		proxy.ErrorHandler = h.Responder.Error
	}
	proxy.ServeHTTP(w, newReq)
}

2. kubeconfig 配置问题

对于使用 kubeconfig 与集群通信的场景,默认的 kubeconfig 中的 cluster server 的地址往往直接指向 kube-apiserver,这使得用户与集群的通信没有经过 Warden-Auth-Proxy 代理。

因此,对于上述场景,我们需要在 kubeconfig 上做文章。KubeCube 提供下载 kubeconfig 的能力,使用当前 user 下载的 kubeconfig,包含了 user 的访问凭证,包含了该 user 所能访问的所有 cluster 的 context,user 可以自行切换 context 来对 KubeCube 纳管的集群进行访问。KubeCube 通过改写 kubeconfig 中的 kube-apiserver 地址为 Warden-Auth-Proxy 地址来使得用户通过该 kubeconfig 执行的 kubectl 或 client-go 请求会被 Warden-Auth-Proxy 所代理。

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: {member_1_cluster_ca_data}
    server: {member_1_warden_auth_proxy_addr}
  name: member-1
- cluster:
    certificate-authority-data: {pivot_cluster_ca_data}
    server: {pivot_warden_auth_proxy_addr}
  name: pivot-cluster
contexts:
- context:
    cluster: member-1
    user: member-1-admin
  name: member-1-admin@member-1
- context:
    cluster: pivot-cluster
    user: pivot-cluster-admin
  name: pivot-cluster-admin@pivot-cluster
current-context: member-1-admin@member-1
kind: Config
users:
- name: member-1-admin
  user:
    token: {user_token}
- name: pivot-cluster-admin
  user:
    token: {user_token}

3. security 通信安全问题

Auth-Proxy-security

在对通信安全有较高要求的场景下,我们需要保证从 Client——>Warden-Auth-Proxy——>kube-apiserver 的通信链路是加密的,可靠的。我们使用以下方式加以保证:

  • KubeCube 使用 Bearer Token 作为用户访问凭证,Invalid Bearer Token 会被 Warden-Auth-Proxy 拒绝,相当于服务端要求验证客户端身份。
  • 应使用 TLS 对 Warden-Auth-Proxy 进行服务端身份校验,需要相应的 CA 证书,该 TLS 能力在规划中,还未支持。当前使用 insecure-skip-tls-verify 跳过,在后续版本中,会增加对 TLS 能力的支持。
  • Warden-Auth-Proxy 与 kube-apiserver 之间,通过 mTLS 进行双向加密通信,Warden-Auth-Proxy 持有 K8s 集群的 admin 证书,并以 admin 身份伪装成目标 user 与 kube-apiserver 进行通信。

写在最后

未来我们会持续提供更多功能,帮助企业简化容器化落地。也欢迎大家参与贡献,提出宝贵的建议。添加以下微信进入 KubeCube 交流群。

kubecube微信

作者简介: 蔡鑫涛,网易数帆轻舟容器平台资深开发,KubeCube Committer

深入解读 KubeCube 多集群管理

为什么需要多集群?

  • 生产级落地,需要经过多个环境验证,单个集群往往不能满足隔离需求
  • 当业务规模超过了单集群的承载能力时
  • 当企业考虑使用多云或者混合云架构时
  • 当架构设计上考虑云容灾,异地多活等场景时

k8s 多集群管理现状

多集群管理并不是 kubernetes 社区的一个主要目标,虽然社区提出了 mcs 的多集群 service 标准,但是这依然无法满足企业想要管理多集群的需求。如果仅仅使用 k8s 原生的能力进行多集群建设,k8s 管理者往往需要管理大量的不同 k8s 集群的配置文件,需要管理不同用户的认证以及权限信息,并且对于应用的多集群发布、运维需要大量的人工的介入,很容易出现配置管理混乱、因操作不当导致故障等问题

KubeCube 多集群能力

KubeCube 可以接管任意标准 Kubernetes 集群,对接管的所有 Kubernetes 集群提供统一的用户管理和基于 Kubernetes 原生 RBAC 扩展的访问控制。为提升用户管理多个Kubernetes集群的效率,KubeCube提供了在线运维工具,可以通过KubeCube这一统一入口,快速管理多集群资源:CloudShell 可以在线对各集群使用kubectl,WebConsole 可以在线访问各集群中的Pod

另外,考虑到混合云场景下 KubeCube 管控集群与业务集群间的网络抖动、异常等问题。我们提供了业务集群自治能力,当业务集群与KubeCube管控集群失联时,业务集群的访问控制等可正常生效,不会受到影响

KubeCube 的多集群模型

kubecube多集群模型

KubeCube 基于多集群模型,实现了多集群管理能力多集群统一认证和鉴权的能力多集群多租户管理能力以及多集群的容错能力,了解多集群模型能够帮助你更加深入地了解 KubeCube。下文会对 KubeCube 和 Warden 中的各个多集群模块的设计进行探索。

KubeCube 中的多集群模块

KubeCube 是管控集群上的核心组件,它实现了多集群的生命周期管理,同时也作为 UI/openAPI 的操作入口。了解 KubeCube 中的多集群模块,我们需要关注以下几个 topic

  1. cluster cr 与 InternalCluster 的关系
  2. multi-cluster-manager 如何管理 InternalCluster
  3. cluster reconcile 的流程
  4. scout 如何侦查计算集群的心跳

Multi-Cluster-Manager – 多集群管理器

MultiClusterMgr

多集群管理器本质上就是对InternalCluster的管理,接口中包含了操作InternalCluster的所需方法

// MultiClustersManager access to internal cluster
type MultiClustersManager interface {
	// Add runtime cache in memory
	Add(cluster string, internalCluster *InternalCluster) error
	Get(cluster string) (*InternalCluster, error)
	Del(cluster string) error

	// FuzzyCopy return fuzzy cluster of raw
	FuzzyCopy() map[string]*FuzzyCluster

	// ScoutFor scout heartbeat for warden
	ScoutFor(ctx context.Context, cluster string) error

	// GetClient get client for cluster
	GetClient(cluster string) (kubernetes.Client, error)
}

InternalCluster 是真实Clustercr 的运行时映射,Clustercr 代表了被KubeCube纳管的集群。

InternalCluster 包含四个字段:

  • Client 包含了沟通指定集群所需的所有 k8s client,包括sig/client-go中的clientsetsig/controller-manager中的client.Clientcache.Cache,以及k8s.io/metric中的clientset,通过 Client,我们可以以各种姿势沟通不同 k8s 集群的 k8s-apiserver,这也是实现多集群的基础
type Client interface {
	Cache() cache.Cache
	Direct() client.Client
	Metrics() versioned.Interface
	ClientSet() kubernetes.Interface
}
  • Scout 为探测指定集群健康状况的侦察员,在下文会详细阐述
  • Config 为沟通指定集群所需的rest.Config,由 Clustercr 中的 KubeConfig 转化而来
  • StopCh 是用来关闭 Scout对指定集群的侦查行为,以及停止与该集群相关的所有informer行为

Scout – 计算集群侦查员

scout处理

scout 的职责是侦查指定集群的健康状况,它对外提供 http 接口来接收来自不同集群的心跳,随后将心跳包发送到对应的 scout 的 Receiver channel 中,对内则是起了一个 goroutine 循环接收来自 Receiver channel 的心跳包,并且根据健康和超时两种情况做不同的 callback 处理。

// Collect will scout a specified warden of cluster
func (s *Scout) Collect(ctx context.Context) {
	for {
		select {
		case info := <-s.Receiver:
			s.healthWarden(ctx, info)

		case <-time.Tick(time.Duration(s.WaitTimeoutSeconds) * time.Second):
			s.illWarden(ctx)

		case <-ctx.Done():
			clog.Warn("scout of %v warden stopped: %v", s.Cluster, ctx.Err())
			return
		}
	}
}

Cluster-Controller – 集群 cr 控制器

cluster-reconcile

Cluster 的 controller 主要负责感知 KubeCube 纳管的集群变更,并且初始化计算集群的相关设置

  1. watch 到新的 cluster cr 的 create 事件
  2. 尝试使用 cluster cr 中的元信息去沟通对应 k8s 集群的 apiserver
  3. 如果与对应的 k8s 集群建联失败,则会使该事件进入 retry 队列(详见下文的多集群容错)
  4. 与 k8s 集群建联成功后会创建对应的InternalCluster,并将其添加到MultiClustersManager管理的缓存中
  5. 向对应的集群下方 warden 的 deployment 以及一些必要的 crd 资源
  6. 创建该集群对应的 scout,并开启对该集群的侦查

Warden 中的多集群模块

Warden 是作为一种 cluster agent 的身份存在于每个计算集群中,它提供了计算集群与管控集群之间的资源同步能力、热插拔能力、统一鉴权的能力、资源配置管理能力、心跳上报能力等等。了解 warden 中的多集群模块,我们需要关注以下几个 topic

  1. warden 如何向管控集群上报心跳信息
  2. warden 如何从管控集群同步资源

Reporter – 集群信息上报者

Warden 的 reporter 上报者是与 KubeCube 的 scout 侦察员一一对应,遥相呼应的

Warden 需要会在启动时根据已注册的健康检查方法去检查各个模块的健康状态,等到它所依赖的各个模块都达到健康状态后,才会开始向管控集群上报心跳,如果健康检查超时,warden 将会启动失败

// waitForReady wait all components of warden ready
func (r *Reporter) waitForReady() error {
	counts := len(checkFuncs)
	if counts < 1 {
		return fmt.Errorf("less 1 components to check ready")
	}

	// wait all components ready in specified time
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(r.WaitSecond)*time.Second)
	defer cancel()

	readyzCh := make(chan struct{})

	for _, fn := range checkFuncs {
		go readyzCheck(ctx, readyzCh, fn)
	}

	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-readyzCh:
			counts--
			if counts < 1 {
				return nil
			}
		}
	}
}

Warden 为它自身所有的模块提供了健康检查入口,所有的会影响 warden 正常运行的模块都需要通过RegisterCheckFunc方法注册健康检查函数以确保 warden 的正常启动

var checkFuncs []checkFunc

// RegisterCheckFunc should be used to register readyz check func
func RegisterCheckFunc(fn checkFunc) {
	checkFuncs = append(checkFuncs, fn)
}

func readyzCheck(ctx context.Context, ch chan struct{}, checkFn checkFunc) {
	for {
		select {
		case <-time.Tick(waitPeriod):
			if checkFn() {
				ch <- struct{}{}
				return
			}
		case <-ctx.Done():
			return
		}
	}
}

完成各个模块的健康检查后,warden 启动就绪,开始向管控集群上报包含集群信息的心跳,并且根据与管控集群的通信情况来触发对应的函数回掉。KubeCube 中的对应的 scout 会根据收到的上报信息做相应的处理。

// report do real report loop
func (r *Reporter) report(stop <-chan struct{}) {
	for {
		select {
		case <-time.Tick(time.Duration(r.PeriodSecond) * time.Second):
			if !r.do() {
				r.illPivotCluster()
			} else {
				r.healPivotCluster()
			}
		case <-stop:
			return
		}
	}
}

Sync-Manager – 集群资源同步器

Sync-Manager 实现了从管控集群同步资源到计算集群的能力,这也是实现多集群统一认证和鉴权能力的基础

kubecube资源同步

多集群容错

不可否认的是,多 k8s 集群在现实情况中可能会出现跨集群通信故障,有时候是因为集群与集群之间的网络故障,有时候是某一集群的 k8s 故障,总之会造成跨集群访问不可用。就 KubeCube 而言,目前主要关心两种情况:

  1. KubeCube 运行时,member cluster 失联

  2. KubeCube 启动时,member cluster 失联

针对上述的两种情况,KubeCube 分别在 cluster-controller、scout 以及 kubecube-apiserver 的 middleware 中做出了相应的处理

Cluster-Controller

上文我们提到了 cluster-controller 的 reconcile 逻辑,已知当我们在初始化纳管一个集群时,我们会将通信失败的 cluster 放进 retry 队列中,并且将 cluster cr 的状态 update 为 initFailed

		// generate internal cluster for current cluster and add
		// it to the cache of multi cluster manager
		skip, err := multiclustermgr.AddInternalCluster(currentCluster)
		if err != nil {
			log.Error(err.Error())
		}

		if err != nil && !skip {
			updateFn := func(cluster *clusterv1.Cluster) {
				initFailedState := clusterv1.ClusterInitFailed
				reason := fmt.Sprintf("cluster %s init failed", cluster.Name)
				cluster.Status.State = &initFailedState
				cluster.Status.Reason = reason
			}

			err := utils.UpdateStatus(ctx, r.Client, &currentCluster, updateFn)
			if err != nil {
				log.Error("update cluster %v status failed", currentCluster.Name)
				return ctrl.Result{}, err
			}

			r.enqueue(currentCluster)

			return ctrl.Result{}, nil
		}

初始化失败的集群会在一个单独的 goroutine 中进行定时重试重联操作,并将重连成功的 cluster 通过 k8s 的 GenericEvent 重新做 reconcile,同时会在重试队列中删除该重试任务。重试超时默认为 12h,重试间隔为 7s

// try to reconnect with cluster api server, requeue if every is ok
	go func() {
		log.Info("cluster %v init failed, keep retry background", cluster.Name)

		// pop from retry queue when reconnected or context exceed or context canceled
		defer r.retryQueue.Delete(cluster.Name)

		for {
			select {
			case <-time.Tick(retryInterval):
				_, err := client.New(config, client.Options{Scheme: r.Scheme})
				if err == nil {
					log.Info("enqueuing cluster %v for reconciliation", cluster.Name)
					r.Affected <- event.GenericEvent{Object: &cluster}
					return
				}
			case <-ctx.Done():
				log.Info("cluster %v retry task stopped: %v", cluster.Name, ctx.Err())

				// retrying timeout need update status
				// todo(weilaaa): to allow user reconnect cluster manually
				if ctx.Err().Error() == "context deadline exceeded" {
					updateFn := func(cluster *clusterv1.Cluster) {
						state := clusterv1.ClusterReconnectedFailed
						reason := fmt.Sprintf("cluster %s reconnect timeout: %v", cluster.Name, retryTimeout)
						cluster.Status.State = &state
						cluster.Status.Reason = reason
					}
					err := utils.UpdateStatus(ctx, r.Client, &cluster, updateFn)
					if err != nil {
						log.Warn("update cluster %v status failed: %v", cluster.Name, err)
					}
				}

				return
			}
		}
	}()

当然,当用户主动删除该 cluster cr 时,controller 会调用 context 的 cancel 方法停止该 cluster 的重试任务,并将其从重试队列中删除。未来会支持用户手动触发重试重连的能力

	// stop retry task if cluster in retry queue
	cancel, ok := r.retryQueue.Load(cluster.Name)
	if ok {
		cancel.(context.CancelFunc)()
		clog.Debug("stop retry task of cluster %v success", cluster.Name)
		return nil
	}

Scout

Scout 作为计算集群的侦察员,当它感知到 member cluster 失联时,它会更新对应的 cluster cr 的 status 为 clusterAbnormal,并且告知 multiClusterManger 该集群的异常状态

	if !isDisconnected(cluster, s.WaitTimeoutSeconds) {
		// going here means cluster heartbeat is normal

		if s.ClusterState != v1.ClusterNormal {
			clog.Info("cluster %v connected", cluster.Name)
		}

		s.LastHeartbeat = cluster.Status.LastHeartbeat.Time
		s.ClusterState = v1.ClusterNormal
		return
	}

	if s.ClusterState == v1.ClusterNormal {
		reason := fmt.Sprintf("cluster %s disconnected", s.Cluster)

		updateFn := func(obj *v1.Cluster) {
			state := v1.ClusterAbnormal
			obj.Status.State = &state
			obj.Status.Reason = reason
			obj.Status.LastHeartbeat = &metav1.Time{Time: s.LastHeartbeat}
		}

		clog.Warn("%v, last heartbeat: %v", reason, s.LastHeartbeat)

		err := utils.UpdateStatus(ctx, s.Client, cluster, updateFn)
		if err != nil {
			clog.Error(err.Error())
		}
	}

	s.ClusterState = v1.ClusterAbnormal

当 Scout 感知到 member cluster 重新上报心跳,恢复连接时,它会更新对应 cluster cr 的 status 为 normal,并告知 multiClusterManger 该集群恢复正常

  if s.ClusterState != v1.ClusterNormal {
		clog.Info("cluster %v connected", cluster.Name)
	}

	s.LastHeartbeat = time.Now()

	updateFn := func(obj *v1.Cluster) {
		state := v1.ClusterNormal
		obj.Status.State = &state
		obj.Status.Reason = fmt.Sprintf("receive heartbeat from cluster %s", s.Cluster)
		obj.Status.LastHeartbeat = &metav1.Time{Time: s.LastHeartbeat}
	}

	err = utils.UpdateStatus(ctx, s.Client, cluster, updateFn)
	if err != nil {
		clog.Error(err.Error())
		return
	}

	s.ClusterState = v1.ClusterNormal

KubeCube-Apiserver-Middlewares

作为一个 http server 的预处理函数,它提供了集群状态预检的能力。当你想要通过 KubeCube 访问某一集群的资源时,它会向 multiClusterManager 询问该集群的状态,如果对应集群不健康,它会做快速失败

// PreCheck do cluster health check, early return
// if cluster if unhealthy
func PreCheck() gin.HandlerFunc {
	return func(c *gin.Context) {
		cluster := fetchCluster(c)

		clog.Debug("request path: %v, request cluster: %v", c.FullPath(), cluster)

		if len(cluster) > 0 {
			_, err := multicluster.Interface().Get(cluster)
			if err != nil {
				clog.Warn("cluster %v unhealthy, err: %v", cluster, err.Error())
				response.FailReturn(c, errcode.CustomReturn(http.StatusInternalServerError, "cluster %v unhealthy", cluster))
				return
			}
		}
	}
}

多集群统一的认证和鉴权能力

KubeCube 的认证和鉴权能力是基于 k8s 原生的 rbac 之上的,不同的是 KubeCube 在此之上做了多集群统一认证和鉴权能力的拓展。

kubecub 统一鉴权

权限规则同步

要做到同一个用户在不同集群中有同样的认证和鉴权结果,即需要保证不同集群间的权限规则相同

  1. 新建用户时,KubeCube 会为其创建对应的 user cr,也就是 roleBinding 中的 subject 主体
  2. 集群管理员为新用户分配角色时,KubeCube 会为该 user 创建对应的 rbac 规则
  3. Warden 的资源同步管理器会将 user cr 以及 rbac 规则从管控集群同步到计算集群

用户访问

KubeCube 支持灵活的用户访问方式,包括通过 KubeCube 的前端访问 k8s,通过 kubectl 访问 k8s 以及直接使用 rest-ful 的方式访问 k8s,本质都是使用 user 对应的 token 去访问 k8s

  1. KubeCube 会为每一个 user 生成复合 jwt 标准的 token,用户可以获取由此生成的 KubeConfig,前端凭借此 token 与 KubeCube 交互

  2. 携带 token 去请求 k8s-apiserver 时,k8s-apiserver 会根据我们事先在它的启动参数中配置的authentication-token-webhook-config-file: "/etc/cube/warden/webhook.config"参数去访问 warden 的 authR webhook api,并获取认证结果,比如持有 vela 的 token:xxxx,去访问 warden 的认证服务后,得到 vela 这个 user

  3. 然后 k8s-apiserver 通过 vela user,以及与之匹配的 rbac 规则作为鉴权

    从架构上看,即使 KubeCube 遭遇故障,只有 warden 正常运行,用户依然可以通过 kubectl 和 rest-ful 的方式,通过统一的 k8s 认证和鉴权去访问对应的 k8s 资源

总结

KubeCube 的多集群模型实现依靠的是 KubeCube 和 Warden 的相辅相成,在使用上提供了多集群统一的认证、鉴权以及多租户管理能力,在故障处理上提供了多集群容错以及单集群自治的能力。

写在最后

未来我们会持续提供更多功能,帮助企业简化容器化落地。也欢迎大家的宝贵建议,添加以下微信进入KubeCube交流群。

kubecube微信

作者简介: 蔡鑫涛,网易数帆轻舟容器平台开发,KubeCube Committer

KubeCube 多级租户模型

KubeCube 多级租户模型

KubeCube (https://kubecube.io) 是由网易数帆近期开源的一个轻量化的企业级容器平台,为企业提供 kubernetes 资源可视化管理以及统一的多集群多租户管理功能。KubeCube 社区将通过系列技术文章解读 KubeCube 的设计特点和技术实现,帮助开发者和用户更快地理解和上手 KubeCube。本文是第二篇,深度解读 KubeCube 的多级租户模型设计。

img

背景

在我们跟企业交流时,发现不同企业虽然规模不一样,但选择进⾏容器化的初衷还是为了降本增效、很多企业会选择多个部⻔共⽤ K8s 集群或者物理资源,在共享资源的同时,希望有⾜够的隔离性。

多租户是一种软件架构技术,可以实现多个租户之间资源复用和共享基础设施,方便运营管理,有效节省开发应用成本;同时又可以实现个性化定制,每个租户的数据是隔离的。

当前大部分云供应商都提供了多租户的解决方案来实现 K8s 资源共享和隔离,以满足企业不同组织架构共享一个 K8s 基础设施的需求。我们将容器服务在以往企业落地实施过程中的经验进行了总结,去数据库化采用更轻量更原生的 CRD + Operator 机制,在传统多租户模型基础上加入了项目层级与软件管理过程相对应,形成了新的多级租户模型,适配企业组织架构和软件资源管理的规范,使得企业可以更好的建立统一的多 K8s 集群管理平台。

租户模型介绍

KubeCube 的多级租户模型通过租户和项目实现权限隔离和资源分配。一个租户表示一个组织(部门、团队),做资源隔离。一个项目通常可以表示一个完整业务应用系统,与企业的软件项目管理过程相对应,可以根据业务系统功能分解拆分多个命名空间管理应用子系统。

租户和项目都是跨集群的概念,所有租户共享多套 K8s 集群基础设施,通过权限限定和配额管理保证必要的隔离,防止恶意操作带来的风险。

多级租户模型设计

KubeCube 多级租户模型提供租户、项目、空间 3 层模型以满足不同规模企业的组织架构层级,从架构上看是一种层级树形结构,一个租户包含多个项目,一个项目包含多个命名空间,项目包含的命名空间可以位于不同的 K8s 集群。这里的命名空间指的是 K8s 的Namespace,用于实际承接业务应用的部署,是管理的最小单元。

image-20210916173931577

租户和项目在实现上是一个 CRD ,用户只需要在管控 K8s 集群上创建租户和项目的 CR,KubeCube会将租户和项目的 CR 实时同步到所有的计算 K8s 集群。运维人员可以集中式的管理所有的计算 K8s 集群,新增集群时会自动同步租户项目等基础信息,项目管理员只需要在任一 K8s 集群(包括管控和计算集群)创建命名空间即可。

image-20210916174102135

租户、项目和命名空间三者之间的关联关系是通过层级命名空间实现的,每一个租户都关联一个Namespace,每一个项目也都关联一个Namespace,通过租户和项目的Manifest里.spec.namespace字段指定关联的Namespace名称。租户和项目关联的命名空间与实际承载应用的命名空间不同,它是为了解决管理员仅可以在拥有权限的租户和项目下面创建命名空间而引入的一个特殊命名空间。

为了避免供应商锁定和更好的兼容原生 K8s 能力,KubeCube 的权限模型是基于 K8s 原生的 RBAC 能力实现的,我们期望项目管理员仅可以在他拥有权限的项目下面创建命名空间。假设授权给一个项目管理员ClusterRole定义赋予创建Namespace的权限,由于Namespace是集群级别资源,那么他将拥有超出项目范围任意创建命名空间的权限,这与我们的期望不符合。

这里我们引入 HNC (The Hierarchical Namespace Controller)的SubNamespace的概念,它是命名空间级别的资源,负责自动生成和控制Namespace的生命周期。在 KubeCube 的设计中,租户和项目管理员都没有直接创建命名空间的权限,他们通过拥有创建SubNamespace的权限来间接获得创建命名空间权利。SubNamespace是命名空间级别的资源,通过 RBAC 限制SubNamespace操作权限,租户管理员只能在自己租户关联的Namespace下创建SubNamespace,项目管理员只能在自己项目关联的Namespace下创建SubNamespace,再由 HNC 控制器组件根据SubNamespace自动创建Namespace,最终实现管理员仅可以在拥有权限的租户和项目下面创建命名空间的权限。

image-20210916185015532

实际使用中,用户创建租户和项目的 CR 时,KubeCube 程序会自动监听并创建相应的SubeNamespace,再由 HNC 控制器监听并创建Namespace,继而将租户和项目与命名空间关联起来。

KubeCube 租户模型采用多层级命名空间的设计除了考虑权限限定能够兼容原生 K8s 的 RBAC 外,还额外考虑到一个因素是可以放置租户级的公共配置和项目级的公共配置,如针对整个项目的统一监控配置。在必要的时候,还可以指定 HNC 控制器将父级命名空间的资源复制传递到子命名空间,如用户权限绑定RoleBinding配置。

租户项目权限设计

KubeCube 多级租户模型中预设了四种角色,它们的权限由大到小分别是:

  • 平台管理员:拥有最高权限,负责管理 K8s 集群,创建租户,设定角色权限和租户配额。
  • 租户管理员:拥有某个租户的所有权限,主要负责租户下的项目管理。
  • 项目管理员:负责在 K8s 集群上创建命名空间,部署应用,配置监控。
  • 项目观察员:仅拥有项目下命名空间和资源的查询权限,可以查看应用日志和监控。

在实现上,四种角色是四个ClusterRole定义,使用CluaterRoleBinding可以给用户授予平台管理员权限,使用RoleBinding可以给用户授予受限的租户管理员、项目管理员和项目观察员权限。在层级命名空间结构中,授予一个用户租户管理员权限相当于在租户关联的命名空间及它所有下级命名空间下创建RoleBinding,同理授予一个用户项目管理员和项目观察员权限相当于在项目关联的命名空间及它所有下级命名空间下创建RoleBinding

image-20210906190824488

HNC 控制器组件在创建Namespace的时候,可以指定把SubNamespace所在的父命名空间的所有 RoleBinding信息往下复制传递。因此给用户授予租户管理员权限时只需要在指定租户关联的命名空间下创建RoleBinding,授权项目管理员和项目观察员权限时只需要在指定项目关联的命名空间下创建RoleBinding,权限绑定关系会随着命名空间的创建逐级复制下发,最终在命名空间下会拥有不同人不同角色的RoleBinding信息。

资源配额管理设计

KubeCube 的配额管理主要是针对多租户共享的 K8s 基础设施集群的资源分配,平台管理员可以为每一个租户划分每一个 K8s 集群的资源使用额度,包括 CPU、内存、磁盘和GPU的配额大小。租户管理员可以继续给项目划分配额,项目管理员可以给每一个承载应用系统的命名空间划分配额。集群信息Cluster (CRD)里记录着整个集群的可用配额信息,租户和项目的配额信息和已分配信息存储在CubeResourceQuta(CRD)里,命名空间的配额信息使用 K8s 原生ResourceQuota

image-20210916185102212

实际使用的时候,项目配额可以省略,如 KubeCube 默认集成的管理平台,平台管理员只需要给每一个租户划分每一个 K8s 集群的可用额度,项目管理员在每一个 K8s 集群上创建命名空间的时候都不能分配超出所属租户的资源额度。

总结

KubeCube 多级租户模型突破传统的容器服务多租户模式,采用租户、项目和空间的三级结构,与企业组织架构和软件管理适配,实现更细粒度的资源配额管理,满足企业统一容器平台的构建需求。以多层级命名空间为基础,租户项目权限隔离兼容原生 RBAC,使得 KubeCube 多级租户模型可以更好的兼容原生 K8s 集群,完全能够在已有 K8s 集群上进行原地升级安装 KubeCube。

作者:傅思达

更多内容请访问:https://www.kubecube.io

KubeCube技术交流群:

kubecube微信

KubeCube设计实践,初学者玩好Kubernetes的正确姿势

8月30日,KubeCube开源项目负责人祝剑锋为大家进行了一次线上分享,结合开源项目KubeCube的设计实践,梳理Kubernetes落地面临的实际问题,逐一给出如何破解Kubernetes落地难题的思路,并介绍具体的架构实现。

点击观看 视频回放

点击下载 分享PPT

未来我们会持续提供更多功能,帮助企业简化容器化落地。也欢迎大家的宝贵建议,添加以下微信进入KubeCube交流群。

kubecube微信

KubeCube开源:魔方六面,降阶Kubernetes落地应用

容器技术发展至今,各行各业对其所带来的好处,如多环境交付一致性、弹性伸缩、故障自愈等,已经达成普遍共识。这些好处的实现,依赖于当前容器编排领域的事实标准——Kubernetes平台。然而,Kubernetes的复杂性、学习曲线陡峭也是不争的事实,这对容器技术落地应用造成很大影响。

根据IDC最新发布的软件定义计算软件市场半年跟踪报告显示,容器软件市场在未来五年仍然会保持超过40%的复合增长率,但 2020 年容器基础架构软件占整体软件定义计算市场的比例仅为16.2%。容器在互联网、金融、AI 等领域已经规模落地,大批头部企业已经基于容器构建新一代企业基础设施平台,但在多数传统企业、中小型企业落地率并不高。

中国软件定义计算软件市场预测

这其中的原因,很大程度上是因为企业在落地容器技术时所面临的各种问题,导致落地成本较高,比如:

  • Kubernetes学习曲线陡峭,配置复杂度高:Kubernetes是一个强大的容器编排系统,但不可否认它也是一个很复杂的分布式系统,其学习门槛高,学习曲线较长,企业需要具备较丰富的经验才能很好的使用和维护Kubernetes集群。这就需要企业付出不小的人力成本及时间成本,对很多中小型企业来说,这个成本是不容小觑的。
  • 单Kubernetes集群无法满足企业需求,多集群管理效率低:我们接触到的不少客户在生产级容器化落地时,发现单个Kubernetes集群根本无法满足需求,典型的场景是需要开发、测试、演练、预发、生产等多种环境,线下环境需要与线上环境进行隔离,这就需要使用多个Kubernetes集群,独立操作多个Kubernetes集群的效率问题就体现出来了。
  • 不能较小代价的获得企业落地所需的特性:企业选择Kubernetes,目标还是想利用Kubernetes实现降本、增效,因此多个部门或者同部门下多项目组共享资源是很常见的场景,但还需要不同项目保持必要的隔离性,保证租户之间公平地分配共享集群资源。并且Kubernetes专注于单集群单租户容器编排能力,虽然社区有相关的项目,但在生产级落地使用还是有较高的门槛。
  • 监控、告警、日志等可观测方面需要建设:社区主流的监控方案是Prometheus、告警是AlertManager、日志方案较多,但使用时配置较复杂,维护难度也较高,这就提升了对运维、研发的要求,势必会影响业务研发的效率。
  • 国产化支持:近几年国际环境的变化,让我们更进一步认识到了自主可控的重要性,企业底层环境越来越多采用国产处理器、国产操作系统,而容器化涉及的系统,并不是全部支持国产“芯”,这也成为一个影响容器化落地的因素。

KubeCube开源

kubecube logo

为了帮助企业加快容器化落地进程,网易数帆将沉淀多年的容器平台KubeCube开源,希望为新基建做出一份贡献,同时希望以此促进国内相关领域的创新,打造国内开放、安全、自主可控的云原生底座,关键时刻,不会被人“卡脖子”。

KubeCube (https://kubecube.io) 是一个轻量化的企业级容器平台,为企业提供kubernetes资源可视化管理以及统一的多集群多租户管理功能,具有简化应用部署、管理应用的生命周期和丰富的监控和日志审计能力。Cube有魔方之意,寓意通过KubeCube的能力组合,企业可以快速构建一个强大和功能丰富的云原生底座,并增强 DevOps 团队的能力。下面我们具体来看KubeCube这个魔方的六面,都提供了哪些能力。

一键部署

KubeCube针对用户的使用场景提供了多种部署方式:适用于POC环境的All In One部署,适用于生产环境的多节点高可用部署。仅需要一条命令即可完成 Kubernetes+KubeCube 的部署,同时提供了开箱即用的多集群管理、多租户、可观测功能。

同时考虑到企业可能已有部分能力建设,如日志平台等,KubeCube可以只部署核心服务,提供多集群多租户能力,可观测等组件可以通过热插拔的方式开启或关闭,同时通过热插拔配置完成用户已有系统对接,用户可以根据实际场景灵活选择。

通过提供Kubernetes资源可视化管理,降低用户的学习曲线,除扩展了必要的企业特性如多租户等能力,其他贴近原生,使用户的学习路线没有断层。

dashboard

多Kubernetes集群统一管理

KubeCube可以接管任意标准Kubernetes集群,对接管的所有Kubernetes集群提供统一的用户管理和基于Kubernetes原生RBAC扩展的访问控制。为提升用户管理多个Kubernetes集群的效率,KubeCube提供了在线运维工具,可以通过KubeCube这一统一入口,快速管理多集群资源:CloudShell可以在线对各集群使用kubectl,WebConsole可以在线访问各集群中的Pod。

另外,考虑到混合云场景下KubeCube管控集群与业务集群间的网络抖动、异常等问题。我们提供了业务集群自治能力,当业务集群与KubeCube管控集群失联时,业务集群的访问控制等可正常生效,不会受到影响。

多租户隔离

在我们跟企业交流时,发现不同企业虽然规模不一样,但选择进行容器化的初衷还是为了降本增效、很多企业会选择多个部门共用Kubernetes集群或者物理资源,在共享资源的同时,希望有足够的隔离性。

因此KubeCube基于HNC进行了部分扩展,提供租户、项目、空间3层模型,以满足不同规模企业的组织架构层级,并以此提供资源可见性隔离、配额控制等。使企业不同部门通过共享降低成本的同时,保证必要的隔离,防止恶意操作带来的风险。

完全兼容原生 Kubernetes API

KubeCube除能够通过UI管理Kubernetes资源外,还提供了OpenAPI以及Kubernetes API访问(可以使用kubectlclient-go直接访问集群),所有访问方式均通过统一的身份认证及权限访问控制。通过OpenAPI可以方便的与企业已有系统进行集成,如果企业已有部分能力建设,如使用kubectl的运维脚本等,都可以无缝迁移。

开箱即用的可观测功能

管控组件监控

提供日志、监控、告警功能,提升问题定位及运维效率,可视化配置,告别复杂的配置规则。

提供多维度基础指标监控,覆盖集群、物理节点、工作负载等多种维度,提供CPU、内存、磁盘、网络、GPU等常用指标,满足日常运维需求,帮助用户快速发现、定位问题。

基于自研的日志配置分发服务,动态感知Pod变化,使日志采集对业务无侵入,同时可减少资源占用,降低成本。

ARM及国产化支持

KubeCube支持AMD及ARM架构,同时支持目前主流的国产处理器及操作系统,如飞腾处理器、麒麟操作系统等。

一图看懂KubeCube

以上是KubeCube的六大特性介绍。我们在下图中更全面地总结了KubeCube的核心信息,可以帮助大家更好地了解KubeCube的能力和用途。

KubeCube长图

写在最后

未来我们会持续提供更多功能,帮助企业简化容器化落地。也欢迎大家的宝贵建议,添加以下微信进入KubeCube交流群。

kubecube微信

作者简介: 祝剑锋,网易数帆轻舟容器平台负责人,KubeCube社区核心维护者,主导KubeCube容器平台的开源工作,负责网易数帆轻舟容器平台集团内大规模落地及产品化建设。具有六年Kubernetes及容器平台相关研发及大规模实践经验。