github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/runtime/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 runtime 16 17 import ( 18 "context" 19 "fmt" 20 "path/filepath" 21 "strings" 22 23 "github.com/alibaba/sealer/common" 24 "github.com/alibaba/sealer/logger" 25 "github.com/alibaba/sealer/pkg/cert" 26 v2 "github.com/alibaba/sealer/types/api/v2" 27 "github.com/alibaba/sealer/utils" 28 "golang.org/x/sync/errgroup" 29 ) 30 31 const ( 32 RemoteCmdCopyStatic = "mkdir -p %s && cp -f %s %s" 33 RemoteApplyYaml = `echo '%s' | kubectl apply -f -` 34 RemoteCmdGetNetworkInterface = "ls /sys/class/net" 35 RemoteCmdExistNetworkInterface = "ip addr show %s | egrep \"%s\" || true" 36 WriteKubeadmConfigCmd = `cd %s && echo '%s' > etc/kubeadm.yml` 37 DefaultVIP = "10.103.97.2" 38 DefaultAPIserverDomain = "apiserver.cluster.local" 39 DefaultRegistryPort = 5000 40 DockerCertDir = "/etc/docker/certs.d" 41 ) 42 43 func (k *KubeadmRuntime) ConfigKubeadmOnMaster0() error { 44 if err := k.LoadFromClusterfile(k.Config.ClusterFileKubeConfig); err != nil { 45 return fmt.Errorf("failed to load kubeadm config from clusterfile: %v", err) 46 } 47 // TODO handle the kubeadm config, like kubeproxy config 48 k.handleKubeadmConfig() 49 if err := k.KubeadmConfig.Merge(k.getDefaultKubeadmConfig()); err != nil { 50 return err 51 } 52 bs, err := k.generateConfigs() 53 if err != nil { 54 return err 55 } 56 cmd := fmt.Sprintf(WriteKubeadmConfigCmd, k.getRootfs(), string(bs)) 57 sshClient, err := k.getHostSSHClient(k.GetMaster0IP()) 58 if err != nil { 59 return err 60 } 61 return sshClient.CmdAsync(k.GetMaster0IP(), cmd) 62 } 63 64 func (k *KubeadmRuntime) generateConfigs() ([]byte, error) { 65 //getCgroupDriverFromShell need get CRISocket, so after merge 66 cGroupDriver, err := k.getCgroupDriverFromShell(k.GetMaster0IP()) 67 if err != nil { 68 return nil, err 69 } 70 k.setCgroupDriver(cGroupDriver) 71 k.setKubeadmAPIVersion() 72 return utils.MarshalYamlConfigs(&k.InitConfiguration, 73 &k.ClusterConfiguration, 74 &k.KubeletConfiguration, 75 &k.KubeProxyConfiguration) 76 } 77 78 func (k *KubeadmRuntime) handleKubeadmConfig() { 79 //The configuration set here does not require merge 80 k.setInitAdvertiseAddress(k.GetMaster0IP()) 81 k.setControlPlaneEndpoint(fmt.Sprintf("%s:6443", k.getAPIServerDomain())) 82 if k.APIServer.ExtraArgs == nil { 83 k.APIServer.ExtraArgs = make(map[string]string) 84 } 85 k.APIServer.ExtraArgs[EtcdServers] = getEtcdEndpointsWithHTTPSPrefix(k.GetMasterIPList()) 86 k.IPVS.ExcludeCIDRs = append(k.KubeProxyConfiguration.IPVS.ExcludeCIDRs, fmt.Sprintf("%s/32", k.getVIP())) 87 } 88 89 //CmdToString is in host exec cmd and replace to spilt str 90 func (k *KubeadmRuntime) CmdToString(host, cmd, split string) (string, error) { 91 ssh, err := k.getHostSSHClient(host) 92 if err != nil { 93 return "", fmt.Errorf("failed to get host ssh client, %s %v", cmd, err) 94 } 95 data, err := ssh.Cmd(host, cmd) 96 if err != nil { 97 return "", fmt.Errorf("exec remote cmd failed, %s %v", cmd, err) 98 } 99 if data != nil { 100 str := string(data) 101 str = strings.ReplaceAll(str, "\r\n", split) 102 str = strings.ReplaceAll(str, "\n", split) 103 return str, nil 104 } 105 return "", nil 106 } 107 108 func (k *KubeadmRuntime) getRemoteHostName(hostIP string) (string, error) { 109 hostName, err := k.CmdToString(hostIP, "hostname", "") 110 if err != nil { 111 return "", err 112 } 113 if hostName == "" { 114 return "", fmt.Errorf("get remote hostname failed %s", hostIP) 115 } 116 return strings.ToLower(hostName), nil 117 } 118 119 func (k *KubeadmRuntime) GenerateCert() error { 120 hostName, err := k.getRemoteHostName(k.GetMaster0IP()) 121 if err != nil { 122 return err 123 } 124 err = cert.GenerateCert( 125 k.getPKIPath(), 126 k.getEtcdCertPath(), 127 k.getCertSANS(), 128 k.GetMaster0IP(), 129 hostName, 130 k.getSvcCIDR(), 131 k.getDNSDomain(), 132 ) 133 if err != nil { 134 return fmt.Errorf("generate certs failed %v", err) 135 } 136 err = k.sendNewCertAndKey(k.GetMasterIPList()[:1]) 137 if err != nil { 138 return err 139 } 140 return k.GenerateRegistryCert() 141 } 142 143 func (k *KubeadmRuntime) GenerateRegistryCert() error { 144 err := GenerateRegistryCert(k.getCertsDir(), k.RegConfig.Domain) 145 if err != nil { 146 return err 147 } 148 err = k.sendRegistryCertAndKey() 149 if err != nil { 150 return err 151 } 152 return k.sendRegistryCert(k.GetMasterIPList()[:1]) 153 } 154 155 func (k *KubeadmRuntime) CreateKubeConfig() error { 156 hostname, err := k.getRemoteHostName(k.GetMaster0IP()) 157 if err != nil { 158 return err 159 } 160 certConfig := cert.Config{ 161 Path: k.getPKIPath(), 162 BaseName: "ca", 163 } 164 165 controlPlaneEndpoint := fmt.Sprintf("https://%s:6443", k.getAPIServerDomain()) 166 err = cert.CreateJoinControlPlaneKubeConfigFiles(k.getBasePath(), 167 certConfig, hostname, controlPlaneEndpoint, "kubernetes") 168 if err != nil { 169 return fmt.Errorf("generator kubeconfig failed %s", err) 170 } 171 return nil 172 } 173 174 func (k *KubeadmRuntime) CopyStaticFiles(nodes []string) error { 175 for _, file := range MasterStaticFiles { 176 staticFilePath := filepath.Join(k.getStaticFileDir(), file.Name) 177 cmdLinkStatic := fmt.Sprintf(RemoteCmdCopyStatic, file.DestinationDir, staticFilePath, filepath.Join(file.DestinationDir, file.Name)) 178 eg, _ := errgroup.WithContext(context.Background()) 179 for _, host := range nodes { 180 host := host 181 eg.Go(func() error { 182 ssh, err := k.getHostSSHClient(host) 183 if err != nil { 184 return fmt.Errorf("new ssh client failed %v", err) 185 } 186 err = ssh.CmdAsync(host, cmdLinkStatic) 187 if err != nil { 188 return fmt.Errorf("[%s] link static file failed, error:%s", host, err.Error()) 189 } 190 return err 191 }) 192 } 193 if err := eg.Wait(); err != nil { 194 return err 195 } 196 } 197 return nil 198 } 199 200 //decode output to join token hash and key 201 func (k *KubeadmRuntime) decodeMaster0Output(output []byte) { 202 s0 := string(output) 203 logger.Debug("decodeOutput: %s", s0) 204 slice := strings.Split(s0, "kubeadm join") 205 slice1 := strings.Split(slice[1], "Please note") 206 logger.Info("join command is: kubeadm join %s", slice1[0]) 207 k.decodeJoinCmd(slice1[0]) 208 } 209 210 // 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 --experimental-control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07 211 func (k *KubeadmRuntime) decodeJoinCmd(cmd string) { 212 logger.Debug("[globals]decodeJoinCmd: %s", cmd) 213 stringSlice := strings.Split(cmd, " ") 214 215 for i, r := range stringSlice { 216 // upstream error, delete \t, \\, \n, space. 217 r = strings.ReplaceAll(r, "\t", "") 218 r = strings.ReplaceAll(r, "\n", "") 219 r = strings.ReplaceAll(r, "\\", "") 220 r = strings.TrimSpace(r) 221 if strings.Contains(r, "--token") { 222 k.setJoinToken(stringSlice[i+1]) 223 } 224 if strings.Contains(r, "--discovery-token-ca-cert-hash") { 225 k.setTokenCaCertHash([]string{stringSlice[i+1]}) 226 } 227 if strings.Contains(r, "--certificate-key") { 228 k.setInitCertificateKey(stringSlice[i+1][:64]) 229 } 230 } 231 logger.Debug("joinToken: %v\nTokenCaCertHash: %v\nCertificateKey: %v", k.getJoinToken(), k.getTokenCaCertHash(), k.getCertificateKey()) 232 } 233 234 //InitMaster0 is 235 func (k *KubeadmRuntime) InitMaster0() error { 236 ssh, err := k.getHostSSHClient(k.GetMaster0IP()) 237 if err != nil { 238 return fmt.Errorf("failed to get master0 ssh client, %v", err) 239 } 240 241 if err := k.SendJoinMasterKubeConfigs([]string{k.GetMaster0IP()}, AdminConf, ControllerConf, SchedulerConf, KubeletConf); err != nil { 242 return err 243 } 244 apiServerHost := getAPIServerHost(k.GetMaster0IP(), k.getAPIServerDomain()) 245 cmdAddEtcHost := fmt.Sprintf(RemoteAddEtcHosts, apiServerHost, apiServerHost) 246 err = ssh.CmdAsync(k.GetMaster0IP(), cmdAddEtcHost) 247 if err != nil { 248 return err 249 } 250 251 logger.Info("start to init master0...") 252 cmdInit := k.Command(k.getKubeVersion(), InitMaster) 253 254 // TODO skip docker version error check for test 255 output, err := ssh.Cmd(k.GetMaster0IP(), cmdInit) 256 if err != nil { 257 _, wErr := common.StdOut.WriteString(string(output)) 258 if wErr != nil { 259 return err 260 } 261 return fmt.Errorf("init master0 failed, error: %s. Please clean and reinstall", err.Error()) 262 } 263 k.decodeMaster0Output(output) 264 err = ssh.CmdAsync(k.GetMaster0IP(), RemoteCopyKubeConfig) 265 if err != nil { 266 return err 267 } 268 269 return nil 270 } 271 272 func (k *KubeadmRuntime) GetKubectlAndKubeconfig() error { 273 if utils.IsFileExist(common.DefaultKubeConfigFile()) { 274 return nil 275 } 276 ssh, err := k.getHostSSHClient(k.GetMaster0IP()) 277 if err != nil { 278 return fmt.Errorf("failed to get master0 ssh client when get kubbectl and kubeconfig %v", err) 279 } 280 281 return GetKubectlAndKubeconfig(ssh, k.GetMaster0IP(), k.getImageMountDir()) 282 } 283 284 func (k *KubeadmRuntime) CopyStaticFilesTomasters() error { 285 return k.CopyStaticFiles(k.GetMasterIPList()) 286 } 287 288 func (k *KubeadmRuntime) init(cluster *v2.Cluster) error { 289 pipeline := []func() error{ 290 k.ConfigKubeadmOnMaster0, 291 k.GenerateCert, 292 k.CreateKubeConfig, 293 k.CopyStaticFilesTomasters, 294 k.ApplyRegistry, 295 k.InitMaster0, 296 k.GetKubectlAndKubeconfig, 297 } 298 299 for _, f := range pipeline { 300 if err := f(); err != nil { 301 return fmt.Errorf("failed to init master0 %v", err) 302 } 303 } 304 305 return nil 306 }