课程目录(第 4 章 / 共 33 章)
课程/最小工作单元

第一个 Pod:kubectl 常用操作

4 章 / 共 33·15 分钟·入门Podkubectl排错

创建你的第一个 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 命名空间,先把它建出来:

bash
kubectl create namespace demo

创建 Pod 最简单的方式是 kubectl run

bash
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 的输出

bash
kubectl get pods -n demo

输出大致是:

text
NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   0          30s

每一列的含义:

含义要警惕的信号
NAMEPod 名称
READY就绪容器数 / 总容器数0/1 说明容器没通过就绪检查
STATUS当前状态或阶段不是 Running 就要查
RESTARTS容器重启次数数字持续增长说明反复崩溃
AGE创建至今的时间

加上 -o wide 会多出几列:

bash
kubectl get pods -n demo -o wide

IP 是 Pod 在集群内的地址(重启后会变),NODE 是它落在哪个节点上——多节点集群里这一列特别有用,能验证调度器确实把 Pod 分散开了。

想看完整的对象定义,用 -o yaml

bash
kubectl get pod web -n demo -o yaml

输出分成两部分:spec你期望的状态(镜像、端口、环境变量),status集群回写的事实(Pod IP、所在节点、容器状态、启动时间)。第 5 章 用 YAML 描述 Pod 会重点强调:status 永远不要手写。

describe 与 Events:排错第一现场

kubectl describe 把对象的配置、状态和最近事件汇总在一起,是排错的第一条命令:

bash
kubectl describe pod web -n demo

重点看输出最下面的 Events 一节:

text
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:看日志、进容器

看容器的标准输出:

bash
kubectl logs web -n demo
kubectl logs -f web -n demo

-f 会持续跟随输出,等价于 tail -f,按 Ctrl+C 退出。如果 Pod 里有多个容器,要加 -c <容器名> 指定;如果容器刚崩溃过,用 --previous 看上一次的日志,这往往是找到崩溃原因的唯一线索。

进到容器里执行命令:

bash
kubectl exec -it web -n demo -- sh

进入之后可以验证 nginx 确实在工作:

bash
curl -s localhost | head -n 4
ls /usr/share/nginx/html
exit

-- 后面的部分是容器内要执行的命令,-it 表示分配交互式终端。这是排查「进程起来了但行为不对」的标准手段,注意容器镜像里不一定有 bash,sh 更保险。

port-forward:在浏览器里验证

Pod 的 IP 只有集群内部能访问,想在浏览器里看效果,用端口转发:

bash
kubectl port-forward pod/web 8080:80 -n demo

保持这个终端不关,打开浏览器访问 <http://localhost:8080>,应该看到 nginx 的欢迎页。也可以直接:

bash
curl -I http://localhost:8080

看到 HTTP/1.1 200 OKServer: nginx/1.27.x 就说明服务正常。

port-forward临时调试工具:它只在本机生效,命令退出后通道就断了,不能用来对外提供服务。正式对外暴露要用 第 7 章 Service第 8 章 Ingress

看完之后按 Ctrl+C 结束转发,然后清理掉这个 Pod:

bash
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 里还会出现 ImagePullBackOffCrashLoopBackOff 这类等待原因,它们本质上仍然是 PendingRunning 阶段中的异常状态。看到这些词,直接用 kubectl describe pod 看 Events 就能定位。

把阶段之间的迁移画出来,更容易理解「为什么它卡住了」:

text
kubectl run / kubectl apply


     Pending ──────► Running ──────► Succeeded(一次性任务正常结束)
        │              │
        │              ├─────► Failed(容器失败退出且不再重启)
        │              └─────► Running(容器反复崩溃:CrashLoopBackOff)

        └─────► Unknown(节点失联,kubelet 停止上报)

关键在 Pending → Running 这一跳:调度、拉镜像、创建容器三步都成功了才会发生。任何一步失败,Pod 就停在 Pending 里并反复重试,原因全部写在 Events 里。

常见坑

两个最常见的新手报错

  • ImagePullBackOff:镜像拉不下来。先确认镜像名和标签拼写(nginx:1.27 而不是 nginx:latestngnix),再 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,但仍是 PendingContainerCreating卡在 kubelet 侧:拉镜像失败或 CNI 未就绪Events 里的 Pulling / Failed / 卷挂载报错修镜像地址,或检查 kube-system 里的 CNI Pod
STATUSRunning,但 READY0/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

练习

  1. kubectl run api --image=hashicorp/http-echo:1.0 --port=5678 -n demo 创建一个后端 Pod,然后用 port-forward 在本地访问它,看它返回什么。
  2. 故意把镜像写成 nginx:1.999,观察 Pod 的 STATUSdescribe 里的 Events,记录下报错原文,再删掉它。
  3. kubectl exec 进入 web Pod,执行 hostnamecat /etc/hosts,想一想这两个输出为什么能说明 Pod 的网络是独立的。

下一章我们用 YAML 描述 Pod,把 kubectl run 换成清单,看清 Pod 的每一个字段是怎么写的。