每天打开终端我敲得最多的命令就是kubectl。这十几年不管是帮同事排查 Pod 反复重启还是给新服务做滚动发布又或者是半夜爬起来看集群为什么报警最后解决问题的关键动作几乎都落在kubectl这行命令上。但坦白说大部分人用kubectl是靠记几个“看起来能用的命令”过日子。真被问到--dry-run和--server-side的区别或者为什么apply比create更推荐不少人其实答不上来。这不是谁的问题——网上搜到的“kubectl 命令大全”大多只是把kubectl help的输出抄了一遍看完还是不知道什么场景该用哪条。所以这篇不想再做命令列表的搬运工。我想按真实的运维场景把kubectl最常用、最容易被误解、也最高频踩坑的命令串一遍顺带把背后的匹配逻辑和我的排查习惯写清楚。适合刚接触 Kubernetes 想系统上手的人也适合已经会在测试集群里get pods但一到生产环境就心里发虚的开发者。1. 先认识 kubectl 的底层身份它不是一个万能工具箱而是一个 API 客户端1.1 kubeconfigkubectl 的“通讯录”很多人把kubectl当成一个“操作 Kubernetes 的万能遥控器”以为kubectl get pods就是让某个节点把 Pod 列出来。但真实逻辑完全不是这样。kubectl本质上是 Kubernetes API Server 的 HTTP 客户端。每一条kubectl命令最后都会变成向 API Server 发一个 HTTP 请求GET资源、POST创建、PATCH更新、DELETE删除。API Server 校验权限、做准入控制再决定把结果返回给你。也就是说kubectl本身不碰 Pod、不连容器运行时它只是站在集群外面跟集群的“大脑”对话。这个认知很重要。很多新手排障时盯着 worker 节点看半天却忘了先确认自己的请求到底有没有到达 API Server、有没有被权限拦截。我就见过有人在一台网络不通的机器上执行kubectl get nodes报错 timeout 后第一反应是“集群挂了”其实他的 kubeconfig 指向的 API Server 地址根本访问不了。kubectl连哪个集群、用哪个身份、进哪个命名空间全都由 kubeconfig 决定。默认情况下这个文件在$HOME/.kube/config里面拆成三块clusters保存集群 API Server 的地址和证书信息users保存你的身份凭据contexts则是把某个cluster和某个user组合在一起顺便可以指定默认的 namespace。clusters: # 集群列表 - name: prod cluster: server: https://api.example.com contexts: # 上下文 - name: prod-ops context: cluster: prod user: prod-admin namespace: default users: # 身份 - name: prod-admin user: token: xxxxx你可以先跑一行命令看看自己现在连的是谁kubectl config current-context输出类似prod-eks-admin后面接get类命令时所有请求都会默认发到这个上下文对应的集群上。老手和新手的差别很多时候只是先跑这么一行。1.2 上下文切换多集群日常的第一道坎如果你手上有开发、测试、预发、生产好几个集群上下文切换一定是你每天的基础操作。命令其实就那么几条kubectl config get-contexts # 列出所有上下文 kubectl config use-context 名字 # 切换到目标上下文但这里有个细节值得多说一句切换上下文只是让“后续执行的命令”使用新集群已经跑起来的命令、已经开着的端口转发、正在watch的终端都不会因为切换而改变。有人一边watch生产环境 Pod一边切到测试集群做练习然后发现生产窗口里还在刷新就以为自己切错了环境——其实这是两个独立的会话watch在启动那一刻就固定了上下文。另一个高频问题是默认 namespace 混乱。use-context只是切换集群不会自动帮你把 namespace 切到目标环境。比如你切到测试集群默认 namespace 可能还是上一个集群的某个名字执行kubectl get pods时看到的是一堆不存在的资源很难受。此时可以这样固定kubectl config set-context --current --namespacedev之后这个上下文下的默认 namespace 就是dev了。我自己的习惯是给每个环境单独建一个上下文命名直接带上环境名和区域比如test-ops、prod-az1避免use-context时打错一个字。上下文名字这种东西没人管你怎么起起得越有规律越不容易误触。2. 高频命令场景拆解看状态、查问题、做变更2.1 日常巡检三板斧get、describe、top如果你只记得一个kubectl子命令那一定是get。它用来查看集群里几乎任何资源的状态Pod、Deployment、Service、Node、ConfigMap都靠它。最基础的用法kubectl get pods不带命名空间时只看当前上下文默认 namespace 下的 Pod。想看全部命名空间加-A或者写全称--all-namespaces。想看到更多列比如节点 IP、Pod IP、所在节点就加-o wide。有一点很多人不知道get的输出列其实是“按条件筛选”的紧凑视图很多关键字段不会默认显示比如 Pod 的 restart 次数、QoS 等级、被调度到哪台节点。所以巡检时我通常用-o wide如果资源数量多再加-w做持续观察比如kubectl get pods -w这个命令会一直刷新直到你按 CtrlC 退出。它是排查滚动发布和异常重启时的第一手工具。get只是把资源的状态拉出来但“为什么会变成这个状态”就得靠describe。describe会返回一个资源的完整详情调度事件、挂载卷、环境变量、容器状态、探针检查结果、最近事件全都揉在一起。比如get看到某个 Pod 是Pendingdescribe里很可能写着pod failed to bind node: node(s) had insufficient memory一眼就能定位是资源不足。kubectl describe pod pod-name顺便提一下top。kubectl top node和kubectl top pod分别看节点和 Pod 的实时 CPU/内存占用。前提是集群里装了 metrics-server 这类指标采集组件没有的话会报metrics not available。top在定位“哪台机器被打满”的时候特别有用配合get nodes -o wide可以快速判断是不是某节点负载失衡。2.2 Pod 故障排查四件套logs、exec、port-forward、events当 Pod 已经 Running 但业务不对或者 CrashLoopBackOff 来回重启我会按这个顺序上手日志、exec、端口转发、事件。日志一定是最先看的kubectl logs pod-name常用参数里-f是跟随模式持续输出类似tail -f--tail200只取最近 200 行日志量大时必用-p或者--previous是看上一个容器实例的日志对 CrashLoop 特别有用因为当前容器可能已经重启过默认logs拿到的可能是新进程的输出加上-p才能看到上一次崩溃前写了什么。进入容器也经常不能省kubectl exec -it pod-name -- /bin/sh这条命令能让你钻进容器里看实际状态进程有没有起来、监听端口是多少、配置文件对不对、依赖的服务通不通。这里有个小坑容器镜像里不一定有bash很多精简镜像只有sh所以我的默认写法永远是-- /bin/sh。port-forward是把集群内的 Pod/Service 端口映射到本地方便调试不通外网的业务kubectl port-forward svc/my-service 8080:80表示把本地8080端口转发到my-service的80端口之后直接在浏览器里访问localhost:8080就能测试。它的本质是在本地和集群之间建一条隧道不要求 Service 已经暴露了 NodePort 或 LoadBalancer 类型。最后一招是看事件kubectl get events --sort-by.lastTimestamp列出命名空间下最近发生的事件加-A可以跨 namespace。CrashLoopBackOff、镜像拉取失败、探针失败这类问题事件里通常都会留下明确记录。2.3 工作负载变更操作apply、scale、rollout、delete 的边界看和查之外日常更关键的是变更操作。kubectl create和kubectl apply是两种不同的心智模型create是“创建”命令资源不存在时才工作存在了再执行就会报错apply是“声明期望状态”资源不存在就创建存在就把差异合并进去。对生产环境我几乎只用apply因为它的幂等性让你可以反复执行同一个清单文件不会因为粗心而重复创建资源。标准的发布动作kubectl apply -f deploy.yaml配合--dry-runclient -o yaml可以在不真正改动集群的前提下把将要生成的 YAML 打印出来。我写新清单的时候经常先跑一遍 dry-run 确认字段没写错再真正apply。扩缩容就用scalekubectl scale deployment myapp --replicas5如果你改了镜像 tag 想触发重新拉取但配置没有变化可以这样kubectl rollout restart deployment myapp这会触发一次滚动重启。相关的还有rollout status看发布是否完成rollout history看发布历史rollout undo可以回滚到上一个版本。再说delete。新手常犯的错是直接kubectl delete pod name以为这就等于重启。如果你这个 Pod 是 Deployment 或 StatefulSet 管的删除后控制器会立刻帮你重建一个所以看起来就像“重启了”但如果这个 Pod 是裸 Pod没有关联任何 ReplicaSet那删了就真的没了不会再回来。delete还有两个危险参数--force和--grace-period0。它们会让 API Server 跳过优雅退出过程直接删。能用默认的优雅删除就别碰这两个参数容器里的进程那点清理逻辑是有原因的。3. 输出格式化的价值让人和脚本都能读懂 kubectl 的结果3.1 从 -o wide 到 custom-columns定制输出视图日常命令里最容易被忽略的其实是-o参数。同样是get pods默认输出只有NAME/READY/STATUS/RESTARTS/AGE五列信息量非常有限。加上-o wide之后多了节点、IP再往后还有-o json、-o yaml、-o name等很多种格式。对不想看 JSON 那一大坨 YAML 的人来说custom-columns是个好东西。它允许你只挑关心的字段按自己的表头打印kubectl get pods -o custom-columnsNAME:.metadata.name,IP:.status.podIP,NODE:.spec.nodeName,STATUS:.status.phase输出就是一个干净的小表格只包含这四列。这在维护几十个 Pod 的场景下特别好用。命令里写死的字段名是从资源对象的 JSON 结构里取的可以先跑kubectl get pod name -o json看结构再挑字段。还有-o name也很实用它只输出资源类型和名字比如pod/nginx-xxx-123这玩意在 shell 脚本里配合xargs特别顺手后面会讲。3.2 JSONPath 与 jq复杂筛选的正确姿势当你希望按条件过滤多个资源时custom-columns不够用就得考虑输出 JSON 后处理kubectl get pods -o json再用jq提取信息。比如想挑出所有不在 Running 状态的 Pod 名字kubectl get pods -o json | jq .items[] | select(.status.phase ! Running) | .metadata.name想统计某个命名空间下 Ready 的 Pod 数量kubectl get pods -o json | jq [.items[] | select(.status.conditions[]?.typeReady and .status.conditions[]?.statusTrue)] | lengthjq的强大之处在于管道、过滤、计数、排序都能一行搞定。注意最后那个?是容错语法避免某些 Pod 没有conditions时报错。不想装jq的机器也可以用kubectl自带的-o jsonpath只是表达式相比jq不那么直观。比如kubectl get pods -o jsonpath{.items[*].metadata.name}会输出所有 Pod 名但默认不带换行挤在一起。我的建议是只在很简单的字段提取时用jsonpath复杂筛选一律交给jq别拿jsonpath硬撑。3.3 dry-run 和 explain不碰集群也能写清单还有两个能让命令行价值翻倍的小命令我几乎天天用kubectl explain和--dry-run。kubectl explain pod.spec.containers这个命令可以查看资源对象某个字段的说明、类型、取值范围写 YAML 的时候比翻文档快。它数据直接来自 API Server 的 OpenAPI 描述所以永远和集群版本保持一致。kubectl create deployment web --imagenginx:1.25 --dry-runclient -o yaml会在本地生成一个 Deployment 清单不提交给集群。这样你可以快速得到一份“最小可用清单”再手动补字段。我自己写新服务的首个 YAML 时通常是create加--dry-run生成骨架然后explain查字段含义最后apply前再用--dry-runserver让 API Server 做一次真正的校验。这比盲写一版然后反复apply撞错误要高效得多。4. 那些让我半夜被叫醒的 kubectl 坑附完整排查链路4.1 CrashLoopBackOff 案例logs 正常不代表没问题有一次同事半夜找我说服务的日志特别干净但 Pod 就是一遍遍重启。他甩过来一条kubectl logs的输出确实只有一行应用启动日志后面就没有了。我让他先跑kubectl describe pod在 Events 里看到了这条Liveness probe failed: HTTP probe failed with statuscode: 500原来这个 Pod 的存活探针一直探测失败失败次数超过阈值就被 Kubelet 杀掉重启。应用自己以为起来就行但探针检查的是健康接口接口返回 500整个 Pod 就被判了死刑。这个例子想说什么logs告诉我们容器里输出了什么describe和events告诉我们为什么 Kubelet 认为这个容器不健康。排查链路应该是get看状态 →describe看条件和事件 →logs看业务输出 → 如果还不行再exec进容器看现场。顺序不能乱大部分问题在describe阶段就能锁定方向。4.2 不存在 restart 命令重建与滚动重启的最佳实践我敢说每个 K8s 新手都在终端里敲过kubectl restart pod ...然后收到一条Unknown command。因为在大多数人的直觉里服务出现异常第一反应是重启一下。但kubectl并没有restart这个子命令这是设计使然Kubernetes 认为资源状态是持续期望的不需要人工“重启”。如果你想重启一个有控制器托管的 Pod做法是删掉它让控制器重建kubectl delete pod name但注意当只有一个副本时删掉再重建的中间过程会有一个短暂不可用窗口。对无状态服务我更推荐kubectl rollout restart deployment/name这会触发一次滚动重启先起新 Pod等 Ready 后再回收旧 Pod全程服务不中断。StatefulSet 也可以用同样命令。还有一个容易踩的坑是这样直接用kubectl scale deployment myapp --replicas0然后马上又scale回 1以为可以强制重启。这种方式在某些情况下能行但会把扩缩容和重启混在一起稍有不慎就走形了。规规矩矩用rollout restart别搞骚操作。4.3 上下文切错之后的应急预案多集群操作里最可怕的不是命令不会写而是切错了上下文。我自己就吃过一次亏当时某个团队让我在测试环境删掉一批过期的临时命名空间我敲了use-context切到“以为是对的”测试集群然后执行了delete。几秒钟后收到消息我把另一个正在用的环境里一批资源删了。为什么因为当时的测试集群和那个环境用的是同一个 kubeconfig上下文名字长得几乎一样少打了一个字符。从那天开始给自己定了三条规则第一执行任何删除类命令之前先跑kubectl config current-context确认当前上下文。第二尽量不用use-context切换而是在每条危险命令后面显式加--context比如kubectl delete ns xxx --context test-ops这样即使切错命令也会被你写在参数里的上下文约束住。第三把危险命令包装成 shell 函数函数里先打印上下文再要求二次确认kdel() { echo Current context: $(kubectl config current-context) read -p 确认要删除 $* ? (yes/no) ans [ $ans yes ] kubectl delete $ }这套东西看起来麻烦但它能救命。大部分人出事故不是技术不行是操作节奏太快没有给自己的手加上保险栓。4.4 批量操作与引号陷阱慎用循环和 grep最后聊一个批量操作的坑。很多人想要“把所有状态为 Evicted 的 Pod 删掉”时第一反应是这么写kubectl get pods | grep Evicted | awk {print $1} | xargs kubectl delete pod这条命令在简单场景下也许能跑通但风险不少如果某个 Pod 名字里带了奇怪字符awk取列可能错位如果正好某一行有额外字段还可能把不是 Pod 的东西也当成目标。更安全的方案是用 label 或者字段选择器比如kubectl delete pod -l applegacy或者kubectl delete pod --field-selectorstatus.phaseFailed这类命令让kubectl自己去精确匹配你不需要在 shell 层做文本解析。如果你一定要用管道循环至少先加--dry-run验证。比如kubectl get pods -o name | xargs kubectl delete --dry-runclient -o name看到输出符合预期再把--dry-run去掉。还有一个非常隐蔽的坑是 shell 引号。执行kubectl get pods -o jsonpath{.items[*].metadata.name}时如果单引号写成了双引号或者干脆丢了引号花括号就会被 shell 当成特殊字符处理要么报错要么输出一串莫名其妙的值。这种问题新手特别容易忽视因为命令看起来明明一模一样就是跑不通。最后说一个我雷打不动的习惯不管多着急只要是批量删除、上下文切换、生产配置更新这三类操作我一定会先跑一条只读命令把当前环境和目标列表确认一遍再动手。你可以笑我慢但半夜接电话总比写事故报告舒服。kubectl的命令深入学习下去其实没有尽头我也是在一次次踩坑里才慢慢把“知道命令”变成了“会判断该用什么命令”。希望这篇从实战角度拆出来的命令大全能帮你少走点弯路。
阅读完成 · 觉得有帮助?