多租户可视化 K8s 管理平台 KubeCube近日迎来了新版本的发布,新版本增加了 K8s 版本转化、HNC GA 版本适配、审计信息国际化、warden 主动上报模式,为集群和项目设置 Ingress 域名后缀等特性,也修复了若干已知问题,详见 ChangeLog

该版本中最主要的特性是 Version-Conversion 能力的支持,使得接入 KubeCube 的用户无需感知被 KubeCube 接管的 K8s 集群版本,可以使用指定版本的 K8s API 来操作 K8s 资源,KubeCube 会做自适应转化;同时 KubeCube 也将这个能力包装成 SDK 供外部使用。

图片

为什么需要多 K8s 版本转化?

在实际的生产场景中,用户的 K8s 集群往往固置于某一稳定版本,并随着时间的推移,在该 K8s 集群中沉淀了大量的业务、工具、方案等,同时 K8s 社区又会不断的推出更高的版本,此时升级 K8s 版本往往需要比较高的代价。

K8s 的版本升级,并不总是保证 API 的完美兼容,绝大多数的 API 会经历从 Development level --> Alpha level --> Beta level --> Stable level 的发展阶段,理想情况下,用户应该使用 Stable level 的 API 用于生产环境,但是现实中,用户所使用的某一资源的 API 很可能处于 Stable level 以下的阶段,比如 extensions/v1beta1 的 Deployment 和 apps/v1 的 Deployment。详见 K8S API 变动规划

当用户需要在控制面纳管多 K8s 集群时,用户暂时不希望升级老的稳定的 K8s 集群,又希望新增的 K8s 集群是比较高的版本,这时,管控面的 KubeCube,就能够提供访问多版本 K8s 的能力,对外暴露统一的 K8s 风格的 RESTfule API,用户既可以使用精确的 GVR 去访问不同版本的 K8s 资源,也可用使用统一版本的 GVR 去访问不同版本的 K8s 资源,KubeCube 会做自适应转化。

图片

K8s native convert

  1. K8s api workflow

图片

  1. K8s version convert
    图片

K8s 版本转换原则

  1. 同一个 group 的不同 version 都可以转换成该 group 的 internalVersion
  2. 某一 group 的 internalVersion 可以转换成该 group 下的任一 versionK8s

版本转化的核心 ——scheme

Scheme 中拥有 concerter 转化器,其内部存放了各个 API 注册的版本转化函数。

type Scheme struct {
    ...

    // converter stores all registered conversion functions. It also has
    // default converting behavior.
    converter *conversion.Converter

    ...
}

// Converter knows how to convert one type to another.
type Converter struct {
    // Map from the conversion pair to a function which can
    // do the conversion.
    conversionFuncs          ConversionFuncs
    generatedConversionFuncs ConversionFuncs

    // Set of conversions that should be treated as a no-op
    ignoredUntypedConversions map[typePair]struct{}
}

我们已经知道 internalVersion 和指定 version 之间的转换规则,它们的转换函数位于 k8s apis 的定义文件夹下,如:pkg/apis/apps/v1/zz_generated.conversion.go

// Code generated by conversion-gen. DO NOT EDIT.

package v1

import (...)

func init() {
    localSchemeBuilder.Register(RegisterConversions)
}

// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {...}

这些转化函数,一般由 install 包下的 Install(scheme) 函数注册到 Scheme 中。

// Package install installs the apps API group, making it available as
// an option to all of the API encoding/decoding machinery.
package install

import (...)

func init() {
    Install(legacyscheme.Scheme)
}

// Install registers the API group and adds types to a scheme
func Install(scheme *runtime.Scheme) {
    utilruntime.Must(apps.AddToScheme(scheme))
    utilruntime.Must(v1beta1.AddToScheme(scheme))
    utilruntime.Must(v1beta2.AddToScheme(scheme))
    utilruntime.Must(v1.AddToScheme(scheme))
    utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
}

注册完的转会函数,将会在 Convert () 方法中使用。

// Convert will translate src to dest if it knows how. Both must be pointers.
// If no conversion func is registered and the default copying mechanism
// doesn't work on this type pair, an error will be returned.
// 'meta' is given to allow you to pass information to conversion functions,
// it is not used by Convert() other than storing it in the scope.
// Not safe for objects with cyclic references!
func (c *Converter) Convert(src, dest interface{}, meta *Meta) error {
  // 转换函数 map 的 key
    pair := typePair{reflect.TypeOf(src), reflect.TypeOf(dest)}
  
  // 实际的 convert 句柄
    scope := &scope{
        converter: c,
        meta:      meta,
    }

    // ignore conversions of this type
    if _, ok := c.ignoredUntypedConversions[pair]; ok {
        return nil
    }
  // 使用预先注册的转换函数进行转换
    if fn, ok := c.conversionFuncs.untyped[pair]; ok {
        return fn(src, dest, scope)
    }
    if fn, ok := c.generatedConversionFuncs.untyped[pair]; ok {
        return fn(src, dest, scope)
    }

    dv, err := EnforcePtr(dest)
    if err != nil {
        return err
    }
    sv, err := EnforcePtr(src)
    if err != nil {
        return err
    }
    return fmt.Errorf("converting (%s) to (%s): unknown conversion", sv.Type(), dv.Type())
}

KubeCube version conversion

了解了 K8s 版本转换的大致思路后,KubeCube 如果需要做版本转换的能力,需要做到以下几点:

  1. 维护版本转换专用的 Scheme
  2. 注册所有的 K8s 的 API 转换函数,并提供拓展方法
  3. 使用 discovery client 提早做 src api 和 dest api 的转换检查
  4. Conversion func register

图片

KubeCube 会默认注册所有 K8s 原生资源的转换函数,同时也提供注册自定义资源转换函数的入口。

  1. Greeting target cluster

图片

  1. Controller-runtime client support

KubeCube 的版本转化 SDK 提供了 Wrap controller-runtime 的 client.Client 的能力,可以将 client.Client 升级为具有版本转化能力的句柄。

写在最后

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

图片

了解更多

KubeCube 用户管理与身份认证深入解读
KubeCube 多集群管理
KubeCube 开源:简化 Kubernetes 落地的六大特性
KubeCube 主页:https://www.kubecube.io/
GitHub:https://github.com/kubecube-i...

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

【点击领取网易技术白皮书】


网易数帆
391 声望550 粉丝

网易数智旗下全链路大数据生产力平台,聚焦全链路数据开发、治理及分析,为企业量身打造稳定、可控、创新的数据生产力平台,服务“看数”、“管数”、“用数”等业务场景,盘活数据资产,释放数据价值。