目录

第一章:认识音乐会的"幕后团队"

1.1 组件角色扮演

1.2 完整工作流程图

第二章:实战开始——部署一个Web应用音乐会

2.1 用户提交"音乐会策划书"

2.2 kubectl:音乐会总指挥发出指令

2.3 API Server:指挥中心接收指令

2.4 Controller Manager:自动编排系统开始工作

第三章:kube-scheduler——智能座位分配师

3.1 调度器发现待安排的"表演者"

3.2 详细的筛选和评分过程

第四章:Kubelet——舞台管理员执行工作

4.1 Kubelet接收并执行调度结果

4.2 具体的表演者准备工作

第五章:完整流程回顾和故障排查

5.1 一次成功的部署时间线

5.2 常见问题排查指南

第六章:总结与启示

6.1 组件协作的智慧

6.2 实际应用建议


想象一下,你要组织一场大型音乐会——有舞台音响、灯光控制、演员休息室等多个环节。Kubernetes就像是一个智能的音乐会指挥系统,而今天我要带你完整走一遍这个系统是如何协同工作的。

第一章:认识音乐会的"幕后团队"

1.1 组件角色扮演

音乐会筹备团队比喻:​

  • 音乐会总指挥(kubectl)​​:你,负责发出所有指令
  • 指挥中心(API Server)​​:音乐厅的控制室,接收所有指令
  • 工作记录板(ETCD)​​:记录每个环节的进度和状态
  • 自动调度系统(Controller Manager)​​:智能的节目编排系统
  • 座位分配师(kube-scheduler)​​:为每个表演者安排最佳位置
  • 舞台管理员(Kubelet)​​:在每个舞台执行具体准备工作

1.2 完整工作流程图

用户(kubectl) → API Server → ETCD → Controller Manager → kube-scheduler → Kubelet → Pod运行

第二章:实战开始——部署一个Web应用音乐会

2.1 用户提交"音乐会策划书"

# concert-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-concert
spec:
  replicas: 3  # 需要3个表演团队
  selector:
    matchLabels:
      show: nginx-concert
  template:
    metadata:
      labels:
        show: nginx-concert
    spec:
      containers:
      - name: singer  # 主唱
        image: nginx:1.19
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "128Mi"
            cpu: "250m"
      - name: dancer  # 舞者
        image: redis:6.0
        resources:
          requests:
            memory: "256Mi"
            cpu: "500m"

用户执行命令:

kubectl apply -f concert-deployment.yaml

2.2 kubectl:音乐会总指挥发出指令

// kubectl内部工作流程
func 提交音乐会策划() error {
    // 1. 读取策划文件
    策划内容, err := ioutil.ReadFile("concert-deployment.yaml")
    if err != nil {
        return fmt.Errorf("读取策划书失败: %v", err)
    }
    
    // 2. 解析为Kubernetes能理解的对象
    deployment := &appsv1.Deployment{}
    if err := yaml.Unmarshal(策划内容, deployment); err != nil {
        return fmt.Errorf("解析策划书失败: %v", err)
    }
    
    // 3. 转换为API请求发送给指挥中心
    req := restClient.Post().
        Resource("deployments").
        Body(deployment)
    
    result := req.Do()
    if result.Error() != nil {
        return fmt.Errorf("提交到指挥中心失败: %v", result.Error())
    }
    
    fmt.Println("🎵 音乐会策划已提交到指挥中心!")
    return nil
}

2.3 API Server:指挥中心接收指令

API Server就像音乐厅的控制室,所有指令都要经过这里:

// API Server 处理请求的完整流程
func (api *APIServer) 处理部署请求(请求 *http.Request) {
    // 🎯 第一步:安全检查
    if !api.验证身份(请求) {
        api.返回错误(401, "身份验证失败:请出示工作证")
        return
    }
    
    if !api.检查权限(请求) {
        api.返回错误(403, "权限不足:您无权组织此类音乐会")
        return
    }
    
    // 📋 第二步:数据验证
    部署数据, err := api.解析部署数据(请求.Body)
    if err != nil {
        api.返回错误(400, "策划书格式错误: "+err.Error())
        return
    }
    
    // 💾 第三步:保存到工作记录板(ETCD)
    if err := api.保存到ETCD("/registry/deployments/default/web-concert", 部署数据); err != nil {
        api.返回错误(500, "保存到工作记录板失败: "+err.Error())
        return
    }
    
    // 📢 第四步:通知相关方(通过Watch机制)
    api.通知监听者("deployments", "CREATE", 部署数据)
    
    api.返回成功(201, 部署数据)
    fmt.Println("✅ 新音乐会策划已记录到工作板")
}

2.4 Controller Manager:自动编排系统开始工作

这是最容易被忽略但最关键的一环!​​ Controller Manager让整个系统拥有了"自动驾驶"能力。

// Deployment Controller:音乐会总导演
func (dc *DeploymentController) 开始导演工作() {
    ticker := time.NewTicker(10 * time.Second) // 每10秒检查一次
    
    for range ticker.C {
        fmt.Println("🎬 总导演开始检查所有音乐会进度...")
        
        // 获取所有音乐会策划
        所有部署, err := dc.client.AppsV1().Deployments("").List()
        if err != nil {
            fmt.Printf("❌ 获取策划列表失败: %v\n", err)
            continue
        }
        
        for _, 部署 := range 所有部署.Items {
            dc.确保音乐会按计划进行(部署)
        }
    }
}

func (dc *DeploymentController) 确保音乐会按计划进行(部署 appsv1.Deployment) {
    // 检查当前状态 vs 期望状态
    当前副本集 := dc.查找关联的副本集(部署)
    当前Pod数 := dc.计算实际Pod数(当前副本集)
    期望Pod数 := *部署.Spec.Replicas
    
    fmt.Printf("📊 音乐会【%s】: 期望%d个团队,实际%d个团队\n", 
        部署.Name, 期望Pod数, 当前Pod数)
    
    if 当前Pod数 < 期望Pod数 {
        // 需要创建更多的表演团队
        需要创建数 := 期望Pod数 - 当前Pod数
        fmt.Printf("🎯 需要创建%d个新的表演团队\n", 需要创建数)
        
        dc.创建副本集(部署, 需要创建数)
    } else if 当前Pod数 > 期望Pod数 {
        // 需要减少表演团队
        需要删除数 := 当前Pod数 - 期望Pod数
        fmt.Printf("🗑️ 需要减少%d个表演团队\n", 需要删除数)
        
        dc.删除多余Pod(当前副本集, 需要删除数)
    } else {
        fmt.Printf("✅ 音乐会【%s】按计划进行中\n", 部署.Name)
    }
}

// ReplicaSet Controller:节目编排助理
func (rsc *ReplicaSetController) 开始编排工作() {
    ticker := time.NewTicker(10 * time.Second)
    
    for range ticker.C {
        fmt.Println("📋 编排助理检查节目单...")
        
        所有副本集, err := rsc.client.AppsV1().ReplicaSets("").List()
        if err != nil {
            continue
        }
        
        for _, 副本集 := range 所有副本集.Items {
            rsc.确保节目单落实(副本集)
        }
    }
}

func (rsc *ReplicaSetController) 确保节目单落实(副本集 appsv1.ReplicaSet) {
    // 获取属于这个副本集的所有Pod
    相关Pods := rsc.查找副本集的Pods(副本集)
    当前Pod数 := len(相关Pods)
    期望Pod数 := *副本集.Spec.Replicas
    
    if 当前Pod数 < 期望Pod数 {
        需要创建数 := 期望Pod数 - 当前Pod数
        fmt.Printf("🎭 为节目【%s】创建%d个表演者(Pod)\n", 副本集.Name, 需要创建数)
        
        for i := 0; i < 需要创建数; i++ {
            pod := rsc.创建Pod模板(副本集)
            // 注意:这里创建的Pod还没有分配舞台(节点)
            pod.Spec.NodeName = "" // 空的,等待调度
            
            _, err := rsc.client.CoreV1().Pods(副本集.Namespace).Create(pod)
            if err != nil {
                fmt.Printf("❌ 创建表演者失败: %v\n", err)
            } else {
                fmt.Printf("✅ 创建了等待调度的表演者: %s\n", pod.Name)
            }
        }
    }
}

第三章:kube-scheduler——智能座位分配师

3.1 调度器发现待安排的"表演者"

// kube-scheduler 的核心调度循环
func (s *Scheduler) 开始分配座位() {
    fmt.Println("🎪 座位分配师开始工作...")
    
    ticker := time.NewTicker(1 * time.Second) // 每秒检查一次
    
    for range ticker.C {
        // 查找所有等待分配的表演者(Pending状态的Pod)
        待调度Pods := s.获取待调度Pods()
        
        if len(待调度Pods) > 0 {
            fmt.Printf("📋 发现%d个等待分配座位的表演者\n", len(待调度Pods))
        }
        
        for _, pod := range 待调度Pods {
            fmt.Printf("👤 正在为表演者【%s】寻找合适舞台...\n", pod.Name)
            
            最佳舞台 := s.为表演者选择舞台(pod)
            if 最佳舞台 != nil {
                s.安排表演者上台(pod, 最佳舞台)
            } else {
                fmt.Printf("❌ 没有找到适合【%s】的舞台\n", pod.Name)
            }
        }
    }
}

// 两阶段调度算法
func (s *Scheduler) 为表演者选择舞台(pod *v1.Pod) *v1.Node {
    // 第一阶段:筛选合适的舞台
    所有舞台 := s.获取所有舞台()
    合格舞台 := s.筛选阶段(所有舞台, pod)
    
    if len(合格舞台) == 0 {
        return nil
    }
    
    fmt.Printf("✅ 初步筛选出%d个合格舞台\n", len(合格舞台))
    
    // 第二阶段:评分排序
    舞台评分 := s.评分阶段(合格舞台, pod)
    
    // 选择评分最高的舞台
    最佳舞台 := s.选择最佳舞台(舞台评分)
    fmt.Printf("🏆 最佳舞台: %s (评分: %d)\n", 最佳舞台.Name, 舞台评分[最佳舞台.Name])
    
    return 最佳舞台
}

3.2 详细的筛选和评分过程

func (s *Scheduler) 筛选阶段(所有舞台 []*v1.Node, pod *v1.Pod) []*v1.Node {
    var 合格舞台 []*v1.Node
    
    for _, 舞台 := range 所有舞台 {
        fmt.Printf("🔍 检查舞台【%s】是否适合...\n", 舞台.Name)
        
        if !s.检查舞台资源(舞台, pod) {
            fmt.Printf("   ❌ 资源不足: 舞台%s无法满足表演需求\n", 舞台.Name)
            continue
        }
        
        if !s.检查舞台条件(舞台, pod) {
            fmt.Printf("   ❌ 条件不符: 舞台%s不符合表演要求\n", 舞台.Name)
            continue
        }
        
        if !s.检查舞台就绪(舞台) {
            fmt.Printf("   ❌ 舞台未就绪: %s正在维护中\n", 舞台.Name)
            continue
        }
        
        fmt.Printf("   ✅ 舞台【%s】通过所有检查\n", 舞台.Name)
        合格舞台 = append(合格舞台, 舞台)
    }
    
    return 合格舞台
}

func (s *Scheduler) 评分阶段(合格舞台 []*v1.Node, pod *v1.Pod) map[string]int {
    评分表 := make(map[string]int)
    
    for _, 舞台 := range 合格舞台 {
        分数 := 0
        
        // 资源平衡策略(避免某个舞台过载)
        分数 += s.计算资源平衡分(舞台)
        
        // 亲和性策略(喜欢把相关表演放一起)
        分数 += s.计算亲和性分(舞台, pod)
        
        // 本地性策略(如果舞台已有所需道具/镜像)
        分数 += s.计算本地性分(舞台, pod)
        
        评分表[舞台.Name] = 分数
        fmt.Printf("   📊 舞台%s评分: %d分\n", 舞台.Name, 分数)
    }
    
    return 评分表
}

第四章:Kubelet——舞台管理员执行工作

4.1 Kubelet接收并执行调度结果

// 每个舞台的管理员工作流程
func (k *Kubelet) 开始舞台管理() {
    fmt.Printf("🎪 舞台管理员【%s】开始工作...\n", k.节点名称)
    
    ticker := time.NewTicker(10 * time.Second) // 每10秒同步一次
    
    for range ticker.C {
        // 1. 检查指挥中心分配给本舞台的表演者
        本舞台任务 := k.获取本舞台Pods()
        
        // 2. 确保每个表演者都按计划准备
        for _, pod := range 本舞台任务 {
            k.确保表演者就位(pod)
        }
        
        // 3. 清理已经结束的表演
        k.清理舞台()
        
        // 4. 向指挥中心汇报舞台状态
        k.汇报舞台状况()
    }
}

func (k *Kubelet) 确保表演者就位(pod *v1.Pod) {
    // 检查表演者当前状态
    当前状态 := k.检查表演者状态(pod)
    
    switch {
    case 当前状态 == "未开始":
        fmt.Printf("🎬 开始为表演者【%s】做准备...\n", pod.Name)
        k.准备表演者(pod)
        
    case 当前状态 == "准备中":
        fmt.Printf("⏳ 表演者【%s】准备中...\n", pod.Name)
        // 继续等待
        
    case 当前状态 == "就绪":
        fmt.Printf("✅ 表演者【%s】已就绪\n", pod.Name)
        // 正常状态,无需操作
        
    case 当前状态 == "异常":
        fmt.Printf("🔄 表演者【%s】异常,重新准备...\n", pod.Name)
        k.重新准备表演者(pod)
        
    case 当前状态 == "已结束":
        fmt.Printf("🧹 清理表演者【%s】的场地...\n", pod.Name)
        k.清理表演者(pod)
    }
}

4.2 具体的表演者准备工作

func (k *Kubelet) 准备表演者(pod *v1.Pod) error {
    fmt.Printf("🎯 开始准备表演者【%s】\n", pod.Name)
    
    // 1. 准备表演场地(Pod沙箱)
    fmt.Println("   🏗️ 创建表演场地...")
    场地ID, err := k.容器运行时.创建沙箱(pod)
    if err != nil {
        return fmt.Errorf("创建场地失败: %v", err)
    }
    
    // 2. 布置舞台网络
    fmt.Println("   🌐 布置舞台网络...")
    if err := k.网络插件.设置网络(场地ID, pod); err != nil {
        k.容器运行时.清理沙箱(场地ID)
        return fmt.Errorf("布置网络失败: %v", err)
    }
    
    // 3. 为每个角色(容器)做准备
    for i, 角色 := range pod.Spec.Containers {
        fmt.Printf("   🎭 准备角色%d: %s\n", i+1, 角色.Name)
        
        // 拉取表演服装(镜像)
        fmt.Printf("      👔 拉取服装【%s】...\n", 角色.Image)
        if err := k.拉取服装(角色.Image); err != nil {
            return fmt.Errorf("拉取服装失败: %v", err)
        }
        
        // 创建角色实例(容器)
        fmt.Printf("      🎪 创建角色实例...\n")
        角色ID, err := k.容器运行时.创建角色(场地ID, 角色, pod)
        if err != nil {
            return fmt.Errorf("创建角色失败: %v", err)
        }
        
        // 开始表演(启动容器)
        fmt.Printf("      🎬 角色开始表演...\n")
        if err := k.容器运行时.开始表演(角色ID); err != nil {
            return fmt.Errorf("启动角色失败: %v", err)
        }
    }
    
    // 4. 开始健康检查(确保表演质量)
    k.开始健康检查(pod)
    
    fmt.Printf("✅ 表演者【%s】准备完成!\n", pod.Name)
    return nil
}

第五章:完整流程回顾和故障排查

5.1 一次成功的部署时间线

# 查看详细事件记录
kubectl describe pod web-concert-xxx

# 正常的事件序列:
Events:
  Type     Reason                 Age   From                Message
  ----     ------                 ----  ----                -------
  Normal   Scheduled              15s   default-scheduler   Successfully assigned default/web-concert-xxx to node-2
  Normal   Pulling                14s   kubelet, node-2    Pulling image "nginx:1.19"
  Normal   Pulled                 13s   kubelet, node-2    Successfully pulled image "nginx:1.19"
  Normal   Created                12s   kubelet, node-2    Created container singer
  Normal   Started                11s   kubelet, node-2    Started container singer
  Normal   Pulling                10s   kubelet, node-2    Pulling image "redis:6.0"
  Normal   Pulled                 9s    kubelet, node-2    Successfully pulled image "redis:6.0"
  Normal   Created                8s    kubelet, node-2    Created container dancer
  Normal   Started                7s    kubelet, node-2    Started container dancer

5.2 常见问题排查指南

问题1:Pod一直处于Pending状态

# 查看详细调度信息
kubectl get events --field-selector reason=FailedScheduling

# 可能的原因和解决方案:
# 1. 资源不足:增加节点资源或减少请求资源
# 2. 节点选择器不匹配:检查nodeSelector配置
# 3. 污点限制:添加合适的容忍度或移除污点

问题2:Pod调度成功但启动失败

# 查看Kubelet日志
journalctl -u kubelet -f

# 常见问题:
# 1. 镜像拉取失败:检查镜像名称和仓库权限
# 2. 端口冲突:修改容器端口配置
# 3. 存储挂载失败:检查PersistentVolume配置

第六章:总结与启示

6.1 组件协作的智慧

通过这个完整的音乐会比喻,我们可以看到Kubernetes设计的精妙之处:

  1. 关注点分离​:每个组件职责单一,易于理解和维护
  2. 声明式API​:我们只需要告诉系统"想要什么",而不是"如何做"
  3. 自我修复​:Controller Manager持续监控和调和状态
  4. 智能调度​:kube-scheduler基于多种策略做出最优决策
  5. 水平扩展​:每个节点都有独立的Kubelet,易于扩展

6.2 实际应用建议

对于开发者:​

  • 理解整个流程有助于更好的故障排查
  • 合理设置资源请求和限制,帮助调度器做出更好决策
  • 使用亲和性/反亲和性规则优化应用部署

对于运维人员:​

  • 监控各个组件的健康状态
  • 理解调度策略,合理规划集群资源
  • 掌握故障排查的基本流程和方法

这个完整的流程展示了Kubernetes如何将复杂的分布式应用管理变得自动化、可视化。就像一场精心组织的音乐会,每个环节都井然有序,最终呈现出完美的演出效果!

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐