课程目录(第 4 章 / 共 33 章)
第一个 Pod:kubectl 常用操作
创建你的第一个 Pod,把 get、describe、logs、exec、port-forward、delete 这几条最常用的 kubectl 命令一次练熟。
学完这一章,你将能够
- ✓用 kubectl run 创建 Pod,并读懂 get pods 各列的含义
- ✓用 describe 的 Events 和 logs 定位 Pod 启动失败的原因
- ✓通过 port-forward 在浏览器里访问 Pod 里的服务
Pod 是什么
Pod 是 Kubernetes 中最小的调度单元:调度器决定它落在哪台机器上,kubelet 负责把它跑起来,它有自己的 IP 和生命周期。你几乎不会只创建裸 Pod 就跑生产,但所有工作负载——Deployment、StatefulSet、Job——最终都会生成 Pod,所以它是必须迈过的第一道坎。
Pod 和容器的关系可以这样理解:Pod 是一层包装,里面可以放一个或多个容器,这些容器共享网络命名空间和存储卷。最常见的情况是一个 Pod 里只有一个容器,这时你完全可以把它当成「被 Kubernetes 托管的一个容器」。
为什么不让调度器直接调度容器?因为有些场景需要多个进程紧密协作(比如主容器 + 日志采集 sidecar),它们必须共享网络和存储,Pod 就是为这种「一起调度、一起销毁」的单元准备的。本章先只看单容器 Pod。
动手:创建第一个 Pod
创建命名空间与 Pod
本课程后续章节统一使用 demo 命名空间,先把它建出来:
kubectl create namespace demo创建 Pod 最简单的方式是 kubectl run:
kubectl run web --image=nginx:1.27 --port=80 -n demo--port=80 只是声明容器会监听 80 端口,方便阅读,并不会真的暴露服务。去掉 -n demo 就会创建到 default 命名空间,本课程统一加 -n demo。
kubectl run 适合临时试验和排错,正式用法是写 YAML 再 `kubectl apply -f`,第 5 章 用 YAML 描述 Pod 就会讲。这里先用它建立手感。
看懂 kubectl get pods 的输出
kubectl get pods -n demo输出大致是:
NAME READY STATUS RESTARTS AGE
web 1/1 Running 0 30s每一列的含义:
| 列 | 含义 | 要警惕的信号 |
|---|---|---|
| NAME | Pod 名称 | 无 |
| READY | 就绪容器数 / 总容器数 | 0/1 说明容器没通过就绪检查 |
| STATUS | 当前状态或阶段 | 不是 Running 就要查 |
| RESTARTS | 容器重启次数 | 数字持续增长说明反复崩溃 |
| AGE | 创建至今的时间 | 无 |
加上 -o wide 会多出几列:
kubectl get pods -n demo -o wideIP 是 Pod 在集群内的地址(重启后会变),NODE 是它落在哪个节点上——多节点集群里这一列特别有用,能验证调度器确实把 Pod 分散开了。
想看完整的对象定义,用 -o yaml:
kubectl get pod web -n demo -o yaml输出分成两部分:spec 是你期望的状态(镜像、端口、环境变量),status 是集群回写的事实(Pod IP、所在节点、容器状态、启动时间)。第 5 章 用 YAML 描述 Pod 会重点强调:status 永远不要手写。
describe 与 Events:排错第一现场
kubectl describe 把对象的配置、状态和最近事件汇总在一起,是排错的第一条命令:
kubectl describe pod web -n demo重点看输出最下面的 Events 一节:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 20s default-scheduler Successfully assigned demo/web to kube101-worker
Normal Pulling 19s kubelet Pulling image "nginx:1.27"
Normal Pulled 9s kubelet Successfully pulled image "nginx:1.27" in 9.8s
Normal Created 9s kubelet Created container web
Normal Started 9s kubelet Started container web读 Events 的方法是按时间顺序往下看,找到第一条 `Warning`:
- 只有
Scheduled之后没有下文:kubelet 还没开始拉镜像,或者节点有问题。 Pulling/Pulled失败:镜像地址、标签或仓库权限有问题。Created之后反复出现BackOff:容器启动后立刻退出,属于应用自身问题。
From 这一列告诉你事件是谁发出的:default-scheduler 是调度器,kubelet 是节点代理。知道谁报的错,就知道该看谁的日志。
logs 与 exec:看日志、进容器
看容器的标准输出:
kubectl logs web -n demo
kubectl logs -f web -n demo-f 会持续跟随输出,等价于 tail -f,按 Ctrl+C 退出。如果 Pod 里有多个容器,要加 -c <容器名> 指定;如果容器刚崩溃过,用 --previous 看上一次的日志,这往往是找到崩溃原因的唯一线索。
进到容器里执行命令:
kubectl exec -it web -n demo -- sh进入之后可以验证 nginx 确实在工作:
curl -s localhost | head -n 4
ls /usr/share/nginx/html
exit-- 后面的部分是容器内要执行的命令,-it 表示分配交互式终端。这是排查「进程起来了但行为不对」的标准手段,注意容器镜像里不一定有 bash,sh 更保险。
port-forward:在浏览器里验证
Pod 的 IP 只有集群内部能访问,想在浏览器里看效果,用端口转发:
kubectl port-forward pod/web 8080:80 -n demo保持这个终端不关,打开浏览器访问 <http://localhost:8080>,应该看到 nginx 的欢迎页。也可以直接:
curl -I http://localhost:8080看到 HTTP/1.1 200 OK 和 Server: nginx/1.27.x 就说明服务正常。
port-forward 是临时调试工具:它只在本机生效,命令退出后通道就断了,不能用来对外提供服务。正式对外暴露要用 第 7 章 Service 和 第 8 章 Ingress。
看完之后按 Ctrl+C 结束转发,然后清理掉这个 Pod:
kubectl delete pod web -n demo
kubectl get pods -n demo第二条命令应该返回 No resources found in demo namespace.,说明 Pod 已经被彻底删除。
Pod 的生命周期阶段
STATUS 列背后的 phase 只有五种取值:
| 阶段 | 含义 | 常见原因 |
|---|---|---|
| Pending | 已被接受,但还没跑起来 | 调度不到节点、镜像还在拉、资源不足 |
| Running | 至少一个容器正在运行 | 正常状态 |
| Succeeded | 所有容器正常退出且不再重启 | 一次性任务完成 |
| Failed | 所有容器已终止,至少一个失败退出 | 任务出错 |
| Unknown | 拿不到 Pod 状态 | 节点失联、kubelet 通信中断 |
除了这五种阶段,STATUS 里还会出现 ImagePullBackOff、CrashLoopBackOff 这类等待原因,它们本质上仍然是 Pending 或 Running 阶段中的异常状态。看到这些词,直接用 kubectl describe pod 看 Events 就能定位。
把阶段之间的迁移画出来,更容易理解「为什么它卡住了」:
kubectl run / kubectl apply
│
▼
Pending ──────► Running ──────► Succeeded(一次性任务正常结束)
│ │
│ ├─────► Failed(容器失败退出且不再重启)
│ └─────► Running(容器反复崩溃:CrashLoopBackOff)
│
└─────► Unknown(节点失联,kubelet 停止上报)关键在 Pending → Running 这一跳:调度、拉镜像、创建容器三步都成功了才会发生。任何一步失败,Pod 就停在 Pending 里并反复重试,原因全部写在 Events 里。
常见坑
两个最常见的新手报错
- ImagePullBackOff:镜像拉不下来。先确认镜像名和标签拼写(
nginx:1.27而不是nginx:latest或ngnix),再kubectl describe pod看 Events 里的完整报错。如果是私有仓库,通常是缺少imagePullSecrets;如果是网络问题,可以先在宿主机docker pull测试,再用 第 3 章 里讲过的kind load docker-image把镜像送进节点。 - CrashLoopBackOff:容器启动后立刻退出,kubelet 反复重启它。这时镜像没问题,问题在应用本身。按顺序做三件事:
kubectl logs web -n demo看退出前的输出,kubectl logs --previous web -n demo看崩溃前一次的输出,kubectl describe pod web -n demo看退出码和 Events。常见原因是启动命令写错、配置文件缺失、连不上依赖的数据库。
Pending 常见原因速查
Pending 是新手最常遇到的卡点,但它只是「还没跑起来」的总称,真正的原因永远在 Events 里。
| 现象 | 原因 | 怎么确认 | 怎么办 |
|---|---|---|---|
Events 首条 Warning 是 FailedScheduling | 没有节点满足资源、亲和性或污点要求 | kubectl describe pod web -n demo 看 Events | 调低 resources.requests,或放宽 nodeSelector、补 tolerations |
Events 里写 Insufficient cpu / Insufficient memory | 节点可分配资源不够 | kubectl describe node <节点名> 看 Allocated resources | 缩小 requests、腾出资源,或加一个节点 |
Events 里写 taint ... that the pod didn't tolerate | 节点有污点,Pod 没有对应容忍 | 同上,读完整报错 | 给 Pod 加 tolerations,或换到无污点的节点 |
Pending 但 Events 里没有 FailedScheduling | 调度器没在工作,或对象没真正提交成功 | kubectl get pods -n kube-system 看调度器 Pod 在不在 | 检查控制平面;kubectl get pod web -n demo -o yaml 确认 spec.nodeName 为空 |
已经有 nodeName,但仍是 Pending 或 ContainerCreating | 卡在 kubelet 侧:拉镜像失败或 CNI 未就绪 | Events 里的 Pulling / Failed / 卷挂载报错 | 修镜像地址,或检查 kube-system 里的 CNI Pod |
STATUS 是 Running,但 READY 是 0/1 | 容器在跑,但没通过就绪检查 | kubectl describe pod 里的 Readiness probe failed | 修探针路径或端口,见 第 11 章 探针与资源管理 |
Pod 是「一次性」的
Pod 不是宠物,而是「牲口」:删掉就没了,重启后名字和 IP 都会变,也不会自动补回来。如果你手动删掉一个 Pod,没有人会替你重建——这正是需要一个上层控制器来管它的原因。
自测题
自测:为什么 Pod 删掉重建之后,IP 会变?(点击展开答案)
因为 Pod IP 是在 Pod 被创建时由 CNI 插件临时分配的,不是预先固定的。Pod 对象一销毁,它占用的那个 IP 就回到节点的地址池,可以被下一个 Pod 拿走。
更麻烦的是重建的 Pod 是一个全新对象:名字变了、可能被调度到另一台节点,拿到的 IP 自然也不同。所以永远不要把 Pod IP 写进配置或代码里,要用 第 7 章 Service 提供的稳定入口。
自测:为什么容器崩溃后 `kubectl logs` 经常看不到报错,必须加 `--previous`?(点击展开答案)
因为每次重启都会创建一个新的容器实例,日志跟着这个实例的生命周期走。kubelet 只保留上一次退出的那个容器的日志副本,kubectl logs 默认读的是当前正在跑的实例——它刚启动,还没来得及输出任何东西。
--previous 就是让 kubelet 把上一份日志取给你。如果容器已经反复重启多次,你只能看到最近一次崩溃前的输出,更早的已经没了——这也是为什么排障要尽快做,或者把日志集中收集起来。
自测:为什么 `port-forward` 能让本机访问 Pod,同事却访问不了?(点击展开答案)
因为 port-forward 根本不是集群里的网络能力,而是 kubectl 在你本机临时搭的一条隧道:kubectl 向 apiserver 发起连接,请求转发到目标 Pod 的端口,再把这个连接映射到你本地的 127.0.0.1:8080。
它只绑定在本机回环地址上,命令一退出隧道就断,也没有任何 Service 或 Ingress 记录它的存在,所以别的机器无从访问。要真正对外提供服务,得用 第 7 章 Service 加 第 8 章 Ingress。
小结
- Pod 是最小的调度单元,可以包含一个或多个共享网络与存储的容器。
kubectl run适合临时试验,正式做法是写 YAML 再kubectl apply -f。kubectl get pods看状态,-o wide看 IP 和节点,-o yaml看完整定义。- 排错顺序固定:先
describe看 Events,再logs看输出,必要时exec进去查。 - Pod 是一次性的,直接管理裸 Pod 无法自动恢复、滚动更新和扩缩容,所以需要 Deployment。
相关章节:kubectl run 之外的正规写法见 第 5 章 用 YAML 描述 Pod;Pod 想被自动重建、滚动更新,交给 第 6 章 Deployment。
练习
- 用
kubectl run api --image=hashicorp/http-echo:1.0 --port=5678 -n demo创建一个后端 Pod,然后用port-forward在本地访问它,看它返回什么。 - 故意把镜像写成
nginx:1.999,观察 Pod 的STATUS和describe里的 Events,记录下报错原文,再删掉它。 - 用
kubectl exec进入webPod,执行hostname和cat /etc/hosts,想一想这两个输出为什么能说明 Pod 的网络是独立的。
下一章我们用 YAML 描述 Pod,把 kubectl run 换成清单,看清 Pod 的每一个字段是怎么写的。