资讯动态

K8s排障实战:从kubectl高频命令到问题定位思路

发布时间:2026/10/21 6:37:13 来源:尧图企业网站定制
说实话K8s的命令并不难记真正难的是不知道怎么把这些命令串成一套处理问题的思路。很多人把“K8s常见操作命令”当成一份背诵清单对着文档抄来抄去结果一到线上环境Pod起不来、Service不通、镜像拉不下来还是不知道先用哪条命令、再看哪个输出。我自己带过不少刚接触K8s的同事发现他们最缺的不是命令本身而是缺少一条主线先查状态、再改期望、最后看结果出了问题再顺着日志和事件往下挖。这篇文章就围绕这条主线把日常用到的高频kubectl命令、常见参数和实际踩过的坑整理一遍。适合刚上手K8s的运维和开发也适合那些已经会用一点、但还没形成自己排障习惯的人。1. 先把kubectl调顺手语法骨架、上下文与自动补全1.1 一条命令的骨架其实只有三部分kubectl的大部分命令都可以拆成同一个骨架kubectl 操作 资源类型 [资源名称] [参数]。操作就是get、describe、create、apply、delete、logs、exec这类动词资源类型就是pod、deployment、service、configmap、secret这些对象。资源类型是支持缩写和复数的比如pods、pod、po都可以表示Poddeploy可以表示Deploymentsvc可以表示Servicecm表示ConfigMappv和pvc分别表示持久卷和持久卷声明。刚开始的时候不需要记全所有缩写常用的就那么几个用多了自然就熟了。理解了骨架之后有一个习惯我会反复跟身边的人强调在敲命令之前先想清楚这步操作到底在干什么。想一眼扫过一批对象的状态用get加输出格式想知道某个对象为什么起不来用describe看事件想改集群里的期望状态用apply提交清单想确认应用实际打印了什么用logs。绝大多数情况下一条命令就是一次“问询”对象是哪个、关注哪个字段、要不要跨命名空间不过就是参数的区别。1.2 上下文和命名空间比命令本身更常踩的坑很多新手在本地练习的时候一切正常一到公司集群就发现命令总是报NotFound原因多半是上下文指错了集群或者命名空间没对。先确认自己当前在哪个集群kubectl config current-context。要切换集群用kubectl config use-context 上下文名称。如果你手上有多个环境的kubeconfig文件可以通过环境变量KUBECONFIG把它们临时合并起来比如KUBECONFIG/path/a/config:/path/b/config kubectl config get-contexts这样就能在一个终端里切来切去不用反复覆盖默认配置。命名空间是另一个高频坑。K8s集群里面很多资源是按命名空间隔离的kubectl get pods默认只查当前上下文里配置的那个命名空间如果你所在的命名空间是空的就会看到No resources found但这不代表集群里没有Pod。我排查问题的时候习惯先跑一下kubectl get pods -A-A是--all-namespaces的缩写先把所有命名空间的Pod扫一眼确认问题到底出在哪一块。记住一个判断规则如果某个资源怎么也查不到先别怀疑它不存在先用-A或者指定-n命名空间再查一遍。1.3 get与describe两种“查状态”的视角配合着用get和describe非常容易混淆但它们的定位完全不同。get是拉一个列表适合快速扫描比如看所有Pod是不是都成了Running看Deployment期望副本数和实际副本数是否一致。describe是看单个对象的详细内容除了基本信息之外还会给出事件列表这些事件往往是排查问题的关键入口。kubectl get pods -n default kubectl get deploy my-app -o wide kubectl describe pod my-app-7d8f9b6c5d-abcdeget可以配合-o参数调整输出格式-o wide会显示节点、IP、所在节点等更多字段-o yaml会输出该对象的完整定义-o json输出JSON格式。很多人用-o yaml只是为了看配置但其实它还有一个更常见的用途当你想知道某个资源当前的确切状态又不想一个个字段去翻的时候直接输出完整YAML再用编辑器搜索关键词效率很高。describe我通常只在两类场景下用一是Pod一直Pending或者CrashLoopBackOff需要看事件二是在搞不清楚这个对象被哪些控制器管理、打过哪些标签、挂载了哪些卷的时候想快速拿到一份“体检报告”。所以我的习惯是先get定位再describe深入这两个命令配合起来基本能覆盖一大半的日常状态查询需求。1.4 自动补全和别名花三分钟配置长期受益kubectl的命令虽然骨架统一但参数和资源名还是容易打错。K8s官方提供了自动补全脚本装好之后Tab键就能补全命令、资源类型和名称强烈建议配置。Bash和Zsh略有不同Zsh用户执行source (kubectl completion zsh)Bash用户执行source (kubectl completion bash)可以把这行写进~/.bashrc或~/.zshrc这样每次打开终端都自动生效。另外我给kubectl起了一个简短的别名k每天至少省几十次按键alias kkubectl有人还会继续给常用组合起别名比如kgp表示kubectl get podskgn表示kubectl get nodes。我不建议一开始就搞一大堆别名因为记忆成本会抵消效率收益。先把原生命令练熟等你敲得足够多、形成肌肉记忆了再酌情精简。2. 工作负载实操Pod与Deployment的创建、扩容与滚动更新2.1 create与apply命令式到声明式的关键一步创建资源有两种典型方式。kubectl create是命令式的适合快速验证比如临时起一个测试Podkubectl create deployment test-nginx --imagenginx:1.25。但它是“一次性”的同样的命令跑第二遍会报资源已存在。kubectl apply是声明式的核心参数是一个YAML清单文件kubectl apply -f deployment.yaml。它会把这次提交的清单记录到资源注解中下次再apply的时候kubectl会做一个三方合并计算出现在状态、上次apply状态和这次期望状态之间的差异然后只修改需要改的字段。这个机制带来的实际好处是整个集群的状态始终跟你的Git仓库里的清单保持一致改什么都是一句话的事回滚也有据可查。所以我的建议很直接生产环境一律用apply把清单文件纳入版本管理临时试验可以用create但养成习惯之后你会发现即使试验也直接写个简单YAML更省事因为你可以随时把它提交到仓库里变成正式配置。如果你不太记得某个资源的字段长什么样可以先让kubectl帮你生成一个示例清单kubectl create deployment my-app --imagenginx:1.25 --dry-runclient -o yaml--dry-runclient表示只在本地生成内容不真正提交给集群-o yaml表示输出YAML格式。你甚至可以把它重定向到文件里作为手写清单的起点。这个技巧特别适合那些“知道大概结构但记不住完整字段”的时候。2.2 扩容、滚动更新与回滚Deployment最常用的三连Deployment是被用得最多的无状态工作负载日常维护无非就是扩缩容、更新版本、出问题回滚这三件事。扩缩容最常见的方式是kubectl scale deployment my-app --replicas5如果你用的是HPA水平Pod自动扩缩手动scale之后可能会被HPA调回去两者会打架。所以生产环境如果已经配置了HPA临时扩容应该调整HPA的上下限而不是直接改副本数。发布新版本时kubectl set image可以快速改镜像kubectl set image deployment/my-app my-appregistry.example.com/my-app:v2但更推荐的做法是直接改YAML里的image字段然后kubectl apply -f deployment.yaml这样变更记录是完整的。触发更新之后用kubectl rollout status deployment/my-app看滚动过程它会实时输出进度一直到所有新Pod就绪。如果发布有问题先看历史版本kubectl rollout history deployment/my-app然后回滚到上一个版本kubectl rollout undo deployment/my-app也可以回滚到指定版本kubectl rollout undo deployment/my-app --to-revision2。回滚之后同样可以用rollout status确认。还有一个高频操作是kubectl rollout restart deployment/my-app很多人以为它是重启Pod其实它的作用是在不改变镜像的前提下触发一次滚动更新常用于ConfigMap或Secret更新之后让Deployment重新读取新的配置。2.3 删除Pod没那么简单要理解控制器的作用新手最容易犯的一个错误是看到某个Pod状态不对直接kubectl delete pod xxx结果发现Pod马上又冒出来了于是再删一次还是出现以为系统出问题了。其实这恰恰说明控制器在工作。Deployment、StatefulSet、DaemonSet这类控制器会保证“期望副本数”始终被满足你手动删掉一个Pod控制器会立刻创建一个新的来替补。所以如果你只是想“重启”某个应用删Pod确实可以达到目的因为它会重新调度一个全新Pod但如果你想彻底停掉这个应用要删的是Deployment或StatefulSet而不是Pod本身kubectl delete deployment my-app删除Pod的时候还有一个细节。默认情况下kubectl会给Pod一个宽限期默认30秒让容器里的进程有机会处理退出信号、清理连接然后才强制终止。如果某个Pod卡在Terminating状态出不来可以调整宽限期或强制删除kubectl delete pod my-app-xxx --grace-period0 --force但这属于最后手段。正常情况下应该先找到它卡住的原因比如节点失联、存储卷挂载异常直接把节点上的Pod强制删除可能会让数据卷处于不干净的状态。能用describe看事件解决的事情别动不动--force。2.4 快速筛查Pod状态别只盯着Running看kubectl get pods会显示每个Pod的STATUS列常见有Pending、ContainerCreating、Running、CrashLoopBackOff、ImagePullBackOff、Terminating。很多人只看是不是Running其实状态为Running不代表一切正常还要看READY列是不是类似1/1、2/2表示容器实际就绪数。如果集群里的Pod非常多一个一个看很累可以用命令过滤非正常状态的Podkubectl get pods -A | grep -v Running | grep -v CompletedCompleted是正常跑完的Job容器排除掉之后剩下的基本就是需要关注的异常对象。这条命令我在排查生产问题的时候几乎必跑能看到谁在Pending、谁在反复重启。3. 服务暴露与配置注入从Service到Endpoint的排查链路3.1 Service的类型和查看ClusterIP、NodePort、LoadBalancerService是K8s里访问Pod的入口。日常操作中kubectl get svc查看服务列表看CLUSTER-IP、EXTERNAL-IP和PORT(S)这几列。kubectl get svc -A kubectl describe svc my-serviceService的Type决定访问方式。ClusterIP是默认类型只能在集群内部访问适合服务间调用NodePort会在每个节点上开一个端口默认范围30000-32767外部可以通过节点IP:NodePort访问LoadBalancer通常由云平台提供负载均衡器会分配一个外部IP。排查外部访问问题时先确认Service类型对不对再看端口列表是不是符合预期比如80:31829/TCP表示ClusterIP端口80映射到节点端口31829。3.2 Endpoint为空的经典场景Selector不匹配Service通过selector选择后端Pod但它本身不直接存Pod的IP而是通过Endpoint对象来记录当前匹配到的Pod IP列表。所以当Service访问不通的时候我第一件事就是看Endpointkubectl get endpoints my-service如果ENDPOINTS列是空的说明Service的selector没有匹配到任何Pod。排查顺序一般是先看Service的selector写的是什么再看Pod的label是不是对得上。比如Service里写app: my-appPod的label却写成app: myapp那就不可能匹配上。这种问题用describe svc和describe pod对照一眼就能发现。还有一种情况是Pod是Running的Endpoint也有IP但访问仍然不通。这时候要看Pod的readinessProbe是否通过。如果健康检查失败Pod即使运行着也不会被纳入Service的负载均衡池。K8s的机制是先readiness后endpoint应用没有就绪就不该收到流量这在滚动发布期间尤其重要。3.3 ConfigMap和Secret查看、创建与解码配置文件我一般交给ConfigMap管敏感信息交给Secret管。创建ConfigMap可以直接从文件或字面量生成kubectl create configmap app-config --from-fileconfig.properties kubectl create configmap app-config --from-literalAPP_ENVproduction查看ConfigMap内容最直接的方式是输出YAMLkubectl get cm app-config -o yamlSecret的查看方式类似但值默认是base64编码。其实不只是查看Secret在K8s里本身就是base64存储的所以kubectl get secret my-secret -o yaml echo 需要解码的字符串 | base64 -d需要特别说清楚的一点是base64只是编码不是加密任何人只要有Secret的读取权限就能轻松解码。所以不要把数据库密码、API密钥这类高敏感信息只放在Secret里就算了更稳妥的做法是配合外部密钥管理服务或者用支持加密存储的方案。3.4 配置变更后要不要重启Pod是很多人忽略的问题修改ConfigMap或Secret之后K8s不会自动更新已经运行的Pod。Pod里的环境变量是在容器启动时注入的卷挂载方式虽然能感知到ConfigMap变化但也不会自动重启应用进程。所以一个很常见的场景是改了配置kubectl get cm看着新值已经生效但应用读到的还是老值。解决办法是让Pod重建一遍。Deployment可以执行kubectl rollout restart deployment my-app它会触发一次滚动更新用同样的镜像重新创建Pod新Pod会重新读取ConfigMap和Secret。需要注意的是rollout restart不会保留旧的配置注入过程如果你是通过kubectl set env命令直接设置了环境变量那会直接改Deployment模板触发滚动更新如果你只是改了ConfigMap本身那就要手动restart一次。两种方案都行但生产环境我推荐把配置和Deployment清单都放同一个Git仓库里改配置之后一起apply再restart这样变更记录清晰出问题也好回溯。4. 线上排障三板斧日志、事件、端口转发与节点维护4.1 日志先看应用输出再下结论日志是排障的第一步。查看日志的基本命令kubectl logs -f deployment/my-app-f是持续跟踪日志类似tail -f。Pod日志文件太大的时候用--tail限制初始行数避免一次性刷屏kubectl logs my-app-xxx --tail200如果容器之前崩溃过、当前容器又起不来加--previous看上一次容器的日志这个参数在CrashLoopBackOff场景下非常有用kubectl logs my-app-xxx --previous多容器Pod要指定-c容器名比如kubectl logs my-app-xxx -c sidecar。不指定的话kubectl会提示让你在多个容器里选一个。实际排查中看日志要掌握一个度应用日志只能证明应用自身有没有报错不能证明集群网络、调度、存储这些底层问题。所以我把日志归为排障链路的一环而不是唯一依据。日志正常就往上排查调度和网络日志报错就先定位应用自身。4.2 事件与状态describe永远是最快的病历本如果Pod起不来第一件事不是看日志而是看事件。kubectl describe pod xxx输出的最后一段Events会记录调度、拉镜像、启动容器等每个环节发生了什么。常见事件和对应原因可以整理成一张表现象事件关键字大概率原因Pod一直PendingFailedScheduling节点资源不足、亲和性不满足、PVC没绑定镜像拉取失败Failed to pull image / ImagePullBackOff镜像名打错、仓库认证失败、私有仓库没配imagePullSecret容器反复重启Back-off restarting failed container / CrashLoopBackOff应用启动即退出、启动命令问题、配置缺失健康检查失败Liveness probe failed / Readiness probe failed探针路径或端口不对、应用启动慢、探针超时设置得过短看到Pending后还可以进一步定位kubectl get pod -o wide看它被调度到哪个节点没有节点信息就说明调度器还没成功绑定需要看节点资源kubectl describe node 节点名能看到资源分配情况ImagePullBackOff则先确认镜像地址能不能访问有没有配认证信息然后再看是不是镜像tag写错了。describe的另一个用途是看Deployment的滚动状态kubectl describe deploy my-app的Events部分会显示Scaled up replica set这类记录能协助回溯发布过程。4.3 钻到容器里exec和port-forward容器里到底缺了什么、环境变量对不对、能不能连上数据库光看日志和事件往往不够需要进容器里看一眼实际状态。exec命令可以在容器内执行命令kubectl exec -it my-app-xxx -- /bin/sh有些镜像里没有/bin/sh只有/bin/bash或者连bash都没有。进不去的时候可以用单条命令直接探测不需要交互式shellkubectl exec my-app-xxx -- ls /etc/nginx kubectl exec my-app-xxx -- env | grep APP_ENV kubectl exec my-app-xxx -- curl -I http://localhost:8080/health这个思路比硬找shell更通用。笔者经常遇到最小化镜像里连bash都没有的情况一条ls或curl往往就能定位问题。另一个很实用的调试命令是port-forward它能把集群里的端口转发到本地特别适合在笔记本电脑上直接访问集群内服务kubectl port-forward svc/my-service 8080:80执行完之后本地浏览器打开http://localhost:8080就能访问集群内的Service了。它支持转发Service、Deployment、Pod也可以转发多个端口kubectl port-forward svc/my-service 8080:80 9090:9090。要注意port-forward是临时建立一条隧道适合调试不适合当生产访问方式Windows和部分Linux环境下端口占用会直接报错换个本地端口就行。4.4 节点维护与资源查看cordon、drain、uncordon节点要重启、下机维护的时候不能直接关机器否则上面的Pod会一起消失。正确的顺序是先cordon标记节点为不可调度再drain把Pod优雅地迁移走维护完成后再uncordon恢复调度。kubectl cordon node-xxx kubectl drain node-xxx --ignore-daemonsets --delete-emptydir-data kubectl uncordon node-xxxcordon的作用是把节点标记为SchedulingDisabled新的Pod不会再调度到这个节点上但已经运行在上面的Pod不会受影响。drain才是真正驱逐Pod的操作。两个参数值得注意--ignore-daemonsets是因为DaemonSet的Pod是每个节点必须有一个的drain不能把它们驱逐走必须忽略--delete-emptydir-data是允许删除使用emptyDir临时卷的Pod如果你不确定节点上有没有emptyDir数据可以先不加这个参数试试它会报错提示有哪些Pod阻挡再判断怎么处理。资源查看方面kubectl top node和kubectl top pod可以看节点和Pod的CPU、内存使用量。但要注意这条命令依赖metrics-server如果集群里没装会提示error: Metrics API not available。没有metrics-server的时候可以用kubectl describe node查看节点上已分配资源的总量配合kubectl get pods -o wide --sort-by.spec.nodeName看哪些Pod落在同一个节点同样能判断资源热点。4.5 一套顺手的排障顺序在线上处理K8s问题我习惯按这个链路走kubectl get pods -A | grep -v Running | grep -v Completed先扫出异常Pod然后对目标Pod执行describe看事件同时看get pod -o wide确认调度位置和节点信息再看logs --previous拿重启前日志必要时exec进容器验证运行环境最后检查Service和Endpoint确认网络链路。整套下来一般不会超过十分钟。这套顺序之所以好使是因为它严格遵循了“从外到内、从状态到内容”的原则先搞清楚它在哪、处于什么状态再分析为什么到这个状态最后才进到应用内部找原因。反过来的话很容易花了半小时看日志最后发现是节点资源不足导致压根没调度上。5. 提高日常效率标签选择器、JSONPath与简短别名5.1 用标签选择器把命令变成过滤器K8s里的标签label不只是元数据它还是查询和筛选的主要手段。给一堆Pod打上appmy-app、envprod这类标签之后就可以用-l参数精准过滤kubectl get pods -l appmy-app kubectl get pods -l env in (prod, staging) kubectl get pods -l appmy-app,envprod标签选择器还支持!和集合操作组合查询只需要用逗号分隔。加上--show-labels可以看到每个对象当前打的所有标签kubectl get pods --show-labels排查的时候我常用kubectl get pods -l appmy-app -o wide把筛选和详情一起拿省一次查询。需要根据Pod运行状态过滤的时候可以用--field-selectorkubectl get pods --field-selectorstatus.phasePendingfield-selector和label selector不太一样前者过滤的是对象字段后者过滤的是标签两类条件可以同时用但注意不是所有字段都支持过滤报错的时候换-l过滤或配合grep也完全足够。5.2 JSONPath和自定义列让输出变成自己想要的形状默认的kubectl get输出列是固定的但很多场景下你需要的信息藏在yaml或json里。用-o jsonpath可以直接提取指定字段。比如我只想看每个Pod的名字和IPkubectl get pods -o jsonpath{range .items[*]}{.metadata.name}{\t}{.status.podIP}{\n}{end}这条命令读起来难一点但它省去了人工翻输出。另一个更直观的方案是custom-columns相当于自定义表格列kubectl get pods -o custom-columnsNAME:.metadata.name,IP:.status.podIP,NODE:.spec.nodeName我实际用custom-columns比较多条理清晰也很好加列。排序可以用--sort-by比如按启动时间排序kubectl get pods --sort-by.metadata.creationTimestamp按IP排序、按节点排序也都可以--sort-by.status.podIP即可。对于脚本化获取数据JSONPath更强大但日常看输出custom-columns的直观性更好。建议先在交互式终端里用custom-columns等需要写脚本的时候再引入JSONPath。5.3 别名、函数与kubectl explain该省则省该查就查别名我建议克制一点只配几个最高频的alias kkubectl alias kgpkubectl get pods alias kgsvckubectl get svc alias kdesckubectl describe如果嫌别名不好记可以写一个简单的shell函数来处理重复性较高的排障。比如我总是要看非Running的Pod就写kbad() { kubectl get pods -A $ | grep -v Running | grep -v Completed }然后一行kbad就能扫全集群。这类函数的价值在于把“经验”固化成了命令新人拿到手就能用。还有一个被很多人忽略的内置命令kubectl explain。它可以直接查看某个资源的字段定义和说明比翻文档还快kubectl explain pod kubectl explain pod.spec.containers kubectl explain deployment.spec.strategy我写YAML清单遇到不确定字段时第一个查的就是它。explain显示的字段含义和类型都来自API定义权威性比网上随便搜到的二手资料高得多。这个命令解决了“记不住字段”的问题所以遇到不熟悉的资源完全不用慌。5.4 把默认命名空间固定下来省掉一半的-n如果某个集群里绝大多数操作都集中在同一个命名空间可以把当前上下文的默认命名空间改掉之后所有命令都不用再加-nkubectl config set-context --current --namespacemy-namespace执行完再kubectl config view --minify确认看到namespace: my-namespace就说明改成功了。这个改动只影响当前上下文不会改动集群里的任何资源也不会影响其他集群的配置。实际用下来它能减少很多重复输入尤其在命名空间名称很长的时候。给新集群做交接或者自己重新上手一个环境时我一般会先跑几条命令kubectl config current-context kubectl get nodes -o wide kubectl get namespaces这三条能在十秒内告诉你当前在哪个集群、节点状态如何、有哪些命名空间。很多操作事故都源于没认清环境尤其当本地电脑上挂着好几个集群的kubeconfig时一条current-context能帮你确认自己正对着哪套环境。最后再分享一个小习惯我几乎从不刻意去背命令遇到不确定的字段和参数都是先kubectl explain查定义再用--dry-runclient -o yaml生成草稿。命令这东西关键是建立一个清晰的模型查状态用get和describe改期望用apply和scale看结果用logs和events进环境用exec和port-forward。把这条链路吃透K8s常见操作命令对你来说就不再是零散的清单而是一套随时可以调用的工具箱。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑