Kubernetes完整工作流程详解:从kubectl提交到Pod运行的奇幻之旅
·
目录
2.4 Controller Manager:自动编排系统开始工作
想象一下,你要组织一场大型音乐会——有舞台音响、灯光控制、演员休息室等多个环节。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设计的精妙之处:
- 关注点分离:每个组件职责单一,易于理解和维护
- 声明式API:我们只需要告诉系统"想要什么",而不是"如何做"
- 自我修复:Controller Manager持续监控和调和状态
- 智能调度:kube-scheduler基于多种策略做出最优决策
- 水平扩展:每个节点都有独立的Kubelet,易于扩展
6.2 实际应用建议
对于开发者:
- 理解整个流程有助于更好的故障排查
- 合理设置资源请求和限制,帮助调度器做出更好决策
- 使用亲和性/反亲和性规则优化应用部署
对于运维人员:
- 监控各个组件的健康状态
- 理解调度策略,合理规划集群资源
- 掌握故障排查的基本流程和方法
这个完整的流程展示了Kubernetes如何将复杂的分布式应用管理变得自动化、可视化。就像一场精心组织的音乐会,每个环节都井然有序,最终呈现出完美的演出效果!
更多推荐

所有评论(0)