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 }