从kubectl部署第一个Pod开始,搞懂K8s的核心组件协作流程
前言
很多K8s新手(包括当初的我)都是从kubectl create开始的——照着教程敲一行命令,Pod就跑起来了,觉得K8s不过如此。但说实话,一旦出了问题,比如Pod一直Pending、或者ContainerCreating卡住不动,就完全懵了,因为根本不知道背后发生了什么。
我个人的建议是:学K8s,先搞清楚一个Pod从提交到运行,中间经过了哪些组件、做了什么事情。 这是理解K8s架构的起点,也是排查问题的基础。
今天我们就从部署一个最简单的nginx Pod开始,把整个流程拆解清楚。
确立目标
在动手之前,先明确我们要搞清楚的三件事:
-
从创建Pod的全流程入手,了解各组件的工作内容,核心组件包括:
- kubectl:客户端命令行工具
- kube-apiserver:集群的"大脑",所有操作的入口
- etcd:分布式KV存储,保存集群所有状态数据
- kube-controller-manager:控制器的大管家
- kube-scheduler:负责Pod调度到哪个Node
- kubelet:每个Node上的代理人,负责实际运行容器
-
对核心模块与引用的库有基本的认识,为后续深入源码做好铺垫
-
结合源码,掌握Kubernetes的核心概念,而不是停留在"会用命令"的层面
从创建Pod开始:手把手部署
编写nginx Pod的YAML
创建一个文件nginx_pod.yaml:
apiVersion: v1 # API版本,Pod属于核心API组v1
kind: Pod # 资源类型,这里是最基础的Pod
metadata:
name: nginx-pod # Pod的名称,集群内唯一标识
spec:
containers: # Pod中运行的容器列表
- name: nginx # 容器名称
image: nginx:1.8 # 使用的镜像及版本
# 注意:nginx:1.8是比较老的版本(2016年发布)
# 实际项目建议使用 nginx:stable 或 nginx:1.25 等较新版本
小贴士:很多教程用的都是
nginx:1.8或nginx:latest。实际项目中,千万别用latest标签——你无法确定每次拉到的镜像版本一致,出了问题很难复现。建议始终指定明确的版本号。
使用kubectl部署Pod
# 根据YAML文件创建Pod
kubectl create -f nginx_pod.yaml
# 输出:pod/nginx-pod created
看到pod/nginx-pod created,说明Pod已经提交给API Server了。但注意,"created"只是表示请求被接受并写入etcd,并不意味着容器已经启动运行。 从created到Running,中间还有调度、拉取镜像、启动容器等好几个步骤。
观察Pod状态
# 查看Pod状态
kubectl get pod
# 输出示例:
# NAME READY STATUS RESTARTS AGE
# nginx-pod 1/1 Running 0 92s
各字段含义:
| 字段名 | 含义 | 说明 |
|---|---|---|
| NAME | Pod名称 | 对应YAML中metadata.name的值 |
| READY | 就绪状态 | 1/1表示1个容器中1个已就绪 |
| STATUS | 当前状态 | Running表示运行中,常见状态见下表 |
| RESTARTS | 重启次数 | 0表示容器未重启过,频繁重启需排查 |
| AGE | 运行时长 | Pod创建后经过的时间 |
Pod常见状态速查:
| 状态 | 含义 | 可能原因 |
|---|---|---|
| Pending | 等待调度 | 资源不足、调度器未选中合适Node |
| ContainerCreating | 容器创建中 | 正在拉取镜像或挂载存储 |
| Running | 运行中 | 容器已启动且正常运行 |
| CrashLoopBackOff | 崩溃重启 | 容器启动后立即退出,反复重启 |
| ImagePullBackOff | 镜像拉取失败 | 镜像地址错误或无权限 |
| Completed | 已完成 | 任务型Pod执行完毕退出 |
查看Pod详情
# 查看Pod的详细信息,包括事件、IP、所在Node等
kubectl describe pod nginx-pod
# 查看Pod的实时日志
kubectl logs nginx-pod
# 持续跟踪日志输出(类似tail -f)
kubectl logs -f nginx-pod
kubectl describe是排查Pod问题最常用的命令,底部的Events区域会记录Pod从创建到运行过程中的所有事件,包括调度决策、镜像拉取、容器启动等。如果Pod状态异常,第一时间看这里。
kubectl按下回车后发生了什么?
这是理解K8s架构的关键问题。很多人以为kubectl create就是直接在Node上创建容器,实际上中间经过了多个组件的协作:
kubectl kube-apiserver etcd
| | |
|--- POST /api/v1/pods --->| |
| |--- 写入Pod数据 -------->|
| |<--- 写入成功 -----------|
|<-- 201 Created -----------| |
| | |
|--- watch通知 ---------> kube-scheduler
| (选择目标Node)
|<-- 绑定Node -----------|
|--- 更新etcd ---------->|
| |
|--- watch通知 ---------> kubelet(目标Node)
| (创建并启动容器)
完整流程拆解:
- kubectl 读取YAML文件,将内容序列化为JSON,向kube-apiserver发起HTTP POST请求
- kube-apiserver 收到请求后进行认证、鉴权、准入控制(Admission Control),校验通过后将Pod数据写入etcd
- kube-scheduler 通过watch机制发现新创建但未调度的Pod,根据资源需求、亲和性等规则选择一个合适的Node,将绑定信息写回etcd
- kubelet 通过watch机制发现被调度到本Node的Pod,调用容器运行时(containerd/Docker)拉取镜像并启动容器
- kubelet 持续上报Pod状态给apiserver,apiserver更新etcd中的状态数据
关键认知:K8s的组件之间不是直接调用的,而是通过apiserver+etcd作为中间层,采用watch/list的声明式机制。这意味着任何组件挂掉重启后,都能从etcd恢复状态,这是K8s高可用的基础。
踩坑实录
坑1:镜像版本踩坑——nginx:1.8的坑
现象:按照教程使用nginx:1.8,在某些新版本的containerd环境下容器启动失败。
根因:nginx:1.8发布于2016年,基于非常老的Debian基础镜像,可能存在glibc兼容性问题,且包含大量已知安全漏洞。
解决方案:使用较新版本的nginx镜像:
spec:
containers:
- name: nginx
image: nginx:1.25 # 推荐使用明确的稳定版本
# image: nginx:stable # 或者使用stable标签
预防措施:项目中建立镜像版本白名单机制,禁止使用超过2年未更新的镜像版本。
坑2:Pod状态卡在Pending
现象:kubectl get pod显示STATUS一直是Pending,等了好几分钟都没变化。
根因:最常见的原因是集群Node资源不足。比如Node的CPU或内存已经分配完了,scheduler找不到满足Pod资源需求的Node。
排查步骤:
# 第一步:查看Pod事件
kubectl describe pod nginx-pod | grep -A 20 Events
# 第二步:查看Node资源使用情况
kubectl describe node <node-name> | grep -A 5 Allocatable
# 第三步:查看所有Pod的资源请求
kubectl get pods -A -o wide
解决方案:
- 释放不用的Pod资源
- 在YAML中设置合理的
resources.requests和resources.limits - 扩容集群Node
坑3:kubectl命令执行报错"connection refused"
现象:执行任何kubectl命令都报The connection to the server x.x.x.x:6443 was refused。
根因:kubeconfig配置错误或API Server未运行。新手最常犯的错误是没有正确配置kubeconfig,特别是使用云厂商托管集群时,需要先下载并配置kubeconfig文件。
解决方案:
# 检查kubeconfig路径
echo $KUBECONFIG
# 查看当前context
kubectl config current-context
# 如果是云厂商集群,按文档下载kubeconfig并设置环境变量
export KUBECONFIG=/path/to/kubeconfig
生产环境Pod部署检查清单
在把Pod部署到生产环境之前,建议逐项检查:
- YAML中指定了明确的镜像版本号(禁止使用
latest) - 设置了
resources.requests和resources.limits - 配置了
livenessProbe和readinessProbe健康检查 - 设置了合理的
imagePullPolicy(建议IfNotPresent) - 配置了
restartPolicy(默认Always,确认符合预期) - 如需持久化,已配置
volumeMounts和volumes - 镜像来源可信,已通过安全扫描
注意:上面的检查清单中,生产环境最不能省的是resources限制和健康检查。我见过太多因为没设resources导致一个Pod吃光Node内存、拖垮整个节点的生产事故。
总结
从一个最简单的kubectl create命令出发,我们拆解了Pod从提交到运行的完整链路:
- kubectl负责将YAML提交给apiserver
- apiserver是所有操作的入口,数据最终持久化到etcd
- scheduler负责为Pod选择合适的Node
- kubelet在目标Node上实际创建和运行容器
- 各组件通过watch机制协作,而非直接调用
理解这个流程,是排查K8s问题的基础。下次Pod状态异常时,你就知道该从哪个环节入手了。
你踩过这些坑吗?
- 你的第一个K8s Pod部署遇到了什么问题?是怎么解决的?
- 在生产环境中,你用什么策略来管理Pod的镜像版本?
- 除了上面提到的三个坑,你在K8s部署中还踩过哪些坑?
延伸思考
- 如果Pod需要访问外部服务,网络策略该怎么配置?
- 在多Node集群中,scheduler如何决定Pod调度到哪个Node?亲和性和反亲和性怎么用?
- 当Pod的容器数量>1(Sidecar模式),READY字段显示
2/2意味着什么?两个容器的生命周期如何关联?
求助与交流
如果你在部署K8s Pod的过程中遇到了问题,或者有更好的实践方案,欢迎在评论区交流。也欢迎分享你踩过的坑,我会整理补充到文章中。
更多推荐


所有评论(0)