1. 为什么一张 kubectl 速查表能救你一整天
刚接手一个线上集群的排查任务时,我经历过那种手心冒汗的时刻:Pod 起不来,日志刷得飞快,脑子里明明知道该用kubectl describe看事件,手却先敲了kubectl logs,然后对着空输出发呆。等反应过来,五分钟已经没了。后来我养成了一个习惯——把高频命令按“场景”而不是按“字母顺序”整理成一张自己的速查表,贴在显示器边上。这张表救过我不止一次。
kubectl是 Kubernetes 的命令行客户端,也是绝大多数人跟集群交互的第一入口。它能做的事非常多:查资源、看日志、进容器、改配置、扩缩容、排障、导出现状。但它的子命令和参数组合极其庞杂,官方文档动辄几十页,真正高频用到的其实就那么二三十条。这篇内容就是把这些高频命令按真实使用场景重新组织,配上我踩过的坑和参数背后的逻辑,让你不用翻文档也能快速定位到该敲什么。
适合谁看?刚接触 K8s 的运维和开发、需要临时上集群排查问题的后端同学、以及想把自己那套零散命令整理成体系的老手。我不会只给你一堆命令让你背,而是告诉你每条命令在什么场景下用、为什么这么用、参数改了会有什么后果。读完你至少能做到:Pod 出问题时不慌,知道按什么顺序查;日常操作不用反复搜索;看到别人敲的命令能秒懂意图。
下面这张表是我自己长期迭代的版本,按“查、看、改、进、排”五个动作分类,后面每个章节会展开讲。
| 动作 | 高频命令 | 典型场景 |
|---|---|---|
| 查 | kubectl get | 快速看资源状态 |
| 看 | kubectl describe/logs | 定位异常原因 |
| 改 | kubectl edit/apply/scale | 调整配置与副本 |
| 进 | kubectl exec/port-forward | 进容器、连服务 |
| 排 | kubectl get events/top | 集群级排障 |
2. get 命令的隐藏玩法:不只是列个清单
2.1 默认输出为什么总是不够用
很多人对kubectl get的印象就是“列一下 Pod”,敲完看到一堆 NAME、READY、STATUS、AGE 就完事了。但默认输出丢掉了太多关键信息。比如你想知道 Pod 跑在哪个节点上,默认列里根本没有;想知道重启了几次,也得额外加参数。我刚开始就吃过亏:一个 Pod 频繁重启,我盯着 STATUS 列的 Running 以为没事,结果 READY 是 0/1,容器其实一直在崩溃循环。
解决办法是-o wide。这个参数会额外输出 IP 和 NODE,排查调度和网络问题时几乎是必加项:
kubectl get pods -o wide kubectl get pods -o wide -n kube-system-n指定命名空间,不加默认是default。这里有个新手常踩的坑:明明kubectl get pods显示没有资源,但服务确实在跑,八成是资源在别的命名空间里。养成习惯,查不到就先加-A全局扫一遍:
kubectl get pods -A-A等价于--all-namespaces,会列出所有命名空间的资源。代价是输出很长,所以定位到具体命名空间后还是要切回-n。
2.2 用 -o 定制输出,把结果喂给别的工具
-o是get最强大的参数,没有之一。它决定了输出格式,常用的有json、yaml、wide、name、custom-columns、jsonpath。我分几个真实场景说。
想导出某个资源的完整定义做备份或改完再 apply,用-o yaml:
kubectl get deployment my-app -o yaml > my-app-backup.yaml注意导出的 yaml 里会带一堆status、metadata.managedFields之类的运行时字段,直接 apply 回去可能报错。我的做法是导出后手动删掉status整段和metadata里的resourceVersion、uid、creationTimestamp、managedFields。这一步很关键,很多人导出来直接改完 apply,结果报“字段不可变”或者版本冲突,就是这些字段在作祟。
只想拿名字做批量操作,用-o name:
kubectl get pods -o name | xargs kubectl delete这条命令会删掉当前命名空间所有 Pod。危险操作,但能说明-o name的用途——输出格式是pod/xxx,方便管道传递。
想自定义列,用custom-columns。比如我经常需要一张“Pod 名 + 节点 + 重启次数”的表:
kubectl get pods -o custom-columns=\ NAME:.metadata.name,\ NODE:.spec.nodeName,\ RESTARTS:.status.containerStatuses[0].restartCount这里.status.containerStatuses[0]取的是第一个容器的状态。如果一个 Pod 有多个容器,这个写法只取第一个,需要全部的话得用jsonpath配合循环,或者干脆用-o json自己解析。
jsonpath适合精确提取单个值,比如拿某个 Service 的 ClusterIP:
kubectl get svc my-service -o jsonpath='{.spec.clusterIP}'jsonpath的语法坑比较多,数组索引用[0],通配用[*],范围用[0:2]。我建议复杂提取直接用-o json配合jq,可读性和可维护性都更好:
kubectl get pods -o json | jq '.items[] | {name: .metadata.name, node: .spec.nodeName}'2.3 排序、筛选与 watch:让 get 变成实时监控
get默认按名字排序,但排障时我更关心谁重启最多、谁最老。--sort-by能按任意字段排序:
kubectl get pods --sort-by=.status.containerStatuses[0].restartCount kubectl get pods --sort-by=.metadata.creationTimestamp第一条按重启次数升序,想降序就加--sort-by后配合-o再处理,或者用--sort-by本身不支持反向,得靠jq反转。第二条按创建时间排,找最新部署的 Pod 特别有用。
-l按标签筛选是日常高频操作。K8s 里几乎所有资源都带标签,-l就是按标签选择器过滤:
kubectl get pods -l app=my-app kubectl get pods -l 'env in (prod,staging)' kubectl get pods -l app=my-app,tier=frontend标签选择器支持=、!=、in、notin、exists几种写法。我踩过的坑是:-l后面的表达式如果带空格或特殊字符,一定要用引号包起来,否则 shell 会先解析,导致选择器失效。
-w是 watch 模式,会持续监听资源变化并实时刷新:
kubectl get pods -w滚动发布时我经常开一个终端跑-w,另一个终端执行rollout,这样能实时看到新 Pod 一个个起来、旧 Pod 一个个退出。比反复敲get高效得多。退出 watch 按Ctrl+C。
3. describe 与 logs:排障时先看哪个
3.1 describe 输出里真正该看的三段
kubectl describe会输出资源的详细信息和关联事件。很多人敲完看到一大坨文字就懵了,其实真正有用的就三段:Status、Conditions、Events。
kubectl describe pod my-pod-xxxStatus段告诉你 Pod 当前处于什么阶段(Pending、Running、Succeeded、Failed、Unknown)。如果是 Pending,基本可以断定是调度问题——资源不够、节点选择器不匹配、污点没容忍。这时候往下看Events,通常会直接告诉你“0/3 nodes are available: insufficient cpu”之类的原因。
Conditions段是 Pod 层面的健康状态,重点看Ready和ContainersReady。如果Ready是 False,说明就绪探针没过,服务不会接流量。这时候要去看容器的State,是Waiting还是Terminated。Waiting常见原因是镜像拉取失败(ImagePullBackOff)或配置缺失(CreateContainerConfigError);Terminated则要看退出码,ExitCode 137通常是 OOMKilled,ExitCode 1多半是应用自己报错退出。
Events段是排障的金矿,但默认只保留最近一小时的事件,而且数量有限。如果事件被刷掉了,可以单独查:
kubectl get events --sort-by=.metadata.creationTimestamp kubectl get events -n my-namespace --field-selector involvedObject.name=my-pod-xxx--field-selector按字段过滤事件,比翻 describe 输出更精准。我习惯在 Pod 反复重启时用这条命令,能快速看到最近几次失败的原因。
3.2 logs 的参数组合与多容器陷阱
kubectl logs看容器标准输出,但有几个参数组合必须掌握。
-f实时跟踪,等价于tail -f:
kubectl logs -f my-pod-xxx--tail只看最后 N 行,避免日志太多刷屏:
kubectl logs --tail=100 my-pod-xxx--since按时间过滤,比如只看最近十分钟:
kubectl logs --since=10m my-pod-xxx--previous看上一个崩溃容器的日志,这是排查 CrashLoopBackOff 的关键:
kubectl logs my-pod-xxx --previousPod 重启后,当前容器的日志是空的(因为刚起来),真正报错的信息在上一个容器里。不加--previous你会以为日志丢了,其实只是看错了地方。
多容器 Pod 必须用-c指定容器名,否则报错:
kubectl logs my-pod-xxx -c sidecar不知道容器名就先 describe 一下,Containers段会列出来。我见过有人对着多容器 Pod 敲了半天 logs 没输出,最后发现是没加-c,命令直接报错被忽略了。
还有一个组合是logs配合标签选择器,一次看一组 Pod 的日志:
kubectl logs -l app=my-app --tail=50 --prefix--prefix会在每行日志前加上 Pod 名,方便区分是哪个实例输出的。这个在排查“只有一个副本报错”的问题时特别好用。
3.3 先 describe 还是先 logs 的判断逻辑
这个问题我被问过很多次。我的判断逻辑很简单:Pod 没起来,先 describe;Pod 起来了但行为不对,先 logs。
Pod 处于 Pending、ContainerCreating、CrashLoopBackOff 这些状态时,容器可能根本没跑起来,logs 自然没东西。这时候 describe 的 Events 会告诉你卡在哪一步。反过来,Pod 是 Running 且 Ready,但接口返回 500,那问题在应用内部,直接看 logs 更快。
还有一种情况是两者都要看:Pod 反复重启。先用 describe 看退出码和事件,判断是 OOM 还是应用崩溃;如果是应用崩溃,再用logs --previous看崩溃前的堆栈。这个顺序能帮你少走弯路。
4. 改配置、扩缩容与滚动更新:别在生产环境手抖
4.1 edit 与 apply 的取舍
改资源配置有两条路:kubectl edit直接改线上,kubectl apply用文件改。我的原则是:临时调试用 edit,正式变更用 apply。
edit会打开默认编辑器(通常是 vi),改完保存即生效:
kubectl edit deployment my-app方便,但风险高。一是没有版本记录,改错了不好回滚;二是多人协作时容易覆盖别人的改动。我踩过的坑是:edit 打开后看到的是当前集群状态,如果这期间别人也改了同一个资源,你保存时会直接覆盖,没有任何冲突提示。
apply用声明式文件,配合 Git 做版本管理,是生产环境的正确姿势:
kubectl apply -f deployment.yamlapply的坑在于它只做“增量合并”,不会删除文件里已经移除的字段。比如你从 yaml 里删了一个环境变量,apply 之后集群里那个变量还在。要彻底同步,得用kubectl replace或者先 delete 再 apply。我一般用kubectl diff -f deployment.yaml先看差异,确认无误再 apply:
kubectl diff -f deployment.yamldiff会输出集群现状和文件内容的差异,相当于 apply 的预演。这个命令救过我很多次,强烈建议养成习惯。
4.2 scale 与 rollout:扩缩容和发布控制
scale调整副本数,简单直接:
kubectl scale deployment my-app --replicas=5但要注意,如果 Deployment 的 yaml 里写了replicas: 3,下次 apply 会把副本数改回 3。所以 scale 适合临时调整,长期变更还是要改 yaml。
rollout是发布管理的核心,几个子命令必须记住:
kubectl rollout status deployment my-app kubectl rollout history deployment my-app kubectl rollout undo deployment my-app kubectl rollout restart deployment my-appstatus看发布进度,会阻塞直到完成或失败。history看历史版本,配合--revision可以回滚到指定版本:
kubectl rollout undo deployment my-app --to-revision=2restart是滚动重启,不改变配置,只是让所有 Pod 重建一遍。这个命令在“配置没变但想让 Pod 重新拉镜像”时特别有用。我经常用它来触发一次干净的滚动,比如 Secret 更新后 Pod 不会自动重载,restart 一下就能让新配置生效。
rollout的坑在于:如果 Deployment 的strategy是Recreate,restart 会导致服务短暂中断。生产环境务必确认是RollingUpdate,并且配好maxUnavailable和maxSurge。
4.3 用 patch 做精准修改
patch是介于 edit 和 apply 之间的选择,适合脚本化地改单个字段:
kubectl patch deployment my-app -p '{"spec":{"replicas":5}}'patch支持strategic、merge、json三种类型,默认是strategic。改副本数、改镜像、加标签这些操作,patch 比 edit 安全,因为它只动你指定的字段,不会覆盖整个资源。
我常用的一个场景是改镜像:
kubectl set image deployment/my-app my-app=my-registry/my-app:v2set image本质就是 patch 的封装,比手写 patch 更直观。类似的还有set env、set resources,都是高频操作。
5. exec、port-forward 与 cp:进容器和连服务的正确姿势
5.1 exec 进容器的参数细节
exec在容器里执行命令,最常用的是开个 shell:
kubectl exec -it my-pod-xxx -- /bin/bash-it是-i(标准输入)和-t(分配终端)的组合,交互式操作必须加。--后面跟要执行的命令,这个分隔符不能省,否则 kubectl 会把命令参数当成自己的参数解析。
如果容器里没有 bash,用 sh:
kubectl exec -it my-pod-xxx -- /bin/sh多容器 Pod 同样要加-c:
kubectl exec -it my-pod-xxx -c sidecar -- /bin/shexec的坑在于:如果 Pod 没有Ready,exec 可能失败或行为异常。另外,exec 进去的环境变量和容器主进程不一定完全一致,因为主进程可能通过 entrypoint 设置了额外变量。排查环境变量问题时,最好直接看容器的/proc/1/environ。
5.2 port-forward 打通本地与集群
port-forward把本地端口转发到 Pod 或 Service 的端口,是本地调试的利器:
kubectl port-forward pod/my-pod-xxx 8080:80 kubectl port-forward svc/my-service 8080:80第一条把本地 8080 转发到 Pod 的 80,第二条转发到 Service 的 80。区别在于:转发到 Pod 时,流量直接进那个 Pod;转发到 Service 时,会经过 Service 的负载均衡,可能落到任意后端 Pod。
port-forward默认只监听localhost,想被其他机器访问要加--address:
kubectl port-forward --address 0.0.0.0 svc/my-service 8080:80这个命令在连数据库、调试内部 API 时特别有用。但它有个限制:只支持 TCP,不支持 UDP。而且连接不稳定时会自动断开,需要重新执行。我一般配合while true循环让它自动重连:
while true; do kubectl port-forward svc/my-service 8080:80; sleep 1; done5.3 cp 在容器和本地之间传文件
kubectl cp复制文件,语法类似scp:
kubectl cp my-pod-xxx:/var/log/app.log ./app.log kubectl cp ./config.yaml my-pod-xxx:/etc/config/config.yaml从容器往外拷,源在前;往容器里拷,源在后。多容器 Pod 要加-c。
cp的坑不少。一是它依赖容器里有tar,如果镜像极简(比如 distroless),cp 会失败。二是大文件传输慢且容易断。三是路径里的冒号是分隔符,Windows 路径带盘符时容易混淆。我的经验是:小文件用 cp,大文件或者容器没 tar 时,用exec配合cat重定向:
kubectl exec my-pod-xxx -- cat /var/log/app.log > ./app.log反过来往容器里写:
cat ./config.yaml | kubectl exec -i my-pod-xxx -- sh -c 'cat > /etc/config/config.yaml'这种方式不依赖 tar,兼容性更好。
6. 集群级排障:从节点到资源的完整链路
6.1 top 看资源占用
kubectl top看节点和 Pod 的 CPU、内存占用:
kubectl top nodes kubectl top pods kubectl top pods -A --sort-by=memorytop依赖 metrics-server 组件,如果报“metrics not available”,说明集群没装或者没配好。这个命令的输出是瞬时值,不是平均值,所以看到某个 Pod 内存飙高不一定代表它一直这么高,要结合--sort-by和多次采样判断。
我常用的组合是top pods -A --sort-by=memory,一眼看出哪个命名空间、哪个 Pod 最吃内存。排查 OOM 问题时,先看 top,再看 describe 里的 limits 和实际用量对比。
6.2 cordon、drain 与 taint:节点维护三件套
节点要维护时,先把 Pod 赶走:
kubectl cordon my-node kubectl drain my-node --ignore-daemonsets --delete-emptydir-datacordon标记节点不可调度,新 Pod 不会上来。drain驱逐节点上的 Pod,配合--ignore-daemonsets忽略 DaemonSet(因为 DaemonSet 本来就要在每个节点跑),--delete-emptydir-data允许删除使用 emptyDir 的 Pod(数据会丢,确认后再加)。
维护完恢复调度:
kubectl uncordon my-nodedrain的坑在于:如果 Pod 没有控制器管理(裸 Pod),drain 会拒绝驱逐,需要加--force。但强制驱逐裸 Pod 意味着它不会自动重建,数据也可能丢,生产环境慎用。
taint给节点打污点,配合 Pod 的toleration实现定向调度:
kubectl taint nodes my-node key=value:NoSchedule kubectl taint nodes my-node key=value:NoSchedule-末尾加-是删除污点。NoSchedule是不调度新 Pod,NoExecute还会驱逐不容忍的现有 Pod。这个机制在专用节点(比如 GPU 节点)上很常见。
6.3 用 api-resources 和 explain 查文档
K8s 资源类型太多,记不住很正常。api-resources列出所有资源类型:
kubectl api-resources kubectl api-resources --namespaced=false--namespaced=false只看集群级资源,比如 Node、PersistentVolume、ClusterRole。这个命令在写 RBAC 或者确认某个资源是否分命名空间时特别有用。
explain查字段文档,不用离开终端:
kubectl explain deployment.spec.strategy kubectl explain pod.spec.containers --recursive--recursive递归展开所有子字段,输出很长但很全。我写复杂 yaml 时经常用它确认字段名和类型,比翻官网快。
6.4 一个完整的排障链路示例
假设有人反馈某个服务 502。我的排查顺序是:
kubectl get pods -n my-ns -o wide看 Pod 状态和节点。- 如果有 Pod 不 Ready,
kubectl describe pod看 Events。 - 如果 Pod 都 Ready,
kubectl get svc my-service -o yaml看 Service 的 selector 和 endpoints。 kubectl get endpoints my-service看后端有没有正确注册。- 如果 endpoints 为空,说明 selector 不匹配或者 Pod 没 Ready。
- 如果 endpoints 正常,
kubectl logs看应用日志。 - 还不行就
kubectl exec进去,curl localhost:port确认应用本身是否正常。
这个链路覆盖了从调度到网络到应用的完整路径。每一步都有明确的判断依据,不会盲目乱试。
7. 把命令变成肌肉记忆的几个实操心得
7.1 别名与补全:让手速跟上思路
kubectl敲全拼太慢,我习惯设别名:
alias k=kubectl alias kgp='kubectl get pods' alias kgs='kubectl get svc' alias kd='kubectl describe' alias kl='kubectl logs'但别名有个问题:换台机器就没了。所以我会把这些写进~/.bashrc或~/.zshrc,并且用 Git 管理 dotfiles,换环境一键同步。
补全更重要。kubectl 自带补全脚本:
source <(kubectl completion bash)zsh 用户:
source <(kubectl completion zsh)补全不仅能补命令,还能补资源名、命名空间、字段。开了补全之后,敲kubectl get pod <Tab>会自动列出当前命名空间的 Pod 名,效率提升非常明显。
7.2 上下文与命名空间切换
多集群环境下,kubectl config是必备:
kubectl config get-contexts kubectl config use-context my-cluster kubectl config set-context --current --namespace=my-nsget-contexts列出所有上下文,use-context切换。set-context --current --namespace把当前上下文的默认命名空间改掉,省得每次加-n。
我踩过的坑是:切了上下文忘了切回来,对着测试集群敲了生产环境的命令。后来我养成了习惯,在 shell 提示符里显示当前上下文和命名空间:
export PS1='[\u@\h \W $(kubectl config current-context)]\$ '这样一眼就能看到自己在哪个集群,避免误操作。
7.3 输出格式化与常用组合
最后分享几个我高频使用的组合命令,都是实战中磨出来的:
查所有命名空间下不 Ready 的 Pod:
kubectl get pods -A --field-selector=status.phase!=Running查最近重启的 Pod:
kubectl get pods -A --sort-by=.status.containerStatuses[0].restartCount | tail -20批量删除 Evicted 状态的 Pod:
kubectl get pods -A | grep Evicted | awk '{print $1, $2}' | xargs -n2 kubectl delete pod -n这条命令要小心,先确认输出再执行。awk取命名空间和 Pod 名,xargs -n2每次传两个参数给 delete。
查节点资源分配率:
kubectl describe node my-node | grep -A5 "Allocated resources"这个输出能看到节点上所有 Pod 的 requests 和 limits 总和,判断节点是否过载。
这些命令单独看都不复杂,但组合起来能解决很多实际问题。我的建议是:不要试图一次记住所有命令,先把最常用的十条练到不用想就能敲出来,剩下的用到时再查。速查表的意义不是让你背,而是让你在需要的时候能快速找到,并且知道为什么这么用。