kubectl 实战指南:从入门到故障排查的常用命令详解
1. 环境准备与基本配置1.1 从零开始安装 kubectl 与版本匹配先聊点实在的。kubectl 是 Kubernetes 的 CLI 客户端所有对集群的操作本质上都是通过它发起请求由 API Server 完成鉴权和资源调度。也就是说你敲的每一条 kubectl 命令最后都会转化为 REST API 调用。这个机制理解透了后面遇到命令能跑但没生效的问题你就知道该往哪个方向查。安装方式直接按官方文档来就好Linux/macOS 用 curl 下载二进制Windows 用 choco 或者直接下载 exe。但我有一件事必须强调kubectl 版本和集群版本的差异不能太大。官方支持的范围是一个小版本内的差异比如 kubectl 1.28 可以管理 1.27 和 1.29 的集群。如果你用太老的客户端对接新集群APIServer 返回的字段这个老版本不认大概率会报错或字段解析失败反过来客户端版本太新也可能出现服务端不兼容的情况。# 查看 kubectl 客户端版本 kubectl version --client # 查看客户端和服务端版本 kubectl version这里有个细节值得注意kubectl version不加--client时会同时请求服务端如果你没有集群 kubeconfig 权限命令会卡在那里等你输入密码或者直接超时。所以排查网络问题时记得区分客户端版本和服务端版本不要一看报错就以为是集群挂了。1.2 kubeconfig多集群切换的命脉很多新手最大的困惑在于为什么我在服务器上执行 kubectl 命令操作的却不是我以为的那个集群答案就在 kubeconfig 文件的上下文context上。kubeconfig 默认路径是~/.kube/config也可以临时通过环境变量指定export KUBECONFIG/path/to/my/configkubeconfig 的核心结构是三个组合cluster集群地址CA、user客户端证书/token、contextclusterusernamespace 的组合映射。日常多集群运维时我强烈建议用kubectl config系列命令管理而不是手动改配置文件。# 查看当前的配置 kubectl config view # 查看所有上下文 kubectl config get-contexts # 切换上下文 kubectl config use-context prod-cluster一个实战习惯把生产环境和测试环境的 context 名称区分得非常明确比如prod-all、dev-namespace1。我见过太多人把测试环境命令直接怼到生产环境却没意识到。在use-context之后第一时间执行kubectl config current-context确认这句话能救你无数次。kubectl config current-context2. 资源查询命令全集2.1 get 命令的花式用法kubectl get是使用率最高的命令但很多人只停留在kubectl get pods这个层间。实际上它有非常多的参数组合用好了能大幅提升你的排查效率。先看最常规的# 查看所有 namespace 的 pod kubectl get pods -A # 查看指定 namespace kubectl get pods -n kube-system # 同时查看多种资源 kubectl get pod,service,deployment -n default-o wide是你排查网络问题时第一眼看的东西。它会额外输出 Pod IP、所在节点、命名空间等关键信息kubectl get pods -o wide再进阶一点按标签筛选资源label selector是生产环境最常用的定位手段。比如分布式应用的前端 Pod 都打了appfrontend标签你想要看完整的实例列表kubectl get pods -l appfrontend -A还可以用-l组合多个条件逗号表示 AND 关系kubectl get pods -l appfrontend,tierfront -n production最后是--field-selector这个参数很多人不熟悉它允许你按照资源字段进行过滤比如只看status.phasePending状态的 Podkubectl get pods --field-selectorstatus.phasePending -A--field-selector和-l是可以同时使用的。这个组合在排查集群里为什么有一堆 Pending 状态的 Pod时非常有用。2.2 describe、logs、events 组合排查get看的是资源的现状摘要而describe看的是资源对象的详细信息包括事件Events、标签、状态条件等。对于容器无法启动、镜像拉取失败、调度失败这类问题describe提供的信息远比get丰富。# 查看 Pod 详细状态 kubectl describe pod my-pod -n my-namespace # 查看节点信息如资源分配、系统版本、磁盘压力等 kubectl describe node node-1describe输出中最关键的是后面的 Events 部分。如果 Pod 一直卡在 ContainerCreating 状态Events 里会直接告诉你 Volume 挂载失败、Secret 缺失或镜像拉取超时等具体原因。很多时候光看事件就定位了问题根本不需要进容器。logs是查看容器输出的# 查看指定容器日志 kubectl logs my-pod -n default # 容器多实例时指定容器 kubectl logs my-pod -c my-container # 实时跟踪日志类似 tail -f kubectl logs -f my-pod # 查看之前崩溃的容器日志容器重启后日志会丢失 kubectl logs my-pod --previous这里有一个常见的逻辑问题--previous只能看上一次容器的日志。如果容器一直 CrashLoopBackOff每次重启都产生新的日志你想要对比崩溃前后的差异就可以先用它捞上一次的。在容器反复崩溃时我最常用的策略是先kubectl get pods确认 STATUS再用describe看原因最后用logs --previous瞄崩溃前的日志内容。这三个命令组合起来能覆盖大多数工作负载故障。3. 资源变更与删除操作3.1 apply、edit、patch 如何取舍资源创建和更新有几种方式各有利弊。kubectl apply是声明式 API 的标准用法也是生产环境使用最多的方式。它的核心逻辑是该资源期望的状态是什么如果资源不存在就创建存在则做差量更新。kubectl apply -f deployment.yaml kubectl apply -f ./manifest-directory/ kubectl apply -f https://example.com/deploy.yaml用-f加目录路径时会自动读取目录下所有 YAML 文件这个在批量部署时非常高效。但要注意如果同一个目录下有两个文件描述了同一个 Deployment后面的会覆盖前面的字段容易造成混乱我的习惯是一个目录只放同一套组件的文件。kubectl edit在你需要临时手动调整线上资源时最顺手它会打开默认编辑器保存后自动执行更新kubectl edit deployment nginx-deployment -n default但也要注意edit是基于当前集群中的实际对象状态去改的如果你手头有一个本地文件而集群状态已经是改过的直接用本地文件apply可能造成回滚。kubectl patch精确修改单个字段时效率最高不想要把整个 YAML 抄出来。比如把 Deployment 的副本数从 3 改成 5kubectl patch deployment nginx-deployment -n default -p {spec:{replicas:5}}对于复杂嵌套字段的更新patch 比 edit 更稳妥因为它不会误动其他字段。但这种写法对 JSON 语法有要求括号引号搞错了会直接报错。3.2 delete 与 ConfigMap 删除的特殊注意事项删除操作是高危操作生产环境最好加上-n指定命名空间。一个不小心把整个 namespace 删了里面所有资源全没了没多少人会感激你。# 删除单个资源 kubectl delete pod my-pod -n default # 删除多个资源 kubectl delete pod my-pod-1 my-pod-2 # 按标签批量删除 kubectl delete pods -l appfrontend # 删除 namespace 下所有特定类型的资源 kubectl delete deployment,svc -l appfrontend -n default热搜词里有kubectl 删除configmap这个关键词这里我专门展开说一下。ConfigMap 删除后正在使用它的 Pod 不会立即感知除非 Pod 被重新创建否则容器内挂载的还是旧数据。也就是说光删 ConfigMap 不重建 Pod你的配置变更不会生效。所以正确的操作顺序是# 1. 删除旧的 ConfigMap kubectl delete configmap my-config -n default # 2. 重新创建新的 ConfigMap kubectl create configmap my-config --from-fileconfig.yaml -n default # 3. 重启关联的 Deployment滚动重启 Pod kubectl rollout restart deployment my-app -n default有些人会问直接用kubectl rollout restart能解决 ConfigMap 更新不生效的问题吗答案是不一定。因为 rollout restart 只是重新创建 Pod它会重新读取 ConfigMap但如果名字没变内容变了Pod 重启后会加载新数据但如果整个 ConfigMap 已经被删除而 Deployment 又引用了它Pod 重启时反而会报 ConfigMap 找不到而挂起。在实际操作中我遇到过多次因为 ConfigMap 删了之后没有同步重建导致 Pod 一直 ContainerCreating 的情况。教训就是删除 ConfigMap 之前先确认哪些工作负载在引用它。# 查看 deployment/statefulset 是否引用了我准备删的 configmap kubectl get deployment -A -o jsonpath{range .items[*]}{.metadata.name}{\t}{.spec.template.spec.volumes[*].configMap.name}{\n}{end}这套 JSONPath 在博文后续部分还会展开讲先用起来。4. Pod 与集群故障排查实用命令4.1 exec、port-forward、cp 的日常组合进入容器内部排查问题这是故障处理的必经之路。# 进入容器交互式 shell kubectl exec -it my-pod -n default -- /bin/sh # 指定容器进入 kubectl exec -it my-pod -c my-container -n default -- /bin/bash # 执行单条命令不进入交互 kubectl exec my-pod -n default -- ls /app注意不同基础镜像里可用的 shell 不同busybox 里只有shUbuntu 镜像里可以bash。如果执行-- /bin/bash报 no such file换成/bin/sh往往就能进入。port-forward是把集群内服务映射到本地访问的调试利器。比如你本机没有集群内网的访问权限但想调一下某个 Service 暴露的 API# 将本地 8080 端口映射到 Pod 的 80 端口 kubectl port-forward pod/my-pod 8080:80 # 将本地端口映射到 Service kubectl port-forward service/my-service 8080:80port-forward的原理是建立一条到 API Server 的加密通道由 API Server 转发到目标资源。这个特性在云端集群中特别有用因为你的笔记本往往不在集群网络内。要注意它不是高性能通道只适合临时调试。文件拷贝方面用kubectl cp# 从 Pod 拷贝到本地 kubectl cp default/my-pod:/var/log/app.log ./app.log # 从本地拷贝到 Pod kubectl cp ./config.yml default/my-pod:/app/config.yml在实际排障中这三个命令的组合使用频率极高先用exec看进程状态再用cp把日志拉出来分析最后用port-forward联调。熟练了这组操作基本能覆盖绝大多数应用层问题的定位。4.2 节点与集群层的常用诊断先把节点的资源情况摸清楚# 查看节点状态 kubectl get nodes # 查看节点详细信息和资源分配情况 kubectl describe node node-1 # 获取节点资源使用情况 kubectl top node # 获取 Pod 资源使用情况 kubectl top pod -n defaultkubectl top依赖 metrics-server 插件如果集群里没装这条命令会报Metrics API not available。不装的话也可以用describe node里的 Allocated resources 来估算它显示的是 requests 和 limits 的分配量而不是实时用量。节点层面最常见的告警是磁盘压力或内存压力。此时查看节点日志基本不可能用 kubectl 直接看到需要登录到节点本身去查 kubelet 的日志。但 kubectl 那边一般会通过 Events 先反馈出来kubectl get events --all-namespaces --sort-by.lastTimestamp这个命令会按时间倒序列出所有事件。注意--sort-by的字段是.lastTimestamp如果你直接复制网上的某些写法写成.metadata.creationTimestamp也勉强能行但是事件更新的维度就不那么准确了。4.3 高频故障速查表根据自己的实操经验我把过去两年里在群里被问烂的故障整理成了速查表。这张表可以贴在工位上出了问题对着查效率很高。现象常见原因排查命令解决方案Pod 一直 Pending节点资源不足、调度约束不满足kubectl describe pod pod看 Events扩容节点、调整 request/limit、检查 nodeSelector 与亲和性ImagePullBackOff镜像名错误、仓库认证失败、镜像不存在kubectl describe pod pod看 Events先手动docker pull验证镜像是否存在再检查 imagePullSecretCrashLoopBackOff容器启动即退出、健康检查失败、环境变量缺失kubectl logs --previous看崩溃日志修启动命令、补全配置、移除错误的环境变量ContainerCreating 很长时间存储卷挂载失败、Pull 镜像超时、ConfigMap/Secret 不存在kubectl describe pod pod看 Events检查 PVC/PV 状态、确认引用的 CM/Secret 是否存在Pod 能创建但无法访问 Service 的 ClusterIPService selector 与 Pod label 不匹配kubectl describe endpoints svc对比 service.spec.selector 与 pod label确保一致Evicted节点内存/磁盘压力过大kubectl get pods --field-selectorstatus.phaseFailed -A清理节点资源、调整 requests/limits节点 NotReadykubelet 停止心跳、网络分区间断kubectl describe node node查看 ConditionsSSH 登录节点看 kubelet/Docker/Containerd 服务状态这张表我每次培训新人都用建议你也复制一份放进日常工作笔记。遇到故障先按表格对照没有命中再上 describe 和 logs 去深挖。5. 输出格式化与脚本化效率技巧5.1 -o 参数穷举json、yaml、jsonpath、wide-o是 kubectl 最强大的输出参数之一能不能玩转它几乎决定了你离普通用户还是资深运维的差距。# 以 YAML 输出资源清单可另存为文件做备份 kubectl get deployment nginx -n default -o yaml # 以 JSON 输出适合程序化处理 kubectl get pods -n default -o json # 只输出 Pod IP 列表 kubectl get pods -n default -o jsonpath{.items[*].status.podIP}YAML 和 JSON 输出常用于将线上资源导出为备份文件或者分析资源对象的嵌套字段。很多人在做资源迁移时直接用-o yaml backup.yaml导出再在新集群里apply这个操作简单但很实用。jsonpath的使用场景更广泛比如我只想看集群里所有 Pod 所在节点的分布kubectl get pods -A -o jsonpath{range .items[*]}{.metadata.name}{\t}{.spec.nodeName}{\n}{end}JSONPath 的语法和 jQuery 的选择器类似{range .items[*]}是遍历{\n}是换行。一开始用会有点绕但熟练后你完全可以从 kubectl 输出中提取精确的信息不用再 grep 一大堆文字。5.2 custom-columns 自定义表格输出custom-columns 允许你定义输出的列和字段相当于自己设计一个速览表。我只想看到 Pod 的名字、IP、所在节点、运行时长kubectl get pods -n default -o custom-columnsPOD:.metadata.name,IP:.status.podIP,NODE:.spec.nodeName,AGE:.metadata.creationTimestamp这个功能在批量观察环境时非常好用比如你同时管理几十个 namespace想要一眼看出哪个 Pod 是 Running、哪个 CrashLoopBackOffkubectl get pods -A -o custom-columnsNS:.metadata.namespace,POD:.metadata.name,STATUS:.status.phase,NODE:.spec.nodeName和jsonpath相比custom-columns 语法上的可读性更高适合生成报表或巡检报告。我个人在高频巡检脚本里就是用它把数据拼到表格里再配合邮件发送。5.3 常用 alias 与脚本化批处理单条命令再好用天天敲一长串也累。这里分享一组我日常必备的 alias建议直接写进~/.bashrc# 常用缩写 alias kkubectl alias kgpkubectl get pods alias kgpakubectl get pods -A alias kdnkubectl describe node alias kdpkubectl describe pod alias klkubectl logs alias kexkubectl exec -it alias kpfkubectl port-forward # 查看所有 ns 中报错/非 Running 的 pod alias kbadkubectl get pods -A | grep -v Running | grep -v Completed # 进入 Pod 的快捷方式 alias kshkubectl exec -it $(kubectl get pods -n default | grep Running | grep -v Completed | head -1) -- /bin/sh这些 alias 在短期会话中提升效率明显但也不要过度依赖因为换台机器或换账号就没了。我更推荐你掌握长命令能理解的能力alias 只是加快手速。脚本化方面批量操作时用循环结构会很省事。比如要批量重启某个 namespace 下所有 Deploymentfor deploy in $(kubectl get deployment -n default -o name); do kubectl rollout restart $deploy -n default done6. 线上操作时必须留意的几件事6.1 小心 --force 和 --grace-periodkubectl delete默认会有优雅删除机制会等容器内的进程收到 SIGTERM 后自己退出默认宽限时间是 30 秒。有些命令教学里会让你加--force --grace-period0强制删除。这个方法在某些场景确实有效比如 Pod 卡在 Terminating 状态删不掉时。但千万不要在正常环境中随手就加。强制删除意味着跳过优雅退出过程直接告诉 API Server 立即清理 Pod。如果你的应用依赖优雅停机去做状态保存、连接池清理、注册中心反注册那加了这个参数后很容易出现数据不一致或连接残留的问题。我见过有人批量强制重启 Pod结果线上服务间歇性不可用最后排查发现就是这条命令的问题。6.2 namespace 切分的习惯很重要操作时永远先确认当前 Namespacekubectl get pods如果你的 default namespace 没有资源而实际资源都在prod-biz里光看不带-n的输出会让你误以为集群空了。这是新手最容易犯的错。给每条命令习惯性带上-n namespace是我见过的所有老司机都遵循的共同习惯。不管多熟都别省。6.3 生产环境的删除操作先打草稿关于删除操作我的个人规矩是先做成 dry-runkubectl delete deployment nginx -n default --dry-runclient--dry-runclient只是模拟执行不会真正发起删除请求。确认无误后再执行真实的删除。同样创建资源也可以先--dry-runclient -o yaml来看最终生成的 YAMLkubectl create deployment dry-demo --imagenginx --dry-runclient -o yaml这一步能帮你验证集群的默认值、标签、字段修正等避免直接 apply 后才发现字段值不符合预期。7. 深入一点kubectl 与 CRI/containerd 之间的调用链路前面说了 kubectl 是客户端那它和底层容器运行时之间到底是什么关系很多人把kubectl 命令和容器运行时操作混为一谈这里拆开讲。整个调用关系可以这样理解kubectl→API Server→kubelet→CRI (Container Runtime Interface)→containerd→ 容器进程kubectl 发出的所有 Pod 创建/删除/日志请求都会先到达 API Server。API Server 通过 etcd 完成资源对象的持久化随后把指令下发到对应节点上的 kubelet。kubelet 才是那个真正和容器运行时打交道的组件它通过 CRI 接口gRPC 协议与 containerd 通信。所以你在 kubectl 里执行kubectl exec -it pod -- /bin/sh实际调用链是kubectl → API Server → kubelet → CRI → containerd → 容器运行时 → /bin/sh 进程这个链路提示了什么如果你用docker ps或ctr c ls能看到容器但 kubectl 访问不了那问题多半出在 API Server 的鉴权、RBAC、或者 kubelet 与 containerd 之间的连接上而不是容器本身没起来。反之kubectl 里 Pod 显示 Running但exec进不去可以检查是 CRI 配置了受限命令还是权限不够。理解这条链可以帮你把kubectl 命令不好使的搜索范围快速缩小。排查时有一个口诀先看 kubectl 能不能访问 API Server再看 kubelet 是否健康最后才去看 containerd/容器本身的状态。层级清晰定位自然快。8. 命令速查把常用的都放进一张大表为了日常速查方便我把自己最常敲的命令整理成了一张速查表。这跟之前故障表不同它是按操作目的组织的更适合背下来或者贴在终端旁边。操作目的命令示例查看当前上下文kubectl config current-context切换上下文kubectl config use-context ctx-name查看所有命名空间kubectl get namespaces查看某命名空间下所有资源kubectl get all -n namespace查看 Pod 列表含 IP、节点kubectl get pods -o wide -n namespace查看 Service 列表kubectl get svc -n namespace查看 Deployment 列表kubectl get deploy -n namespace查看 ConfigMapkubectl get cm -n namespace查看 Secretkubectl get secret -n namespace查看 PVCkubectl get pvc -n namespace查看节点资源kubectl top node查看 Pod 资源占用kubectl top pod -n namespace查看事件kubectl get events --sort-by.lastTimestamp -n namespace查看 Deployment 滚动状态kubectl rollout status deployment/name -n namespace重新启动 Deploymentkubectl rollout restart deployment/name -n namespace查看历史版本kubectl rollout history deployment/name -n namespace回滚到上一个版本kubectl rollout undo deployment/name -n namespace创建资源kubectl create -f xxx.yaml更新资源声明式kubectl apply -f xxx.yaml删除资源kubectl delete -f xxx.yaml查看资源详情kubectl describe resource name -n namespace进入容器kubectl exec -it pod -n namespace -- /bin/sh查看容器日志kubectl logs pod -n namespace跟踪容器日志kubectl logs -f pod -n namespace端口转发kubectl port-forward pod/pod 8080:80复制文件到 Podkubectl cp ./file.txt namespace/pod:/tmp/file.txt从 Pod 复制文件到本地kubectl cp namespace/pod:/var/log/app.log ./app.log输出 YAMLkubectl get resource name -o yaml输出 JSONkubectl get resource name -o json自定义列输出kubectl get pods -o custom-columnsNAME:.metadata.name,IP:.status.podIP优雅删除默认30skubectl delete pod pod --grace-period30强制删除谨慎kubectl delete pod pod --force --grace-period0这张表我不建议直接全文背因为你真正用到的可能只有五分之一。但把它保存好每次忘记参数时翻一下几周后你自然就记住高频的那些了。9. 避坑心得这些细节我踩过很多次最后分享几个真正从线上教训里沉淀出来的心得想到哪写到哪但对新人来说绝对有用。第一永远不要相信不加 -n 的命令会对所有 namespace 生效。很多人以为kubectl get pods会列出所有 Pod其实是只列当前 context 默认 namespace 下的 Pod。想要全量数据必须加-A。这个误解导致的问题轻则误判资源数量重则你删错了 namespace 里的资源还以为删的是测试环境。第二kubectl 和容器的退出码是有区分的。我在写脚本时会把 kubectl 命令本身的退出码和容器内命令的退出码分开判断。比如kubectl exec pod -- /bin/sh -c exit 3 echo $?这条命令最终返回的可能是 1kubectl 报错或 3容器内的退出码取决于 kubectl 和 APIServer 的交互。写自动化编排时一定要区分容器内命令的状态和 API 调用的状态否则脚本判断会出错。第三优先使用 rollout restart 而不是手动删 Pod。如果目的是让 Deployment 里的 Pod 重新加载配置永远用rollout restart这样会触发滚动更新而不是瞬间全部杀掉再创建对服务的冲击小得多。第四操作前先确认镜像拉取策略。imagePullPolicy是 Always 还是 IfNotPresent直接决定你在本地没有镜像时能不能顺利创建 Pod。而如果你给 Deployment 改了镜像 tag 但没改 imagePullPolicy测试环境容易遇到明明改了镜像还是旧版本的奇怪问题。排查时一定先看这个字段。第五定期导出资源清单做备份。很多人在集群出事故后才想到没备份。但 Kubernetes 资源本身就是声明式的用kubectl get deploy,svc,cm -n namespace -o yaml backup.yaml定期导出成本极低关键时刻能救命。没有 infra 团队的小团队我建议至少每周导一次。写到这儿我把日常用得上的 kubectl 命令和细节都梳理了一遍。这个工具并不难绝大多数命令的语法结构都一致真正让人翻车的地方往往就是对上下文、命名空间和资源状态的粗心。你把这些习惯刻进肌肉记忆里再来解决集群问题效率会提升一个量级。
