From 8d94bb910c55d0da24c641bf38c2b0e5d8950f2c Mon Sep 17 00:00:00 2001 From: cheliequan <liequanche@126.com> Date: 星期三, 24 五月 2023 15:20:11 +0800 Subject: [PATCH] 增加自动安装rancher、docker和kubectl和创建集群相关函数 --- src/cluster/cluster.go | 545 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 545 insertions(+), 0 deletions(-) diff --git a/src/cluster/cluster.go b/src/cluster/cluster.go new file mode 100644 index 0000000..68686a0 --- /dev/null +++ b/src/cluster/cluster.go @@ -0,0 +1,545 @@ +package main + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "strings" + "time" + + "golang.org/x/crypto/ssh" +) + +type Cluster struct { + ID string `json:"id"` + Name string `json:"name"` +} + +type ClustersResponse struct { + Data []Cluster `json:"data"` +} + +type RegistrationTokenResponse struct { + Command string `json:"command"` +} + +type NodeCommandResponse struct { + NodeCommand string `json:"nodeCommand"` +} + +func createRegistrationToken(serverURL, bearerToken, clusterID string) (string, error) { + url := fmt.Sprintf("%s/v3/clusterregistrationtokens", serverURL) + + payload := strings.NewReader(fmt.Sprintf(`{ + "clusterId": "%s" + }`, clusterID)) + + req, err := http.NewRequest("POST", url, payload) + if err != nil { + return "", fmt.Errorf("failed to create registration token request: %v", err) + } + + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", bearerToken)) + req.Header.Set("Content-Type", "application/json") + + client := createHTTPClient() + + resp, err := client.Do(req) + if err != nil { + return "", fmt.Errorf("failed to create registration token: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return "", fmt.Errorf("failed to create registration token: unexpected status code %d", resp.StatusCode) + } + + var tokenResp RegistrationTokenResponse + err = json.NewDecoder(resp.Body).Decode(&tokenResp) + if err != nil { + return "", fmt.Errorf("failed to decode registration token response: %v", err) + } + + return tokenResp.Command, nil +} + +func getNodeCommand(serverURL, bearerToken, clusterID string) (string, error) { + url := fmt.Sprintf("%s/v3/clusterregistrationtokens", serverURL) + + payload := map[string]interface{}{ + "clusterId": clusterID, + } + + payloadBytes, err := json.Marshal(payload) + if err != nil { + return "", fmt.Errorf("failed to marshal payload: %v", err) + } + + req, err := http.NewRequest("POST", url, bytes.NewReader(payloadBytes)) + if err != nil { + return "", fmt.Errorf("failed to create node command request: %v", err) + } + + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", bearerToken)) + req.Header.Set("Content-Type", "application/json") + + client := createHTTPClient() + + resp, err := client.Do(req) + if err != nil { + return "", fmt.Errorf("failed to get node command: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return "", fmt.Errorf("failed to get node command: unexpected status code %d", resp.StatusCode) + } + + var nodeCommandResponse NodeCommandResponse + err = json.NewDecoder(resp.Body).Decode(&nodeCommandResponse) + if err != nil { + return "", fmt.Errorf("failed to decode node command response: %v", err) + } + + return nodeCommandResponse.NodeCommand, nil +} + +func getClusterID(serverURL, bearerToken, clusterName string) (string, error) { + url := fmt.Sprintf("%s/v3/clusters", serverURL) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return "", fmt.Errorf("failed to create cluster list request: %v", err) + } + + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", bearerToken)) + + client := createHTTPClient() + + resp, err := client.Do(req) + if err != nil { + return "", fmt.Errorf("failed to get cluster list: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("failed to get cluster list: unexpected status code %d", resp.StatusCode) + } + + // Parse the API response + var clustersResponse ClustersResponse + err = json.NewDecoder(resp.Body).Decode(&clustersResponse) + if err != nil { + return "", fmt.Errorf("failed to decode cluster list response: %v", err) + } + + // Print cluster names + for _, cluster := range clustersResponse.Data { + if cluster.Name == clusterName { + fmt.Printf("Cluster ID: %s, Name: %s\n", cluster.ID, cluster.Name) + return cluster.ID, nil + } + } + + return "", fmt.Errorf("cluster '%s' not found", clusterName) +} + +// Create an HTTP client with insecure TLS configuration +func createHTTPClient() *http.Client { + transport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + return &http.Client{Transport: transport} +} + +func sshExec(nodeIP, sshUsername, sshPassword, remoteSSHCommand string, sshPort int) (string, error) { + // SSH 杩炴帴閰嶇疆 + config := &ssh.ClientConfig{ + User: sshUsername, + Auth: []ssh.AuthMethod{ + ssh.Password(sshPassword), + }, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + + // 杩炴帴鍒拌繙绋嬫湇鍔″櫒 + client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", nodeIP, sshPort), config) + if err != nil { + return "", fmt.Errorf("failed to connect to node %s: %v", nodeIP, err) + } + defer client.Close() + + // 鍒涘缓浼氳瘽 + session, err := client.NewSession() + if err != nil { + return "", fmt.Errorf("failed to create SSH session: %v", err) + } + defer session.Close() + + // 鍒涘缓涓�涓叿鏈夎秴鏃剁殑涓婁笅鏂� + ctx, cancel := context.WithTimeout(context.Background(), 300*time.Second) + defer cancel() + + // 閫氳繃浼氳瘽鎵ц杩滅▼鍛戒护 + outputChan := make(chan string) + errorChan := make(chan error) + go func() { + // 鍒涘缓涓�涓繛鎺ユ爣鍑嗚緭鍏ョ殑绠¢亾 + stdinPipe, err := session.StdinPipe() + if err != nil { + errorChan <- fmt.Errorf("failed to create stdin pipe: %v", err) + return + } + + // 鍚姩浼氳瘽 + if err := session.Start(fmt.Sprintf("sudo -SE %s", remoteSSHCommand)); err != nil { + errorChan <- fmt.Errorf("failed to start command: %v err锛�%v", remoteSSHCommand, err) + return + } + + // 灏嗗瘑鐮佸啓鍏ユ爣鍑嗚緭鍏ョ閬� + _, err = fmt.Fprintf(stdinPipe, "%s\n", sshPassword) + if err != nil { + errorChan <- fmt.Errorf("failed to write password to stdin: %v", err) + return + } + + // 绛夊緟浼氳瘽缁撴潫 + if err := session.Wait(); err != nil { + errorChan <- fmt.Errorf("command execution failed: %v err锛�%v", remoteSSHCommand, err) + return + } + + outputChan <- "" + }() + + // 绛夊緟缁撴灉鎴栬秴鏃� + select { + case <-ctx.Done(): + // 鍏抽棴浼氳瘽浠ョ粓姝㈣繙绋嬪懡浠� + session.Close() + // 绛夊緟浼氳瘽鍏抽棴鐨� goroutine 缁撴潫 + <-outputChan + return "", fmt.Errorf("SSH command execution timed out") + case err := <-errorChan: + return "", err + case <-outputChan: + fmt.Printf("Command: %v executed on the remote server: %s\n", remoteSSHCommand, nodeIP) + return "", nil + } +} + +// 瀹夎Docker +func installDocker(nodeIP, sshUsername, sshPassword string, sshPort int) error { + // 妫�鏌ocker鏄惁宸插畨瑁� + checkCommand := "which docker" + _, err := sshExec(nodeIP, sshUsername, sshPassword, checkCommand, sshPort) + if err == nil { + fmt.Println("Docker is already installed on the remote server.") + return nil + } + + // 瀹夎Docker + installCommand := "sudo curl -fsSL https://get.docker.com -o get-docker.sh && sudo sh get-docker.sh" + _, err = sshExec(nodeIP, sshUsername, sshPassword, installCommand, sshPort) + if err != nil { + return fmt.Errorf("failed to install Docker on the remote server: %v", err) + } + + fmt.Println("Docker has been installed on the remote server.") + return nil +} + +// 瀹夎kubectl +func installKubectl(nodeIP, sshUsername, sshPassword string, sshPort int) error { + // 妫�鏌ubectl鏄惁宸插畨瑁� + checkCommand := "which kubectl" + _, err := sshExec(nodeIP, sshUsername, sshPassword, checkCommand, sshPort) + if err == nil { + fmt.Println("kubectl is already installed on the remote server.") + return nil + } + + // 瀹夎kubectl + installCommand := "sudo curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && sudo chmod +x kubectl && sudo mv kubectl /usr/local/bin/" + _, err = sshExec(nodeIP, sshUsername, sshPassword, installCommand, sshPort) + if err != nil { + return fmt.Errorf("failed to install kubectl on the remote server: %v", err) + } + + fmt.Println("kubectl has been installed on the remote server.") + return nil +} + +type Node struct { + ClusterName string `json:"clusterName"` + Roles []string `json:"roles"` + IP string `json:"ip"` + SSHUsername string `json:"sshUsername"` + SSHPassword string `json:"sshPassword"` + SSHPort int `json:"sshPort"` +} + +type RancherConfig struct { + RancherURL string `json:"rancherURL"` + BearerToken string `json:"bearerToken"` +} + +type ClusterCreateRequest struct { + Name string `json:"name"` + Driver string `json:"driver"` + ControlPlaneProvider string `json:"controlPlaneProvider"` + WorkerNodeProvider string `json:"workerNodeProvider"` + RancherKubernetesEngineConfig interface{} `json:"rancherKubernetesEngineConfig"` + // Add other cluster properties as needed +} + +type ClusterCreateResponse struct { + ID string `json:"id"` + Name string `json:"name"` + // Add other response fields as needed +} + +func createClusterData(clusterName string) []byte { + requestBody := ClusterCreateRequest{ + Name: clusterName, + Driver: "custom", + ControlPlaneProvider: "external", + WorkerNodeProvider: "external", + RancherKubernetesEngineConfig: map[string]interface{}{ + "ignoreDockerVersion": true, + "kubernetesVersion": "v1.20.15-rancher2-1", + // Add other RKE config properties as needed + }, + // Add other cluster properties as needed + } + + requestBodyBytes, err := json.Marshal(requestBody) + if err != nil { + log.Fatalf("Failed to marshal request body: %v", err) + } + + return requestBodyBytes +} + +// Deploy Kubernetes roles on a node using SSH +func deployk8sRolesOnNode(nodeIP, sshUsername, sshPassword, remoteSSHCommand string, sshPort int, roles []string) error { + rancherAgentInstalled, err := isRancherAgentInstalled(nodeIP, sshUsername, sshPassword, sshPort) + if err == nil { + return nil + } + + if !rancherAgentInstalled { + // Add role parameters to nodeCommand + for _, role := range roles { + switch role { + case "etcd": + remoteSSHCommand += " --etcd" + case "controlplane": + remoteSSHCommand += " --controlplane" + case "worker": + remoteSSHCommand += " --worker" + default: + log.Fatalf("invalid role specified : %s", role) + } + } + + _, err := sshExec(nodeIP, sshUsername, sshPassword, remoteSSHCommand, sshPort) + if err != nil { + return fmt.Errorf("failed to deploy Kubernetes roles on the remote server: %v", err) + } + return nil +} + +func isRancherInstalled(ip, username, password string, sshPort int) (bool, error) { + // 妫�鏌ancher瀹瑰櫒鏄惁宸茶繍琛� + checkRancherCommand := "sudo docker ps --format '{{.Image}}' | grep -q rancher/rancher:v2.5.17" + _, err := sshExec(ip, username, password, checkRancherCommand, sshPort) + if err != nil { + // 濡傛灉鎵ц鍛戒护鍑洪敊锛屽垯璇存槑Rancher鏈畨瑁呮垨鍙戠敓鍏朵粬閿欒 + return false, fmt.Errorf("failed to check Rancher installation: %v", err) + } + + return true, nil +} + +func isRancherAgentInstalled(ip, username, password string, sshPort int) (bool, error) { + // 妫�鏌ancher瀹瑰櫒鏄惁宸茶繍琛� + checkRancherCommand := "sudo docker ps --format '{{.Image}}' | grep -q rancher/rancher-agent:v2.5." + _, err := sshExec(ip, username, password, checkRancherCommand, sshPort) + if err != nil { + // 濡傛灉鎵ц鍛戒护鍑洪敊锛屽垯璇存槑Rancher鏈畨瑁呮垨鍙戠敓鍏朵粬閿欒 + return false, fmt.Errorf("failed to check Rancher installation: %v", err) + } + + return true, nil +} + +func installRancher(ip, username, password string, sshPort int) error { + rancherInstalled, err := isRancherInstalled(ip, username, password, sshPort) + if err == nil { + return nil + } + + if !rancherInstalled { + // 瀹夎Rancher鍛戒护 + rancherCommand := "sudo docker run --privileged -d --restart=unless-stopped -p 8081:80 -p 8443:443 -v /opt/rancher:/var/lib/rancher registry.cn-hangzhou.aliyuncs.com/rancher/rancher:v2.5.17" + _, err = sshExec(ip, username, password, rancherCommand, sshPort) + if err != nil { + return fmt.Errorf("failed to install Rancher: %v", err) + } + } else { + fmt.Println("Rancher is already installed on the remote server.") + } + + return nil +} + +func installDockerAndRancher(ip, username, password string, sshPort int) error { + // 瀹夎Docker鍛戒护 + err := installDocker(ip, username, password, sshPort) + if err != nil { + return err + } + + // 瀹夎Rancher鍛戒护 + err = installRancher(ip, username, password, sshPort) + if err != nil { + return err + } + + return nil +} + +func createCluster(rancherConfig RancherConfig, clusterName string) error { + requestBody := createClusterData(clusterName) + + url := fmt.Sprintf("%s/v3/clusters", rancherConfig.RancherURL) + req, err := http.NewRequest("POST", url, bytes.NewBuffer(requestBody)) + if err != nil { + return fmt.Errorf("Failed to create HTTP request: %v", err) + } + + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", rancherConfig.BearerToken)) + req.Header.Set("Content-Type", "application/json") + + client := createHTTPClient() + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("Failed to send HTTP request: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("Failed to create cluster, status code: %d", resp.StatusCode) + } + + var responseBody ClusterCreateResponse + err = json.NewDecoder(resp.Body).Decode(&responseBody) + if err != nil { + return fmt.Errorf("Failed to decode response body: %v", err) + } + + fmt.Printf("Cluster created: ID=%s, Name=%s\n", responseBody.ID, responseBody.Name) + + return nil +} + +func main() { + clusterName := "kubernetus" + nodes := []Node{ + { + ClusterName: clusterName, + Roles: []string{"etcd", "controlplane", "worker"}, + IP: "192.168.20.189", + SSHUsername: "basic", + SSHPassword: "123", + SSHPort: 22, + }, + { + ClusterName: clusterName, + Roles: []string{"worker"}, + IP: "192.168.20.10", + SSHUsername: "basic", + SSHPassword: "123", + SSHPort: 22, + }, + { + ClusterName: clusterName, + Roles: []string{"worker"}, + IP: "192.168.20.115", + SSHUsername: "basic", + SSHPassword: "alien123", + SSHPort: 22, + }, + // Add more nodes here if needed + } + + //install rancher on master node + err := installDockerAndRancher(nodes[0].IP, nodes[0].SSHUsername, nodes[0].SSHPassword, nodes[0].SSHPort) + if err != nil { + log.Fatalf("Failed to install Rancher: %v", err) + } + + // Create the cluster + // Rancher configuration + /*rancherConfig := RancherConfig{ + RancherURL: "https://192.168.20.119:8443", + BearerToken: "token-nnrsc:w68zdt8s47fnpjd5xqdl5hhzpz4j2d56kt5nx49nsswcbpdzc28kh5", + }*/ + + rancherConfig := RancherConfig{ + RancherURL: "https://192.168.20.189:8443", + BearerToken: "token-t4cdf:h7zhmbvbzdvd9mmjw8zmt8rh4m7rl5gtqpqljlhl9tlr2z26j9lf4l", + } + + // Deploy clusterId + clusterID, err := getClusterID(rancherConfig.RancherURL, rancherConfig.BearerToken, clusterName) + if err != nil { + log.Fatal(err) + err = createCluster(rancherConfig, clusterName) + if err != nil { + log.Fatalf("Failed to create cluster: %v", err) + } + fmt.Printf("Cluster created: %s\n", clusterName) + clusterID, err = getClusterID(rancherConfig.RancherURL, rancherConfig.BearerToken, clusterName) + if err != nil { + log.Fatal(err) + } + } + fmt.Println(clusterID) + + // Deploy nodeCommand + nodeCommand, err := getNodeCommand(rancherConfig.RancherURL, rancherConfig.BearerToken, clusterID) + if err != nil { + log.Fatal(err) + } + fmt.Println(nodeCommand) + + for _, node := range nodes { + //Deploy Docker on each node + err = installDocker(node.IP, node.SSHUsername, node.SSHPassword, node.SSHPort) + if err != nil { + log.Fatal(err) + } + + // Deploy Kubectl on each node + err = installKubectl(node.IP, node.SSHUsername, node.SSHPassword, node.SSHPort) + if err != nil { + log.Fatal(err) + } + + // Deploy Kubernetes roles on each node + err = deployk8sRolesOnNode(node.IP, node.SSHUsername, node.SSHPassword, nodeCommand, node.SSHPort, node.Roles) + if err != nil { + log.Fatal(err) + } + } + os.Exit(0) +} -- Gitblit v1.8.0