如何用Go创建一个 deployment 到容器拉起来的全流程
整体流程概述:
- 认证与配置: 连接到Kubernetes集群。
- 创建Deployment对象: 构建一个
appsv1.Deployment
结构体,定义Pod的模板、副本数量、选择器等。 - 发送创建请求: 使用Kubernetes Go客户端库将Deployment对象发送到API服务器。
- 监控Deployment状态: 持续检查Deployment的状态,直到它达到预期的副本数量并且所有Pod都已准备就绪。
详细步骤与Go语言代码示例:
首先,您需要导入必要的Kubernetes Go客户端库:
package mainimport ("context""fmt""time"appsv1 "k8s.io/api/apps/v1"apiv1 "k8s.io/api/core/v1"metav1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/client-go/kubernetes""k8s.io/client-go/tools/clientcmd"
)
1. 认证与配置
Go客户端库可以通过多种方式连接到Kubernetes集群:
- 集群内(In-cluster): 如果您的Go应用程序运行在Kubernetes集群内部的Pod中,它会自动使用Service Account凭证。
- 集群外(Out-of-cluster): 如果您的Go应用程序运行在集群外部(例如本地开发环境),它通常会使用kubeconfig文件。
以下示例展示了如何使用kubeconfig文件进行配置:
func getKubeConfig() (*kubernetes.Clientset, error) {// 尝试从默认路径或KUBECONFIG环境变量加载kubeconfig// 您也可以指定一个具体的kubeconfig文件路径kubeconfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(),&clientcmd.ConfigOverrides{},)config, err := kubeconfig.ClientConfig()if err != nil {return nil, fmt.Errorf("加载kubeconfig失败: %w", err)}clientset, err := kubernetes.NewForConfig(config)if err != nil {return nil, fmt.Errorf("创建Kubernetes客户端失败: %w", err)}return clientset, nil
}
2. 创建Deployment对象
这是一个Go语言结构体,定义了Deployment的各种属性,包括Pod的模板、副本数量等。
func createDeploymentObject(namespace, deploymentName, imageName string, replicas int32) *appsv1.Deployment {labels := map[string]string{"app": "my-app",}return &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: deploymentName,Namespace: namespace,},Spec: appsv1.DeploymentSpec{Replicas: &replicas, // 副本数量Selector: &metav1.LabelSelector{MatchLabels: labels,},Template: apiv1.PodTemplateSpec{ObjectMeta: metav1.ObjectMeta{Labels: labels,},Spec: apiv1.PodSpec{Containers: []apiv1.Container{{Name: "my-container",Image: imageName,Ports: []apiv1.ContainerPort{{ContainerPort: 80,},},},},},},},}
}
3. 发送创建请求
使用clientset.AppsV1().Deployments(namespace).Create()
方法向Kubernetes API服务器发送创建Deployment的请求。
func createDeployment(clientset *kubernetes.Clientset, deployment *appsv1.Deployment) (*appsv1.Deployment, error) {fmt.Printf("尝试创建Deployment: %s\n", deployment.Name)ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) // 设置超时defer cancel()result, err := clientset.AppsV1().Deployments(deployment.Namespace).Create(ctx, deployment, metav1.CreateOptions{})if err != nil {return nil, fmt.Errorf("创建Deployment失败: %w", err)}fmt.Printf("Deployment %s 创建成功\n", result.Name)return result, nil
}
4. 监控Deployment状态
创建Deployment后,Kubernetes Controller Manager会负责创建Pod并确保它们运行起来。我们需要监控Deployment的状态,直到所有预期的Pod都已启动并准备就绪。
func waitForDeploymentReady(clientset *kubernetes.Clientset, namespace, deploymentName string, desiredReplicas int32, timeout time.Duration) error {fmt.Printf("等待Deployment %s 准备就绪...\n", deploymentName)ctx, cancel := context.WithTimeout(context.Background(), timeout)defer cancel()ticker := time.NewTicker(5 * time.Second) // 每5秒检查一次defer ticker.Stop()for {select {case <-ctx.Done():return fmt.Errorf("等待Deployment %s 超时", deploymentName)case <-ticker.C:deployment, err := clientset.AppsV1().Deployments(namespace).Get(ctx, deploymentName, metav1.GetOptions{})if err != nil {return fmt.Errorf("获取Deployment %s 状态失败: %w", deploymentName, err)}// 检查ReadyReplicas是否达到预期if deployment.Status.ReadyReplicas == desiredReplicas &&deployment.Status.AvailableReplicas == desiredReplicas &&deployment.Status.UpdatedReplicas == desiredReplicas {fmt.Printf("Deployment %s 准备就绪!(副本数: %d)\n", deploymentName, desiredReplicas)return nil}fmt.Printf("Deployment %s 状态: ReadyReplicas=%d, AvailableReplicas=%d, UpdatedReplicas=%d (预期: %d)\n",deploymentName,deployment.Status.ReadyReplicas,deployment.Status.AvailableReplicas,deployment.Status.UpdatedReplicas,desiredReplicas,)}}
}
主函数示例:
func main() {clientset, err := getKubeConfig()if err != nil {fmt.Printf("获取Kubernetes客户端失败: %v\n", err)return}namespace := "default" // 或者您想部署到的命名空间deploymentName := "go-app-deployment"imageName := "nginx:latest" // 您要部署的镜像replicas := int32(3) // 期望的副本数量// 1. 定义Deploymentdeployment := createDeploymentObject(namespace, deploymentName, imageName, replicas)// 2. 创建Deployment_, err = createDeployment(clientset, deployment)if err != nil {fmt.Printf("创建Deployment失败: %v\n", err)return}// 3. 等待Deployment就绪err = waitForDeploymentReady(clientset, namespace, deploymentName, replicas, 5*time.Minute) // 最多等待5分钟if err != nil {fmt.Printf("等待Deployment就绪失败: %v\n", err)return}fmt.Println("Deployment 和容器已成功启动并准备就绪。")// 可选:获取Pod信息pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: "app=my-app", // 使用Deployment的标签选择器来过滤Pod})if err != nil {fmt.Printf("获取Pod列表失败: %v\n", err)} else {fmt.Println("相关Pod列表:")for _, pod := range pods.Items {fmt.Printf(" - Pod Name: %s, Status: %s\n", pod.Name, pod.Status.Phase)}}// 可选:清理资源 (如果不需要,可以注释掉)// fmt.Printf("尝试删除Deployment %s...\n", deploymentName)// deletePolicy := metav1.DeletePropagationForeground// if err := clientset.AppsV1().Deployments(namespace).Delete(context.TODO(), deploymentName, metav1.DeleteOptions{// PropagationPolicy: &deletePolicy,// }); err != nil {// fmt.Printf("删除Deployment失败: %v\n", err)// } else {// fmt.Printf("Deployment %s 已成功删除。\n", deploymentName)// }
}
运行此代码的步骤:
- 安装Go语言: 确保您的系统上安装了Go。
- 安装Kubernetes客户端库:
go get k8s.io/client-go@latest
- 配置kubeconfig: 确保您的
~/.kube/config
文件配置正确,并且有权访问您的Kubernetes集群。 - 保存代码: 将上述Go代码保存为
.go
文件(例如deploy_app.go
)。 - 运行:
go run deploy_app.go
运行结果
解释流程中涉及的Kubernetes概念:
- Deployment (部署): Kubernetes中最常用的控制器,用于声明式地管理Pod的副本。它确保指定数量的Pod始终运行,并支持滚动更新和回滚。
- Pod (容器组): Kubernetes中最小的可部署单元,包含一个或多个紧密耦合的容器。
- ReplicaSet (副本集): Deployment在底层会创建和管理ReplicaSet。ReplicaSet确保特定数量的Pod副本始终运行。
- Container (容器): 在Pod中运行的应用程序进程。
- Image (镜像): 容器运行所需的文件系统和应用程序代码的打包。
- Namespace (命名空间): 用于将集群资源划分为逻辑组,避免命名冲突并提供资源隔离。
- Labels (标签): 键值对,用于标识Kubernetes对象。它们对于选择器(如Deployment的选择器)至关重要。
- Selector (选择器): 用于查找和管理具有特定标签的Kubernetes对象。Deployment使用选择器来识别它应该管理的Pod。
- Kubernetes API Server: Kubernetes集群的控制平面,所有操作都通过它进行。
- Controller Manager: 运行各种控制器(包括Deployment控制器)的组件,这些控制器会持续观察集群的期望状态和实际状态,并采取行动使它们一致。
容器拉取过程(在K8s集群内部发生):
当您创建Deployment后,Kubernetes Controller Manager会执行以下步骤以拉起容器:
- Deployment控制器创建ReplicaSet: Deployment控制器会根据Deployment的
spec.replicas
和spec.selector
创建一个或多个ReplicaSet。 - ReplicaSet控制器创建Pod: ReplicaSet控制器根据Deployment的
spec.template
创建Pod。这些Pod会被标记为由该ReplicaSet管理。 - 调度器(Scheduler)选择节点: Kubernetes调度器会监听到新的Pod被创建,并根据资源需求(CPU、内存)、节点亲和性/反亲和性、Taints/Tolerations等策略,选择一个最合适的节点来运行这个Pod。
- Kubelet在节点上创建Pod: 被调度到节点的Kubelet会接收到Pod的创建请求。
- 容器运行时(Container Runtime)拉取镜像: Kubelet会指示节点上的容器运行时(例如Docker、containerd)从配置的镜像仓库(如Docker Hub、私有仓库)拉取指定的容器镜像。
- 容器运行时启动容器: 镜像拉取完成后,容器运行时会根据Pod的定义启动容器。
- 容器启动和健康检查:
- 容器内的进程开始运行。
- 如果Pod定义了
livenessProbe
和readinessProbe
,Kubelet会定期执行这些健康检查。 livenessProbe
失败会导致容器重启。readinessProbe
失败会导致Pod的Service流量不再路由到该Pod,直到其准备就绪。
- Pod状态更新: Kubelet会向API Server报告Pod的当前状态(例如Pending -> Running -> Ready)。
- Deployment状态更新: Deployment控制器会持续监控其管理的ReplicaSet和Pod的状态。当足够数量的Pod达到"Ready"状态时,Deployment会被认为是"准备就绪"。
整个过程是异步的,Go程序通过轮询Deployment的状态来判断容器是否成功拉起并运行。