KubeVela 是多集群应用管理组件,所以在使用之前需要将集群纳管到 KubeVela 中,让 KubeVela 能感知并维护集群信息。在应用下发到指定集群时,KubeVela 能知道如何连接到目标集群并进行操作。
KubeVela 使用的是 Secret
来保存集群信息的,和 Cluster Gateway 共享的同一套 Secret
进行集群管理。当进行集群纳管时,KubeVela 会创建名字和集群名相同的 Secret
,用于存储集群的连接信息。
当请求从 APIServer 转发到 Cluster Gateway 时,使用路径中提供的集群名去查询 Secret 并获取到纳管集群的连接信息。
Cluster Gateway 处理流程如下:
Cluster Gateway 处理流程图
集群信息 Secret
Secret 数据类似下面这样:
apiVersion: v1
kind: Secret
metadata:
name: managed1
labels:
cluster.core.oam.dev/cluster-credential-type: ServiceAccountToken
type: Opaque # <--- Has to be opaque
data:
endpoint: "..." # Should NOT be 127.0.0.1
ca.crt: "..." # ca cert for cluster "managed1"
token: "..." # working jwt token
纳管集群实现
纳管集群时,KubeVela 有两套非常相似的处理逻辑,VelaUX 和 vela-cli:
VelaUX 业务逻辑入口在 clusterServiceImpl.createKubeCluster
,在进行一些判断后通过 joinClusterByKubeConfigString()
函数注册纳管集群,最终调用 multicluster.JoinClusterByKubeConfig()
函数进行集群纳管处理;
vela-cli 处理入口在 NewClusterJoinCommand
中,直接调用 multicluster.JoinClusterByKubeConfig()
函数进行集群纳管处理。
JoinClusterByKubeConfig()
函数通过 Secret
来管理集群,而在些之上 VelaUX 更进一步。
VelaUX 的 createKubeCluster()
方法还会在 Store 创建 model.Cluster{}
结构的数据,保存集群信息。Store 如果是 kubeapi
,则是存储到 ConfigMap
中,否则存储到 MongoDB。
JoinClusterByKubeConfig
multicluster.JoinClusterByKubeConfig()
函数会创建前面提到的 Secret
用于管理集群,但是在创建后会做一些判断,如果条件不满足会删除创建的数据。
pkg/multicluster/cluster_management.go:389
// JoinClusterByKubeConfig add child cluster by kubeconfig path, return cluster info and error
func JoinClusterByKubeConfig(ctx context.Context, cli client.Client, kubeconfigPath string, clusterName string, options ...JoinClusterOption) (*KubeClusterConfig, error) {
args := newJoinClusterArgs(options...)
// 读取纳管集群的 kubeconfig 文件
clusterConfig, err := LoadKubeClusterConfigFromFile(kubeconfigPath)
if err != nil {
return nil, err
}
if err := clusterConfig.SetClusterName(clusterName).SetCreateNamespace(args.createNamespace).Validate(); err != nil {
return nil, err
}
// 纳管处理
switch args.engine {
case ClusterGateWayEngine:
if err = clusterConfig.RegisterByVelaSecret(ctx, cli); err != nil {
return nil, err
}
case OCMEngine:
if args.inClusterBootstrap == nil {
return nil, errors.Wrapf(err, "failed to determine the registration endpoint for the hub cluster "+
"when parsing --in-cluster-bootstrap flag")
}
if err = clusterConfig.RegisterClusterManagedByOCM(ctx, cli, args); err != nil {
return clusterConfig, err
}
}
if cfg, ok := ctx.Value(KubeConfigContext).(*rest.Config); ok {
if err = SetClusterVersionInfo(ctx, cfg, clusterConfig.ClusterName); err != nil {
return nil, err
}
}
return clusterConfig, nil
}
JoinClusterByKubeConfig()
函数在纳管集群时需要从本地文件读取 kubeconfig
配置,所以在 VelaUX 的 joinClusterByKubeConfigString()
中会先创建临时文件再调用 JoinClusterByKubeConfig()
函数处理。
上面代码中有两个分支的处理逻辑。这里主要关注直接通过 Cluster Gateway 管理的集群,OCM 集群先跳过。ClusterGateway 纳管集群通过 clusterConfig.RegisterByVelaSecret()
方法进行:
func (clusterConfig *KubeClusterConfig) RegisterByVelaSecret(ctx context.Context, cli client.Client) error {
// 检查集群是否已经存在
if err := ensureClusterNotExists(ctx, cli, clusterConfig.ClusterName); err != nil {
return errors.Wrapf(err, "cannot use cluster name %s", clusterConfig.ClusterName)
}
// 不存在,创建集群 Secret
if err := clusterConfig.createClusterSecret(ctx, cli, true); err != nil {
return errors.Wrapf(err, "failed to add cluster to kubernetes")
}
// 后置检查
return clusterConfig.PostRegistration(ctx, cli)
}
纳管逻辑很重要部分在后置检查这里,后置检查会保证配置的 clusterConfig.CreateNamespace
命名空间在纳管集群创建,此时会发起请求通过 Cluster Gateway 连接纳管集群进行操作。
[!warning] 这里请求会直接从本地向 APIServer 发起,而且请求 URL 是以下格式: /apis/cluster.core.oam.dev/v1alpha1/clustergateways/{clusterName}/proxy/{api}
Namespace 操作失败会回退纳管操作,删除 Secret
,返回纳管失败错误。
Cluster Gateway 获取集群信息
当代理请求通过 Cluster Gateway 时,就需要去查询集群的信息。
在 Cluster Gateway 代码中,创建代理连接时的处理方法 Connect()
里会去获取集群信息。这个方法会通过 parentStorage.Get()
调用的父资源 ClusterGateway
的 Get()
方法:
pkg/apis/cluster/v1alpha1/clustergateway_types_secret.go:46
func (in *ClusterGateway) Get(ctx context.Context, name string, _ *metav1.GetOptions) (runtime.Object, error) {
if singleton.GetSecretControl() == nil {
return nil, fmt.Errorf("loopback secret client are not inited")
}
clusterSecret, err := singleton.GetSecretControl().Get(ctx, name)
if err != nil {
klog.Warningf("Failed getting secret %q/%q: %v", config.SecretNamespace, name, err)
return nil, err
}
if options.OCMIntegration {
if singleton.GetClusterControl() == nil {
return nil, fmt.Errorf("loopback cluster client are not inited")
}
managedCluster, err := singleton.GetClusterControl().Get(ctx, name)
if err != nil {
return convertFromSecret(clusterSecret)
}
return convertFromManagedClusterAndSecret(managedCluster, clusterSecret)
}
return convertFromSecret(clusterSecret)
}
这个方法内部是通过 SecretControl
获取集群同名的 Secret
,并转换成 ClusterGateway
对象,最终传给 proxyHandler
使用。
总结
KubeVela 这样使用 Secret
管理集群也是有好处的:
Secret
能进行权限管理,保证安全;
支持通过 Label
设置集群元信息,用于调度时基于 Label
进行应用分发。
在代码中我们可以看到,如果使用的 vela-cli 进行纳管,请求会从当前主机向 API Server 发出,而很多时候 Kubernetes 的 API Server 是不开放到外面访问的。
所以在生产环境中,更多的是使用 vela-cli in pod 的方式进行管理。