github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/pkg/runtime/kubernetes/init.go (about) 1 // Copyright © 2021 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 kubernetes 16 17 import ( 18 "context" 19 "fmt" 20 "net" 21 "os" 22 "path" 23 "path/filepath" 24 "strings" 25 26 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" 27 28 "github.com/sealerio/sealer/common" 29 "github.com/sealerio/sealer/pkg/clustercert" 30 "github.com/sealerio/sealer/pkg/runtime/kubernetes/kubeadm" 31 "github.com/sealerio/sealer/utils/shellcommand" 32 "github.com/sealerio/sealer/utils/yaml" 33 "github.com/sirupsen/logrus" 34 "golang.org/x/sync/errgroup" 35 ) 36 37 func (k *Runtime) initKubeadmConfig(masters []net.IP) (kubeadm.KubeadmConfig, error) { 38 extraSANsStr := k.infra.GetClusterEnv()[common.EnvCertSANs] 39 var extraSANs []string 40 if extraSANsStr != "" { 41 extraSANs = strings.Split(extraSANsStr, ",") 42 } 43 conf, err := kubeadm.NewKubeadmConfig( 44 k.Config.KubeadmConfigFromClusterFile, 45 k.getDefaultKubeadmConfig(), 46 masters, 47 k.getAPIServerDomain(), 48 k.Config.containerRuntimeInfo.CgroupDriver, 49 k.Config.RegistryInfo.URL, 50 k.getAPIServerVIP(), extraSANs) 51 if err != nil { 52 return kubeadm.KubeadmConfig{}, err 53 } 54 55 if output, err := k.infra.CmdToString(masters[0], nil, GetCustomizeCRISocket, ""); err == nil && output != "" { 56 conf.InitConfiguration.NodeRegistration.CRISocket = output 57 } 58 59 bs, err := yaml.MarshalWithDelimiter(&conf.InitConfiguration, 60 &conf.ClusterConfiguration, 61 &conf.KubeletConfiguration, 62 &conf.KubeProxyConfiguration, 63 &conf.JoinConfiguration) 64 if err != nil { 65 return kubeadm.KubeadmConfig{}, err 66 } 67 68 localTmpFile := "/tmp/kubeadm.yaml" 69 if err = os.WriteFile(localTmpFile, bs, 0600); err != nil { 70 return kubeadm.KubeadmConfig{}, err 71 } 72 73 if err = k.infra.Copy(masters[0], localTmpFile, KubeadmFileYml); err != nil { 74 return kubeadm.KubeadmConfig{}, err 75 } 76 if err = k.infra.Copy(masters[0], localTmpFile, path.Join(k.infra.GetClusterRootfsPath(), "kubeadm.yaml")); err != nil { 77 return kubeadm.KubeadmConfig{}, err 78 } 79 80 if err = os.Remove(localTmpFile); err != nil { 81 return kubeadm.KubeadmConfig{}, err 82 } 83 84 return conf, nil 85 } 86 87 func (k *Runtime) generateCert(kubeadmConf kubeadm.KubeadmConfig, master0 net.IP) error { 88 hostName, err := k.infra.GetHostName(master0) 89 if err != nil { 90 return err 91 } 92 93 return clustercert.GenerateAllKubernetesCerts( 94 k.getPKIPath(), 95 k.getEtcdCertPath(), 96 hostName, 97 kubeadmConf.GetSvcCIDR(), 98 kubeadmConf.GetDNSDomain(), 99 kubeadmConf.GetCertSANS(), 100 master0, 101 ) 102 } 103 104 func (k *Runtime) createKubeConfig(master0 net.IP) error { 105 hostName, err := k.infra.GetHostName(master0) 106 if err != nil { 107 return err 108 } 109 if nno := k.getNodeNameOverride(master0); nno != "" { 110 hostName = nno 111 } 112 113 controlPlaneEndpoint := fmt.Sprintf("https://%s", net.JoinHostPort(k.getAPIServerDomain(), "6443")) 114 115 return clustercert.CreateJoinControlPlaneKubeConfigFiles(k.infra.GetClusterRootfsPath(), k.getPKIPath(), 116 "ca", hostName, controlPlaneEndpoint, "kubernetes") 117 } 118 119 func (k *Runtime) copyStaticFiles(nodes []net.IP) error { 120 for _, file := range MasterStaticFiles { 121 staticFilePath := filepath.Join(k.getStaticFileDir(), file.Name) 122 cmdLinkStatic := fmt.Sprintf("mkdir -p %s && cp -f %s %s", file.DestinationDir, staticFilePath, filepath.Join(file.DestinationDir, file.Name)) 123 eg, _ := errgroup.WithContext(context.Background()) 124 for _, host := range nodes { 125 h := host 126 eg.Go(func() error { 127 if err := k.infra.CmdAsync(h, nil, cmdLinkStatic); err != nil { 128 return fmt.Errorf("[%s] failed to link static file: %s", h, err.Error()) 129 } 130 131 return nil 132 }) 133 } 134 if err := eg.Wait(); err != nil { 135 return err 136 } 137 } 138 return nil 139 } 140 141 // initMaster0 is using kubeadm init to start up the cluster master0. 142 func (k *Runtime) initMaster0(master0 net.IP) (v1beta3.BootstrapTokenDiscovery, string, error) { 143 if err := k.initKube([]net.IP{master0}); err != nil { 144 return v1beta3.BootstrapTokenDiscovery{}, "", err 145 } 146 147 if err := k.sendClusterCert([]net.IP{master0}); err != nil { 148 return v1beta3.BootstrapTokenDiscovery{}, "", err 149 } 150 151 if err := k.sendKubeConfigFilesToMaster([]net.IP{master0}, AdminConf, ControllerConf, SchedulerConf, KubeletConf); err != nil { 152 return v1beta3.BootstrapTokenDiscovery{}, "", err 153 } 154 155 if err := k.infra.CmdAsync(master0, nil, shellcommand.CommandSetHostAlias(k.getAPIServerDomain(), master0.String())); err != nil { 156 return v1beta3.BootstrapTokenDiscovery{}, "", fmt.Errorf("failed to config cluster hosts file cmd: %v", err) 157 } 158 159 cmdInit, err := k.Command(InitMaster, k.getNodeNameOverride(master0)) 160 if err != nil { 161 return v1beta3.BootstrapTokenDiscovery{}, "", err 162 } 163 logrus.Info("start to init master0...") 164 165 // TODO skip docker version error check for test 166 output, err := k.infra.Cmd(master0, nil, cmdInit) 167 if err != nil { 168 _, wErr := common.StdOut.WriteString(string(output)) 169 if wErr != nil { 170 return v1beta3.BootstrapTokenDiscovery{}, "", err 171 } 172 return v1beta3.BootstrapTokenDiscovery{}, "", fmt.Errorf("failed to init master0: %s. Please clean and reinstall", err) 173 } 174 175 if err = k.infra.CmdAsync(master0, nil, "rm -rf .kube/config && mkdir -p /root/.kube && cp /etc/kubernetes/admin.conf /root/.kube/config"); err != nil { 176 return v1beta3.BootstrapTokenDiscovery{}, "", err 177 } 178 179 token, certKey := k.decodeMaster0Output(output) 180 181 return token, certKey, nil 182 } 183 184 // decode output to join token hash and key 185 func (k *Runtime) decodeMaster0Output(output []byte) (v1beta3.BootstrapTokenDiscovery, string) { 186 s0 := string(output) 187 logrus.Debugf("decodeOutput: %s", s0) 188 slice := strings.Split(s0, "kubeadm join") 189 slice1 := strings.Split(slice[1], "Please note") 190 logrus.Infof("join command is: kubeadm join %s", slice1[0]) 191 192 return k.decodeJoinCmd(slice1[0]) 193 } 194 195 // 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 --experimental-control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07 196 func (k *Runtime) decodeJoinCmd(cmd string) (v1beta3.BootstrapTokenDiscovery, string) { 197 logrus.Debugf("[globals]decodeJoinCmd: %s", cmd) 198 stringSlice := strings.Split(cmd, " ") 199 200 token := v1beta3.BootstrapTokenDiscovery{} 201 var certKey string 202 203 for i, r := range stringSlice { 204 // upstream error, delete \t, \\, \n, space. 205 r = strings.ReplaceAll(r, "\t", "") 206 r = strings.ReplaceAll(r, "\n", "") 207 r = strings.ReplaceAll(r, "\\", "") 208 r = strings.TrimSpace(r) 209 if strings.Contains(r, "--token") { 210 token.Token = stringSlice[i+1] 211 } 212 if strings.Contains(r, "--discovery-token-ca-cert-hash") { 213 token.CACertHashes = []string{stringSlice[i+1]} 214 } 215 if strings.Contains(r, "--certificate-key") { 216 certKey = stringSlice[i+1][:64] 217 } 218 } 219 220 return token, certKey 221 } 222 223 // initKube do some initialize kubelet works, such as configuring the host environment, initializing the kubelet service, and so on. 224 func (k *Runtime) initKube(hosts []net.IP) error { 225 initKubeletCmd := fmt.Sprintf("cd %s && export RegistryURL=%s && bash %s", filepath.Join(k.infra.GetClusterRootfsPath(), "scripts"), k.Config.RegistryInfo.URL, "init-kube.sh") 226 eg, _ := errgroup.WithContext(context.Background()) 227 for _, h := range hosts { 228 host := h 229 eg.Go(func() error { 230 if err := k.infra.CmdAsync(host, nil, initKubeletCmd); err != nil { 231 return fmt.Errorf("failed to init Kubelet Service on (%s): %s", host, err.Error()) 232 } 233 return nil 234 }) 235 } 236 if err := eg.Wait(); err != nil { 237 return err 238 } 239 return nil 240 } 241 242 func (k *Runtime) sendClusterCert(hosts []net.IP) error { 243 f := func(host net.IP) error { 244 if err := k.infra.Copy(host, k.getPKIPath(), clustercert.KubeDefaultCertPath); err != nil { 245 return fmt.Errorf("failed to copy cluster cert: %v", err) 246 } 247 if err := k.infra.Copy(host, k.getPKIPath(), k.getPKIPath()); err != nil { 248 return fmt.Errorf("failed to copy cluster cert: %v", err) 249 } 250 return nil 251 } 252 253 return k.infra.Execute(hosts, f) 254 } 255 256 func (k *Runtime) sendKubeadmFile(hosts []net.IP) error { 257 f := func(host net.IP) error { 258 if err := k.infra.Copy(host, path.Join(k.infra.GetClusterRootfsPath(), "kubeadm.yaml"), path.Join(k.infra.GetClusterRootfsPath(), "kubeadm.yaml")); err != nil { 259 return fmt.Errorf("failed to copy kubeadm file: %v", err) 260 } 261 return nil 262 } 263 264 return k.infra.Execute(hosts, f) 265 } 266 267 func (k *Runtime) sendKubeConfigFilesToMaster(masters []net.IP, files ...string) error { 268 for _, kubeFile := range files { 269 src := filepath.Join(k.infra.GetClusterRootfsPath(), kubeFile) 270 dest := filepath.Join(clustercert.KubernetesConfigDir, kubeFile) 271 272 f := func(host net.IP) error { 273 if err := k.infra.Copy(host, src, dest); err != nil { 274 return fmt.Errorf("failed to copy cluster kubeconfig file : %v", err) 275 } 276 if err := k.infra.Copy(host, src, src); err != nil { 277 return fmt.Errorf("failed to copy cluster kubeconfig file : %v", err) 278 } 279 return nil 280 } 281 if err := k.infra.Execute(masters, f); err != nil { 282 return err 283 } 284 } 285 286 return nil 287 } 288 289 func (k *Runtime) getJoinTokenHashAndKey(master0 net.IP) (v1beta3.BootstrapTokenDiscovery, string, error) { 290 cmd := fmt.Sprintf(`kubeadm init phase upload-certs --upload-certs -v %d`, k.Config.Vlog) 291 292 output, err := k.infra.CmdToString(master0, nil, cmd, "\r\n") 293 if err != nil { 294 return v1beta3.BootstrapTokenDiscovery{}, "", err 295 } 296 logrus.Debugf("[globals]decodeCertCmd: %s", output) 297 slice := strings.Split(output, "Using certificate key:") 298 if len(slice) != 2 { 299 return v1beta3.BootstrapTokenDiscovery{}, "", fmt.Errorf("failed to get certifacate key: %s", slice) 300 } 301 key := strings.Replace(slice[1], "\r\n", "", -1) 302 certKey := strings.Replace(key, "\n", "", -1) 303 304 cmd = fmt.Sprintf("kubeadm token create --print-join-command -v %d", k.Config.Vlog) 305 306 out, err := k.infra.Cmd(master0, nil, cmd) 307 if err != nil { 308 return v1beta3.BootstrapTokenDiscovery{}, "", fmt.Errorf("failed to create kubeadm join token: %v", err) 309 } 310 311 token, certKey2 := k.decodeMaster0Output(out) 312 313 if certKey == "" { 314 certKey = certKey2 315 } 316 317 return token, certKey, nil 318 }