一文读懂集群全生命周期管理

Posted by 爱折腾的工程师 on Friday, March 11, 2022

1. 集群全生命周期管理简介

集群全生命周期管理,包括集群的部署、更新、升级,以及后期集群的扩容/缩容等操作。传统模式下的集群全生命周期管理大多数是命令式API方式实现, 命令式API注重如何部署,定义部署的过程;云原生模式下的集群全生命周期管理是声明式API实现,声明式API注重定义集群的状态,而不是定义部署的过程, 实际上定义集群的状态里包括了定义部署的过程,对外暴露更简单了,实际上内部增加了复杂性,声明式API可以看作是命令式API的抽象。

2. 开源实现

2.1 tkestack

2.1.1 tkestack简介

TKEStack是一个开源项目,为在生产环境中部署容器的组织提供一个统一的容器管理平台。TKEStack可以简化部署和使用Kubernetes,具备统一集群管理能力。

统一集群管理

  • 提供Web控制台和命令行客户端,用于集中管理多个Kubernetes集群
  • 可与现有的身份验证机制集成,包括LDAP,Active Directory,front proxy和public OAuth providers(例如GitHub)
  • 统一授权管理,不仅在集群管理级别,甚至在Kubernetes资源级别
  • 多租户支持,包括团队和用户对容器、构建和网络通信的隔离

2.1.2 代码分析

集群管理能力是由tke-platform模块提供,k8s的Aggregated API实现,分为tke-platform-api和tke-platform-controller两个进程服务。 AA是扩展k8s API的一种方式,另外一种扩展方式是使用CRD。

2.1.2.1 tke-platform-api

启动入口tkestack.io/tke/cmd/tke-platform-api/apiserver.go

2.2 kubekey

2.2.1 kubekey简介

KubeKey(由Go语言开发)是一种全新的安装工具,替代了以前使用的基于ansible的安装程序。KubeKey为您提供灵活的安装选择,您可以仅安装Kubernetes,也可以同时安装Kubernetes和KubeSphere。

KubeKey的几种使用场景:

  • 仅安装Kubernetes;
  • 使用一个命令同时安装Kubernetes和KubeSphere;
  • 扩缩集群;
  • 升级集群;
  • 安装Kubernetes相关的插件(Chart 或 YAML)。

2.2.2 代码分析

2.2.2.1 命令行模式

入口函数github.com/kubesphere/kubekey/cmd/main.go

// Using a separate entry-point can reduce the size of the binary file
func main() {
    //初始化kubekey命令行实例,简称kk
    cmd := ctl.NewDefaultKubeKeyCommand()
    _ = exec.Command("/bin/bash", "-c", "ulimit -u 65535").Run()
    _ = exec.Command("/bin/bash", "-c", "ulimit -n 65535").Run()

    // Execute adds all child commands to the root command and sets flags appropriately.
    // This is called by main.main(). It only needs to happen once to the rootCmd.
    if err := cmd.Execute(); err != nil {
        //fmt.Println(err)
        os.Exit(1)
    }
}

2.2.2.2 kk主命令

命令行主要逻辑,主命令及子命令初始化

// NewKubeKeyCommand creates a new kubekey root command
func NewKubeKeyCommand(o KubeKeyOptions) *cobra.Command {
    //主命令kk
    cmds := &cobra.Command{
        Use:   "kk",
        Short: "Kubernetes/KubeSphere Deploy Tool",
        Long: `Deploy a Kubernetes or KubeSphere cluster efficiently, flexibly and easily. There are three scenarios to use KubeKey.
1. Install Kubernetes only
2. Install Kubernetes and KubeSphere together in one command
3. Install Kubernetes first, then deploy KubeSphere on it using https://github.com/kubesphere/ks-installer`,
    }
    //init子命令及os、registry子命令
    cmds.AddCommand(initOs.NewCmdInit())
    //create子命令及cluster、config、manifest子命令
    cmds.AddCommand(create.NewCmdCreate())
    //delete子命令及cluster、node子命令
    cmds.AddCommand(delete.NewCmdDelete())
    //add子命令及nodes子命令
    cmds.AddCommand(add.NewCmdAdd())
    //upgrade子命令
    cmds.AddCommand(upgrade.NewCmdUpgrade())
    //certs子命令及check-expiration、renew子命令
    cmds.AddCommand(cert.NewCmdCerts())
    //artifact子命令及export、images子命令
    cmds.AddCommand(artifact.NewCmdArtifact())
    //plugin子命令及list子命令
    cmds.AddCommand(plugin.NewCmdPlugin(o.IOStreams))
    //completion子命令
    cmds.AddCommand(completion.NewCmdCompletion())
    //version子命令
    cmds.AddCommand(version.NewCmdVersion())
    return cmds
}

2.2.2.3 init子命令

// NewCmdInit create a new init command
func NewCmdInit() *cobra.Command {
    o := NewInitOptions()
    cmd := &cobra.Command{
        Use:   "init",
        Short: "Initializes the installation environment",
    }
    o.CommonOptions.AddCommonFlag(cmd)
    cmd.AddCommand(NewCmdInitOs())
    cmd.AddCommand(NewCmdInitRegistry())
    return cmd
}

init子命令下又添加了两个子命令os和registry

type InitOsOptions struct {
    //公共参数,包含InCluster(是否在集群内),VerBose(是否打印详情),SkipConfirmCheck(是否跳过确认),IgnoreErr(是否忽略错误信息)
    CommonOptions  *options.CommonOptions
    //集群配置文件路径
    ClusterCfgFile string
    //依赖的源目录
    SourcesDir     string
    //是否创建本地的registry
    AddImagesRepo  bool
}

// NewCmdInitOs creates a new init os command
func NewCmdInitOs() *cobra.Command {
    //初始化了InitOsOptions参数
    o := NewInitOsOptions()
    cmd := &cobra.Command{
        Use:   "os",
        Short: "Init operating system",
        Run: func(cmd *cobra.Command, args []string) {
            util.CheckErr(o.Run())
        },
    }
    //通用参数命令行方式赋值
    o.CommonOptions.AddCommonFlag(cmd)
    //os子命令,命令行方式赋值
    o.AddFlags(cmd)
    return cmd
}

os子命令逻辑,核心处理函数o.Run()

func (o *InitOsOptions) Run() error {
    //这里又初始化了一个Argument结构体实例,把InitOsOptions的参数重新赋值給它
    arg := common.Argument{
        FilePath:      o.ClusterCfgFile,
        SourcesDir:    o.SourcesDir,
        AddImagesRepo: o.AddImagesRepo,
        Debug:         o.CommonOptions.Verbose,
    }
    //初始化依赖
    return pipelines.InitDependencies(arg)
}
func InitDependencies(args common.Argument) error {
    var loaderType string
    //根据是否FilePath来初始化loaderType参数
    if args.FilePath != "" {
        loaderType = common.File
    } else {
        loaderType = common.AllInOne
    }
    //初始化一个KubeRuntime实例
    runtime, err := common.NewKubeRuntime(loaderType, args)
    if err != nil {
        return err
    }
    //
    if err := NewInitDependenciesPipeline(runtime); err != nil {
        return err
    }
    return nil
}

NewKubeRuntime函数

func NewKubeRuntime(flag string, arg Argument) (*KubeRuntime, error) {
    //根据loaderType返回对应的实现Loader接口的实例,Loader接口包含一个Load()方法定义
    //有三类结构体ConfigMapLoader、DefaultLoader、FileLoader实现了Loader接口
    loader := NewLoader(flag, arg)
    //初始化一个kubekeyapiv1alpha2.Cluster实例,从对应的Loader接口实现中获取Cluster实例
    cluster, err := loader.Load()
    if err != nil {
        return nil, err
    }
    //初始化一个BaseRuntime实例
    //NewDialer:初始化一个Dialer结构体实例,存放ssh的连接,连接实现了Connection接口,接口定义了
    //Exec、PExec、Fetch、Scp、RemoteFileExist、RemoteDirExist、MkDirAll、Chmod、Close
    base := connector.NewBaseRuntime(cluster.Name, connector.NewDialer(), arg.Debug, arg.IgnoreErr)

    clusterSpec := &cluster.Spec
    //设置集群参数默认值
    defaultCluster, roleGroups := clusterSpec.SetDefaultClusterSpec(arg.InCluster)

    hostSet := make(map[string]struct{})
    //遍历角色组,设置对应host的角色,赋值base实例的allHosts、roleHosts变量
    for _, role := range roleGroups {
        for _, host := range role {
            if host.IsRole(Master) || host.IsRole(Worker) {
                host.SetRole(K8s)
            }
            if _, ok := hostSet[host.GetName()]; !ok {
                hostSet[host.GetName()] = struct{}{}
                base.AppendHost(host)
                base.AppendRoleMap(host)
            }
        }
    }

    arg.KsEnable = defaultCluster.KubeSphere.Enabled
    arg.KsVersion = defaultCluster.KubeSphere.Version
    //初始化KubeRuntime实例
    r := &KubeRuntime{
        Cluster:     defaultCluster,
        ClusterName: cluster.Name,
        Arg:         arg,
    }
    r.BaseRuntime = base

    return r, nil
}

NewInitDependenciesPipeline函数

func NewInitDependenciesPipeline(runtime *common.KubeRuntime) error {
    //Module是个接口,定义的方法:IsSkip、Default、Init、Is、Run、Until、Slogan、AutoAssert、AppendPostHook、CallPostHook
    //定义要执行模块有哪些
    m := []module.Module{
        //InitDependenciesModule是个嵌套结构体,实现了继承&重载的效果
        //嵌套顺序:InitDependenciesModule -> common.KubeModule -> module.BaseTaskModule -> BaseModule
        //每个Module接口具体实现中又定义了task.Interface接口实现,
        //task.Interface接口定义的方法:GetDesc(获取task描述信息)、Init(task初始化)、Execute(执行)、ExecuteRollback(执行回退),
        //实现task.Interface接口的结构体有两种:LocalTask、RemoteTask
        &os.InitDependenciesModule{},
    }

    //Pipeline包含多个Module实现,也包含多个PostHookInterface接口实现(执行完模块操作之后的后续操作)
    //Pipeline定义了Start、Init、InitModule、RunModule方法,调用关系为Start -> Init/InitModule/RunModule
    p := pipeline.Pipeline{
        Name:    "InitDependenciesPipeline",
        Modules: m,
        Runtime: runtime,
    }
    //运行Pipeline
    //1.Init: 初始化一个sync.Map, 获取所有主机的数量
    //2.遍历所有的执行模块,设置SkipConfirmCheck参数的就跳过,依次执行
    //InitModule,初始化module(用到一个moduleCache实例变量从sync.Pool里获取)
    //RunModule,运行module,返回执行结果
    //CallPostHook,再运行PostHook操作
    //3.PipelineCache变量置空
    if err := p.Start(); err != nil {
        return err
    }
    return nil
}

init/os命令的过程就是这样了,按照pipeline -> module -> task流程,其它子命令的过程类似。

2.2.2.2 控制器模式

controller模式的集群全生命周期实现,集群结构体定义:

// Cluster is the Schema for the clusters API
// +kubebuilder:resource:path=clusters,scope=Cluster
type Cluster struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`

    Spec   ClusterSpec   `json:"spec,omitempty"`
    Status ClusterStatus `json:"status,omitempty"`
}

// ClusterSpec defines the desired state of Cluster
type ClusterSpec struct {
    // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
    // Important: Run "make" to regenerate code after modifying this file

    // Foo is an example field of Cluster. Edit Cluster_types.go to remove/update
    //主机组配置信息,包含名称,地址,内部地址,ssh连接信息,架构,标签信息
    Hosts                []HostCfg            `yaml:"hosts" json:"hosts,omitempty"`
    //角色组,不同的角色安装不同的组件
    RoleGroups           map[string][]string  `yaml:"roleGroups" json:"roleGroups,omitempty"`
    //控制面接入地址,包含内部负载均衡,域名,地址,端口
    ControlPlaneEndpoint ControlPlaneEndpoint `yaml:"controlPlaneEndpoint" json:"controlPlaneEndpoint,omitempty"`
    //系统配置,包含ntpserver和时区配置
    System               System               `yaml:"system" json:"system,omitempty"`
    //k8s集群配置
    Kubernetes           Kubernetes           `yaml:"kubernetes" json:"kubernetes,omitempty"`
    //k8s网络插件配置
    Network              NetworkConfig        `yaml:"network" json:"network,omitempty"`
    //镜像仓库配置
    Registry             RegistryConfig       `yaml:"registry" json:"registry,omitempty"`
    //额外插件配置(chart)
    Addons               []Addon              `yaml:"addons" json:"addons,omitempty"`
    //是否启用KubeSphere
    KubeSphere           KubeSphere           `json:"kubesphere,omitempty"`
}

// ClusterStatus defines the observed state of Cluster
//集群状态
type ClusterStatus struct {
    // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
    // Important: Run "make" to regenerate code after modifying this file
    //创建集群/添加节点的job信息,job包含一组pod信息
    JobInfo       JobInfo      `json:"jobInfo,omitempty"`
    //pipeline状态信息
    PiplineInfo   PiplineInfo  `json:"piplineInfo,omitempty"`
    //集群版本
    Version       string       `json:"version,omitempty"`
    //集群网络插件
    NetworkPlugin string       `json:"networkPlugin,omitempty"`
    //集群节点统计
    NodesCount    int          `json:"nodesCount,omitempty"`
    //etcd节点统计
    EtcdCount     int          `json:"etcdCount,omitempty"`
    //master节点统计
    MasterCount   int          `json:"masterCount,omitempty"`
    //worker节点统计
    WorkerCount   int          `json:"workerCount,omitempty"`
    //节点组的状态信息
    Nodes         []NodeStatus `json:"nodes,omitempty"`
    //过程的步骤信息,每一步安装步骤的信息可以回写到这里
    Conditions    []Condition  `json:"Conditions,omitempty"`
}

// JobInfo defines the job information to be used to create a cluster or add a node.
type JobInfo struct {
    //job的命名空间
    Namespace string    `json:"namespace,omitempty"`
    //job名称
    Name      string    `json:"name,omitempty"`
    //job关联的pod组信息
    Pods      []PodInfo `json:"pods,omitempty"`
}

// PodInfo defines the pod information to be used to create a cluster or add a node.
type PodInfo struct {
    //pod名称
    Name       string          `json:"name,omitempty"`
    //pod关联的container组信息
    Containers []ContainerInfo `json:"containers,omitempty"`
}

// ContainerInfo defines the container information to be used to create a cluster or add a node.
type ContainerInfo struct {
    //container名称
    Name string `json:"name,omitempty"`
}

// PiplineInfo define the pipline information for operating cluster.
type PiplineInfo struct {
    // Running or Terminated
    //pipeline状态,只有Running和Terminated
    Status string `json:"status,omitempty"`
}

// NodeStatus defines the status information of the nodes in the cluster.
type NodeStatus struct {
    //内部IP
    InternalIP string          `json:"internalIP,omitempty"`
    //主机名
    Hostname   string          `json:"hostname,omitempty"`
    //角色
    Roles      map[string]bool `json:"roles,omitempty"`
}

// Condition defines the process information.
type Condition struct {
    //步骤名称
    Step      string           `json:"step,omitempty"`
    //开始时间
    StartTime metav1.Time      `json:"startTime,omitempty"`
    //结束时间
    EndTime   metav1.Time      `json:"endTime,omitempty"`
    //状态是否正常
    Status    bool             `json:"status,omitempty"`
    //执行步骤产生的事件组信息
    Events    map[string]Event `json:"event,omitempty"`
}

type Event struct {
    //事件关联的步骤名称
    Step    string `yaml:"step" json:"step,omitempty"`
    //事件状态
    Status  string `yaml:"status" json:"status,omitempty"`
    //事件详情
    Message string `yaml:"message" json:"message,omitempty"`
}

2.3 cluster-api

参考链接

「真诚赞赏,手留余香」

爱折腾的工程师

真诚赞赏,手留余香

使用微信扫描二维码完成支付