github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/pkg/cluster-runtime/installer.go (about)

     1  // Copyright © 2022 Alibaba Group Holding Ltd.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package clusterruntime
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"net"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/sealerio/sealer/common"
    25  	"github.com/sealerio/sealer/pkg/clusterfile"
    26  	containerruntime "github.com/sealerio/sealer/pkg/container-runtime"
    27  	"github.com/sealerio/sealer/pkg/imagedistributor"
    28  	"github.com/sealerio/sealer/pkg/infradriver"
    29  	"github.com/sealerio/sealer/pkg/registry"
    30  	"github.com/sealerio/sealer/pkg/runtime"
    31  	"github.com/sealerio/sealer/pkg/runtime/k0s"
    32  	"github.com/sealerio/sealer/pkg/runtime/kubernetes"
    33  	"github.com/sealerio/sealer/pkg/runtime/kubernetes/kubeadm"
    34  	v1 "github.com/sealerio/sealer/types/api/v1"
    35  	v2 "github.com/sealerio/sealer/types/api/v2"
    36  	"github.com/sealerio/sealer/utils"
    37  
    38  	"github.com/sirupsen/logrus"
    39  	corev1 "k8s.io/api/core/v1"
    40  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    41  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    42  	runtimeClient "sigs.k8s.io/controller-runtime/pkg/client"
    43  	"sigs.k8s.io/kustomize/kyaml/yaml"
    44  )
    45  
    46  var tryTimes = 10
    47  var trySleepTime = time.Second
    48  
    49  const (
    50  	// CRILabel is key for get container runtime interface type.
    51  	CRILabel = "cluster.alpha.sealer.io/container-runtime-type"
    52  	// CRTLabel is key for get cluster runtime type.
    53  	CRTLabel = "cluster.alpha.sealer.io/cluster-runtime-type"
    54  
    55  	RegistryConfigMapName     = "sealer-registry"
    56  	RegistryConfigMapDataName = "registry"
    57  )
    58  
    59  // RuntimeConfig for Installer
    60  type RuntimeConfig struct {
    61  	Distributor            imagedistributor.Distributor
    62  	ContainerRuntimeConfig v2.ContainerRuntimeConfig
    63  	KubeadmConfig          kubeadm.KubeadmConfig
    64  	Plugins                []v1.Plugin
    65  }
    66  
    67  type Installer struct {
    68  	RuntimeConfig
    69  	infraDriver               infradriver.InfraDriver
    70  	containerRuntimeInstaller containerruntime.Installer
    71  	clusterRuntimeType        string
    72  	hooks                     map[Phase]HookConfigList
    73  	regConfig                 v2.Registry
    74  }
    75  
    76  type InstallInfo struct {
    77  	ContainerRuntimeType string
    78  	ClusterRuntimeType   string
    79  }
    80  
    81  func getCRIInstaller(containerRuntime string, infraDriver infradriver.InfraDriver) (containerruntime.Installer, error) {
    82  	switch containerRuntime {
    83  	case common.Docker:
    84  		return containerruntime.NewInstaller(v2.ContainerRuntimeConfig{
    85  			Type: common.Docker,
    86  		}, infraDriver)
    87  	case common.Containerd:
    88  		return containerruntime.NewInstaller(v2.ContainerRuntimeConfig{
    89  			Type: common.Containerd,
    90  		}, infraDriver)
    91  	default:
    92  		return nil, fmt.Errorf("not support container runtime %s", containerRuntime)
    93  	}
    94  }
    95  
    96  func getClusterRuntimeInstaller(clusterRuntimeType string, driver infradriver.InfraDriver, crInfo containerruntime.Info,
    97  	registryInfo registry.Info, kubeadmConfig kubeadm.KubeadmConfig) (runtime.Installer, error) {
    98  	switch clusterRuntimeType {
    99  	case common.K8s:
   100  		return kubernetes.NewKubeadmRuntime(kubeadmConfig, driver, crInfo, registryInfo)
   101  	case common.K0s:
   102  		return k0s.NewK0sRuntime(driver, crInfo, registryInfo)
   103  		//todo support k3s runtime
   104  	default:
   105  		return nil, fmt.Errorf("not support cluster runtime %s", clusterRuntimeType)
   106  	}
   107  }
   108  
   109  func NewInstaller(infraDriver infradriver.InfraDriver, runtimeConfig RuntimeConfig, installInfo InstallInfo) (*Installer, error) {
   110  	var (
   111  		err       error
   112  		installer = &Installer{
   113  			regConfig:          infraDriver.GetClusterRegistry(),
   114  			clusterRuntimeType: installInfo.ClusterRuntimeType,
   115  		}
   116  	)
   117  
   118  	installer.RuntimeConfig = runtimeConfig
   119  	// configure container runtime
   120  	//todo need to support other container runtimes
   121  	installer.containerRuntimeInstaller, err = getCRIInstaller(installInfo.ContainerRuntimeType, infraDriver)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	// add installer hooks
   127  	hooks, err := transferPluginsToHooks(runtimeConfig.Plugins)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	installer.hooks = hooks
   132  	installer.infraDriver = infraDriver
   133  	return installer, nil
   134  }
   135  
   136  func (i *Installer) Install() error {
   137  	var (
   138  		masters = i.infraDriver.GetHostIPListByRole(common.MASTER)
   139  		master0 = masters[0]
   140  		workers = getWorkerIPList(i.infraDriver)
   141  		all     = append(masters, workers...)
   142  		rootfs  = i.infraDriver.GetClusterRootfsPath()
   143  	)
   144  
   145  	// set HostAlias
   146  	if err := i.infraDriver.SetClusterHostAliases(all); err != nil {
   147  		return err
   148  	}
   149  
   150  	// distribute rootfs
   151  	if err := i.Distributor.Distribute(all, rootfs); err != nil {
   152  		return err
   153  	}
   154  
   155  	if err := i.runClusterHook(master0, PreInstallCluster); err != nil {
   156  		return err
   157  	}
   158  
   159  	if err := i.runHostHook(PreInitHost, all); err != nil {
   160  		return err
   161  	}
   162  
   163  	if err := i.containerRuntimeInstaller.InstallOn(all); err != nil {
   164  		return err
   165  	}
   166  
   167  	crInfo, err := i.containerRuntimeInstaller.GetInfo()
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	var deployHosts []net.IP
   173  	if i.regConfig.LocalRegistry != nil {
   174  		installer := registry.NewInstaller(nil, i.regConfig.LocalRegistry, i.infraDriver, i.Distributor)
   175  		if *i.regConfig.LocalRegistry.HA {
   176  			deployHosts, err = installer.Reconcile(masters)
   177  			if err != nil {
   178  				return err
   179  			}
   180  		} else {
   181  			deployHosts, err = installer.Reconcile([]net.IP{master0})
   182  			if err != nil {
   183  				return err
   184  			}
   185  		}
   186  	}
   187  
   188  	registryConfigurator, err := registry.NewConfigurator(deployHosts, crInfo, i.regConfig, i.infraDriver, i.Distributor)
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	if err = registryConfigurator.InstallOn(masters, workers); err != nil {
   194  		return err
   195  	}
   196  
   197  	registryDriver, err := registryConfigurator.GetDriver()
   198  	if err != nil {
   199  		return err
   200  	}
   201  
   202  	registryInfo := registryConfigurator.GetRegistryInfo()
   203  
   204  	kubeRuntimeInstaller, err := getClusterRuntimeInstaller(i.clusterRuntimeType, i.infraDriver,
   205  		crInfo, registryDriver.GetInfo(), i.KubeadmConfig)
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	if err = kubeRuntimeInstaller.Install(); err != nil {
   211  		return err
   212  	}
   213  
   214  	if err = i.runHostHook(PostInitHost, all); err != nil {
   215  		return err
   216  	}
   217  
   218  	if err = i.runClusterHook(master0, PostInstallCluster); err != nil {
   219  		return err
   220  	}
   221  
   222  	runtimeDriver, err := kubeRuntimeInstaller.GetCurrentRuntimeDriver()
   223  	if err != nil {
   224  		return err
   225  	}
   226  
   227  	if err := i.setRoles(runtimeDriver); err != nil {
   228  		return err
   229  	}
   230  
   231  	if err = i.setNodeLabels(all, runtimeDriver); err != nil {
   232  		return err
   233  	}
   234  
   235  	if err = i.setNodeTaints(all, runtimeDriver); err != nil {
   236  		return err
   237  	}
   238  
   239  	if err = i.saveRegistryInfo(runtimeDriver, registryInfo); err != nil {
   240  		return err
   241  	}
   242  
   243  	return nil
   244  }
   245  
   246  func (i *Installer) GetCurrentDriver() (registry.Driver, runtime.Driver, error) {
   247  	var (
   248  		masters             = i.infraDriver.GetHostIPListByRole(common.MASTER)
   249  		master0             = masters[0]
   250  		registryDeployHosts = masters
   251  	)
   252  	crInfo, err := i.containerRuntimeInstaller.GetInfo()
   253  	if err != nil {
   254  		return nil, nil, err
   255  	}
   256  
   257  	if i.regConfig.LocalRegistry != nil && !*i.regConfig.LocalRegistry.HA {
   258  		registryDeployHosts = []net.IP{master0}
   259  	}
   260  	// TODO, init here or in constructor?
   261  	registryConfigurator, err := registry.NewConfigurator(registryDeployHosts, crInfo, i.regConfig, i.infraDriver, i.Distributor)
   262  	if err != nil {
   263  		return nil, nil, err
   264  	}
   265  
   266  	registryDriver, err := registryConfigurator.GetDriver()
   267  	if err != nil {
   268  		return nil, nil, err
   269  	}
   270  
   271  	kubeRuntimeInstaller, err := kubernetes.NewKubeadmRuntime(i.KubeadmConfig, i.infraDriver, crInfo, registryDriver.GetInfo())
   272  	if err != nil {
   273  		return nil, nil, err
   274  	}
   275  
   276  	runtimeDriver, err := kubeRuntimeInstaller.GetCurrentRuntimeDriver()
   277  	if err != nil {
   278  		return nil, nil, err
   279  	}
   280  
   281  	return registryDriver, runtimeDriver, nil
   282  }
   283  
   284  // setRoles save roles
   285  func (i *Installer) setRoles(driver runtime.Driver) error {
   286  	nodeList := corev1.NodeList{}
   287  	if err := driver.List(context.TODO(), &nodeList); err != nil {
   288  		return err
   289  	}
   290  
   291  	genRoleLabelFunc := func(role string) string {
   292  		return fmt.Sprintf("node-role.kubernetes.io/%s", role)
   293  	}
   294  
   295  	for idx, node := range nodeList.Items {
   296  		addresses := node.Status.Addresses
   297  		for _, address := range addresses {
   298  			if address.Type != "InternalIP" {
   299  				continue
   300  			}
   301  			roles := i.infraDriver.GetRoleListByHostIP(address.Address)
   302  			if len(roles) == 0 {
   303  				continue
   304  			}
   305  			newNode := node.DeepCopy()
   306  
   307  			for _, role := range roles {
   308  				newNode.Labels[genRoleLabelFunc(role)] = ""
   309  			}
   310  			patch := runtimeClient.MergeFrom(&nodeList.Items[idx])
   311  
   312  			if err := driver.Patch(context.TODO(), newNode, patch); err != nil {
   313  				return err
   314  			}
   315  		}
   316  	}
   317  
   318  	return nil
   319  }
   320  
   321  func (i *Installer) setNodeLabels(hosts []net.IP, driver runtime.Driver) error {
   322  	// set new added host labels if it is existed
   323  	nodeList := corev1.NodeList{}
   324  	if err := driver.List(context.TODO(), &nodeList); err != nil {
   325  		return fmt.Errorf("failed to list cluster nodes: %v", err)
   326  	}
   327  
   328  	nodeLabel := make(map[string]corev1.Node)
   329  	for _, node := range nodeList.Items {
   330  		nodeLabel[getAddress(node.Status.Addresses)] = node
   331  	}
   332  
   333  	for _, ip := range hosts {
   334  		labels := i.infraDriver.GetHostLabels(ip)
   335  		if len(labels) == 0 {
   336  			continue
   337  		}
   338  
   339  		if node, ok := nodeLabel[ip.String()]; ok {
   340  			newNode := node.DeepCopy()
   341  			m := node.GetLabels()
   342  			for key, value := range labels {
   343  				m[key] = value
   344  			}
   345  
   346  			newNode.SetLabels(m)
   347  			newNode.SetResourceVersion("")
   348  			if err := driver.Update(context.TODO(), newNode); err != nil {
   349  				return fmt.Errorf("failed to label cluster nodes %s: %v", ip.String(), err)
   350  			}
   351  		}
   352  	}
   353  
   354  	return nil
   355  }
   356  
   357  func getAddress(addresses []corev1.NodeAddress) string {
   358  	for _, v := range addresses {
   359  		if strings.EqualFold(string(v.Type), "InternalIP") {
   360  			return v.Address
   361  		}
   362  	}
   363  	return ""
   364  }
   365  
   366  func (i *Installer) setNodeTaints(hosts []net.IP, driver runtime.Driver) error {
   367  	var (
   368  		k8snode    corev1.Node
   369  		ok         bool
   370  		nodeTaints []corev1.Taint
   371  	)
   372  	nodeList := corev1.NodeList{}
   373  	if err := driver.List(context.TODO(), &nodeList); err != nil {
   374  		return fmt.Errorf("failed to list cluster nodes: %v", err)
   375  	}
   376  	nodeTaint := make(map[string]corev1.Node)
   377  	for _, node := range nodeList.Items {
   378  		nodeTaint[getAddress(node.Status.Addresses)] = node
   379  	}
   380  
   381  	for _, ip := range hosts {
   382  		taints := i.infraDriver.GetHostTaints(ip)
   383  		if len(taints) == 0 {
   384  			continue
   385  		}
   386  
   387  		if k8snode, ok = nodeTaint[ip.String()]; !ok {
   388  			continue
   389  		}
   390  		newNode := k8snode.DeepCopy()
   391  		for _, taint := range taints {
   392  			if strings.Contains(taint.Key, infradriver.DelSymbol) {
   393  				taintKey := strings.TrimSuffix(taint.Key, infradriver.DelSymbol)
   394  				nodeTaints, _ = infradriver.DeleteTaintsByKey(newNode.Spec.Taints, taintKey)
   395  				newNode.Spec.Taints = nodeTaints
   396  			} else if strings.Contains(string(taint.Effect), infradriver.DelSymbol) {
   397  				nodeTaints, _ = infradriver.DeleteTaint(newNode.Spec.Taints, &taint) // #nosec
   398  				newNode.Spec.Taints = nodeTaints
   399  			} else {
   400  				newNode.Spec.Taints = taints
   401  			}
   402  		}
   403  		newNode.SetResourceVersion("")
   404  		if err := utils.Retry(tryTimes, trySleepTime, func() error {
   405  			if err := driver.Update(context.TODO(), newNode); err != nil {
   406  				return err
   407  			}
   408  			return nil
   409  		}); err != nil {
   410  			return err
   411  		}
   412  	}
   413  
   414  	return nil
   415  }
   416  
   417  func (i *Installer) saveRegistryInfo(driver runtime.Driver, registryInfo registry.RegistryInfo) error {
   418  	info, err := yaml.Marshal(registryInfo)
   419  	if err != nil {
   420  		return err
   421  	}
   422  
   423  	cm := &corev1.ConfigMap{
   424  		ObjectMeta: metav1.ObjectMeta{
   425  			Name:      RegistryConfigMapName,
   426  			Namespace: clusterfile.ClusterfileConfigMapNamespace,
   427  		},
   428  		Data: map[string]string{RegistryConfigMapDataName: string(info)},
   429  	}
   430  
   431  	ctx := context.Background()
   432  	if err := driver.Create(ctx, cm); err != nil {
   433  		if !apierrors.IsAlreadyExists(err) {
   434  			return fmt.Errorf("unable to create configmap: %v", err)
   435  		}
   436  
   437  		if err := driver.Update(ctx, cm); err != nil {
   438  			return fmt.Errorf("unable to update configmap: %v", err)
   439  		}
   440  	}
   441  	return nil
   442  }
   443  
   444  func GetClusterInstallInfo(imageLabels map[string]string, criConfig v2.ContainerRuntimeConfig) InstallInfo {
   445  	cri := imageLabels[CRILabel]
   446  	if cri == "" {
   447  		cri = common.Docker
   448  	}
   449  	if criConfig.Type != "" {
   450  		cri = criConfig.Type
   451  	}
   452  	clusterRuntimeType := imageLabels[CRTLabel]
   453  	if clusterRuntimeType == "" {
   454  		clusterRuntimeType = common.K8s
   455  	}
   456  	logrus.Infof("The cri is %s, cluster runtime type is %s\n", cri, clusterRuntimeType)
   457  	return InstallInfo{
   458  		ContainerRuntimeType: cri,
   459  		ClusterRuntimeType:   clusterRuntimeType,
   460  	}
   461  }
   462  
   463  func GetClusterConfPath(labels map[string]string) string {
   464  	clusterRuntimeType := labels[CRTLabel]
   465  	if clusterRuntimeType == "" {
   466  		clusterRuntimeType = common.K8s
   467  	}
   468  	switch clusterRuntimeType {
   469  	case common.K8s:
   470  		return kubernetes.AdminKubeConfPath
   471  	case common.K0s:
   472  		return k0s.DefaultAdminConfPath
   473  	//TODO support k3s
   474  	default:
   475  		return kubernetes.AdminKubeConfPath
   476  	}
   477  }