ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

kubectl速查手册:从命令模型到容器排障实战

kubectl速查手册:从命令模型到容器排障实战 1. 先建立命令心智模型动词加资源一切都有规律每次有新人问我 kubectl 怎么学我都会说别背整理一份属于自己的速查手册。Kubernetes 的命令看着多真正每天用的其实就那么十几个。我整理这份手册的起因是一次凌晨上线明明要查看某个服务的 Pod 状态结果把 deployments 拼错成 deploymen那台机器又恰好没配命令补全硬是靠着 --help 才想起来。后来我给自己定了一个规矩凡是查过两次以上、需要翻文档的命令一律记进速查手册。今天配合这份手册把关键命令、常见坑和排障思路一起梳理一遍希望能让你少走点弯路。kubectl 的底层逻辑其实非常统一一句话就能说清kubectl 动词 资源类型 [资源名称] [参数]。动词本身基本上就是“你打算对资源做什么”资源类型则是“你在操作什么对象”。刚开始容易卡住是因为资源和动词的组合太多但只要理解了“动词加资源”这个骨架大部分命令都是顺着这个结构扩出来的。例如你要看 Pod 列表就是kubectl get pods想看 Deployment 详情就是kubectl describe deployment nginx想给 StatefulSet 扩容就是kubectl scale statefulset web --replicas3。动词、资源、名称、参数这四个格子都能替换命令再多也逃不出这个框。1.1 常用资源缩写先记这一张表资源类型是速查手册里第一张必须放的表。Kubernetes 的资源名特别长好在官方提供了缩写日常敲命令可以用缩写省很多事。我个人建议至少把下面这些记牢资源缩写用途podspo最小调度单位deploymentsdeploy无状态工作负载servicessvc暴露服务入口namespacesns资源隔离空间nodesno集群节点replicasetsrs管理 Pod 副本statefulsetssts有状态应用daemonsetsds每个节点都跑一份configmapscm配置文件secretssecret敏感信息eventsev事件记录ingressing对外路由不仅是缩写资源名的单复数其实也可以混用kubectl get deploy、kubectl get deployments、kubectl get deployment都会返回同一个结果。大多数人不知道这个小细节但偶尔记错单复数时能救你一次。1.2 动词表日常会用到的基本就是这些我按使用频率给自己排了一份动词清单你可以直接抄get / describe / logs查状态、查详情、查日志apply / create / delete资源生命周期的管理edit / patch / set修改资源定义scale / rollout扩缩容和版本发布的控制exec / port-forward / cp进入容器、转发端口、拷贝文件top实时观测 CPU 和内存api-resources / explain在终端里临时查用法这里有个容易被忽略的命令叫 explain它是“终端里的官方文档”。比如你忘了 Pod 里就绪探针该怎么写直接敲kubectl explain pod.spec.containers.readinessProbe它会按层级把字段和类型列出来比翻网页快得多。再配合kubectl api-resources查看当前集群支持的所有资源类型基本可以做到不离开终端就能完成大部分查询工作。很多人以为手册就是一个文档其实终端本身就是你的速查手册问题在于你是否掌握了打开它的方式。2. Context切换多集群环境下最容易翻车的地方kubectl 多集群管理一直是日常使用中翻车率最高的场景之一。尤其是那种需要同时维护开发环境、测试环境、生产环境的同学最怕的不是命令记不住而是不小心在错误的集群里执行了高风险的操作。我身边真实发生过有人想在所有环境同步一份 YAML结果因为在错误 Context 下执行了kubectl delete把测试环境某个服务删干净了。这种事故一旦发生很难靠“再改回来”补救。所以我认为 Context 管理是每个 kubectl 使用者都必须静下心搞明白的第一课。2.1 Context 到底是什么Context 可以理解为一组“连接配置”的组合。它包含三个要素集群地址、认证用户、默认命名空间。也就是说一个 Context 不只是指代某一个集群还绑定了你用哪个身份连接它以及进入之后默认落在哪个 namespace。很多新手会误以为“切集群”就是改一个地址实际上没搞定认证信息时就算地址填对后端也会直接给你报 401 或者 403。查看当前使用的 Context 非常简单kubectl config current-context如果想完整列出本地 kubeconfig 里所有可用的 Context用kubectl config get-contexts输出会显示每一行的 Context 名称、对应的集群、认证用户和默认命名空间。切换业务环境的命令是kubectl config use-context prod注意切换到某个 Context 后use-context 不会主动输出任何确认信息。在这个设计下有人一旦敲错了名字切到了别的环境后续所有操作都在那个环境里执行你完全无感。我的习惯是切换后立刻执行kubectl config current-context kubectl get pods -A | head -20先确认自己在哪再去看一眼集群里跑着的对象感觉到位了再继续。2.2 多 kubeconfig 文件合并与常见命名冲突插件 kubectx 和 kubens 确实好用但如果你希望在原生 kubectl 里解决核心是理解KUBECONFIG环境变量。默认情况下 kubectl 只会读取$HOME/.kube/config。当你有多个集群配置时可以把它们一股脑合并进来export KUBECONFIG$HOME/.kube/config:$HOME/.kube/staging:$HOME/.kube/prod kubectl config view --flatten $HOME/.kube/config追加合并后kubectl 会把多个文件里的 Context、Cluster、User 全部加载进来get-contexts里就能一次性看到所有环境。但随之而来的坑是如果两个文件里出现过同名的 Context 或 User后面加载的配置会以追加顺序为准造成“我想连 A 却被连到了 B”的诡异现象。解决的办法是养成在配置文件里写清晰环境前缀的习惯比如把生产环境的 Context 命名为prod-eks-xxx测试环境命名为staging-ack-xxx而不是泛泛地叫cluster1。另外临时使用某个独立的 kubeconfig 时记得用--kubeconfig参数显式指定kubectl get nodes --kubeconfig /tmp/backup-kubeconfig这会让当前默认配置完全绕开直接以指定文件为准避免不同文件相互干扰。3. 查询输出怎么选get 的 -o 参数与场景化用法kubectl get应该是所有命令里使用频率最高的一个但很多人一直只会用默认输出默认输出确实能给出一行列表但当你想定位“这个 Pod 到底跑在哪个节点上”时默认输出根本不够用。这时候要对-o参数有更细的理解。-o是 output 的意思后面跟不同的输出格式直接决定信息密度。3.1 常用的 -o 参数对照输出格式命令示例适合场景widekubectl get pods -o wide查看 Pod 所在节点、Pod IP、宿主机 IPyamlkubectl get pod nginx -o yaml查看对象的完整定义jsonkubectl get pod nginx -o json接 jq 做自动化解析jsonpathkubectl get pod nginx -o jsonpath{.status.phase}提取单个字段的值custom-columnskubectl get pods -o custom-columnsNAME:.metadata.name,IMAGE:.spec.containers[*].image只显示自己关心的列用-o wide是最常见的操作尤其是排查跨节点网络问题时一屏就能看到每个 Pod 跑在哪台节点上。用-o yaml则适合检查资源对象的全部字段比如确认探针配置、环境变量、挂载卷是否按预期写入。输出太长没关系配合 grep 或者编辑器搜索都可以。3.2 label 选择器和字段选择器每天面对几十上百个 Pod如果只靠kubectl get pods全列出来再肉眼找效率太低。这里有两个非常好用的过滤手段label 选择器和字段选择器。Label 是 Kubernetes 最核心的关联方式。只查看带有appnginx标签的 Podkubectl get pods -l appnginx如果有多个标签需要同时满足用逗号分隔kubectl get pods -l appnginx,envprod字段选择器则按对象自身的属性过滤例如只看运行中的 Podkubectl get pods --field-selectorstatus.phaseRunning说实话字段选择器支持的字段有限日常用它最多的场景是看某节点上所有 Pod或者看某命名空间下的所有 PVC 状态。大多数时候我更愿意用 label selector因为它是资源设计时留下来的关联方式语义清晰。另一个提高查询效率的小习惯是给 get 加上-w参数做实时监听。比如发布过程中想看 Pod 是否全部 Running执行kubectl get pods -l appnginx -w画面会持续刷新直到每个 Pod 都进入 Ready 状态。对于看版本滚动发布这条命令比反复手动执行 get 方便得多。3.3 认识全量资源开关 -A-A也就是--all-namespaces作用是一次性列出所有命名空间里的资源。特别适合集群维度查询kubectl get pods -A -o wide kubectl get events -A --sort-by.lastTimestamp第一条能让你快速知道整个集群里所有 Pod 的健康状态第二条则是集群事件的全量汇总排障时经常会用到。不过也要提醒一句kubectl get all -A并不会列出所有资源比如 ConfigMap、Secret 这类“非 workload”资源就不在里面所以如果需要检查敏感信息的配置还是要单独kubectl get cm -A、kubectl get secret -A。4. describe 与 logs看状态与看日志的正确分工get 告诉我们“现状如何”但没告诉我们“为什么会这样”。当 Pod 不健康时我最先做的是两件事describe 看事件logs 看日志输出。这两者分工不同不能互相替代。4.1 describe 里真正值得关注的信息执行kubectl describe pod my-app-7d8b9f5d6c-abc12输出信息很长但真正值得看的是靠后的Events:段落。Events 会按时间倒序把 Pod 经历的事件列出来其中 Reason 和 Message 两列基本上就是排查方向的答案。举个例子如果你看到Failed to pull image ...: failed to resolve reference大概率是镜像地址写错、镜像 tag 不存在或者访问镜像仓库的凭证不对。如果看到Back-off restarting failed container说明容器曾经启动过但随后退出下一步就该去看日志。describe 的另一个高频用途是指定节点不可调度的问题。当 Pod 状态是 Pending 时Events 里会出现类似0/3 nodes are available: 1 node(s) had untolerated taint或者Insufficient memory的信息前者是污点容忍问题后者是资源不足这两类问题从 Events 一眼就能判断方向。4.2 logs 的常用参数组合日志是定位容器内部问题的根本手段命令本身不长但参数组合极多。先说最常用的几个kubectl logs my-app-7d8b9f5d6c-abc12 kubectl logs -f my-app-7d8b9f5d6c-abc12 kubectl logs --tail200 -f my-app-7d8b9f5d6c-abc12 kubectl logs --since1h my-app-7d8b9f5d6c-abc12-f持续跟踪日志适合边发请求边看输出--tail控制从倒数多少行开始输出避免一次性刷出几万行--since则是按时间窗口过滤日志。对崩溃重启的容器还要加上-p参数查看上一次的日志。因为容器已经重启当前日志文件已经属于新进程老日志只能通过 previous log 拿到kubectl logs -p my-app-7d8b9f5d6c-abc12如果你遇到的是多容器 Pod比如一个业务容器加一个边车容器执行 logs 时必须加上-c指定容器名kubectl logs my-app-7d8b9f5d6c-abc12 -c sidecar否则 kubectl 会直接提示你容器名是必填项。这里还要单独提一下 Init 容器。初始化容器的日志在 Pod 处于异常状态时尤其关键很多早期初始化的失败不会体现到主容器的日志中必须这样取kubectl logs my-app-7d8b9f5d6c-abc12 -c init-migrate总之不管是主容器、边车容器还是初始化容器取日志的思路都是先确定容器名再组合--tail、-f、-p这三个参数。5. 高频变更操作扩容缩容、更新镜像、回滚与删除日常运维里真正会改资源状态的操作不外乎扩缩容、换镜像、发版本、回滚、删除这几类。这些操作背后都对应同一个原则尽量用声明式命令让控制器去做而不是登录节点手动改容器。直接在节点上改 Docker 容器是在绕过 Kubernetes 的控制器后续调度、探针、副本管理全都会错乱这是新手最容易踩的坑。5.1 扩缩容和自动扩缩容手动扩容一个 Deployment最简单的方式kubectl scale deployment nginx --replicas5StatefulSet 也是同样的动词kubectl scale statefulset mysql --replicas3如果想让它根据负载自动伸缩可以用 autoscale 创建 HPA 对象kubectl autoscale deployment nginx --min2 --max10 --cpu-percent70这条命令等价于创建了一个 HorizontalPodAutoscaler控制 CPU 平均使用率在 70% 时触发扩容。而当你观察缩容表现时有个常见的误会值得说一句缩容并不一定马上把多余 Pod 删掉因为 HPA 的默认行为会考虑 Pod 启动预热。这个机制叫scale-down stabilization是为了避免频繁抖动。所以看到缩容延迟不要急着认为是系统卡了。5.2 更新镜像与滚动发布更新 Deployment 的镜像版本最常见的三种方式分别适用于不同场景。一是直接改 YAML 后 applykubectl apply -f deployment.yaml二是原地修改线上对象kubectl set image deployment/nginx nginxnginx:1.21.6其中nginxnginx:1.21.6的格式是“容器名新镜像名”有些人把等号左边误写成镜像名改完后会报“container 未找到”这里要特别注意。三是用 edit 打开默认编辑器手动改完保存后自动生效kubectl edit deployment/nginx不管用哪种方式改完后我都会立刻观察发布过程kubectl rollout status deployment/nginx这个命令会实时追踪滚动升级的进度直到策略同时显示“successfully rolled out”时才算结束。如果发布中途想取消可以另开终端执行kubectl rollout undo deployment/nginx它会回到上一个版本。还有一个非常容易混淆的命令是rollout restart它并不会更新镜像版本只是触发一次滚动重启让 Pod 重新调度、重新拉镜像。当我们要让所有 Pod 重新加载 ConfigMap 时通常会用它kubectl rollout restart deployment/nginx5.3 回滚的正确姿势回滚本身动作很简单kubectl rollout history deployment/nginx kubectl rollout undo deployment/nginx --to-revision2但有几个细节值得写进手册。第一如果 Deployment 从未通过set image或edit触发过新的 ReplicaSetrollout history 里只有一个 REVISION 1这时候执行 undo 是无效的。第二如果你用了kubectl apply管理 YAML但本地文件一直没有同步更新回滚后下一次 apply 可能会把你的手工回滚覆盖掉。这个坑非常隐蔽所以我更建议所有通过 apply 管理的资源至少要放到 Git 里统一跟踪不要只改线上不落代码仓库。第三查看指定版本的镜像信息可以加--revision参数kubectl rollout history deployment/nginx --revision2这样能在回滚前先确认这个版本到底包含什么镜像避免回错。5.4 delete 的三条红线删除命令看起来简单但在生产环境里是最需要小心的。先列出最常见的删除操作kubectl delete pod my-app-abc12 kubectl delete deployment nginx kubectl delete ns test第一条红线不要在发布过程中手动删除 Pod。Deployment 确实会自动重新创建一个新的 Pod但你手动删除的这个 Pod 可能正好是当前唯一健康的副本删除时机不对会导致短暂的可用性下降。第二条红线慎重使用--force --grace-period0。这个组合会绕过 Kubernetes 的优雅终止机制容器进程不会收到正常的 SIGTERM很多无法在强杀前完成的状态清理会丢失。除非 Pod 确实长时间卡在 Terminating而且已经确认无状态、无持久化动作否则我不建议用。第三条红线删除命名空间时如果 Events 里一直显示 Terminating不要一上来就手动移除 finalizers。有些 finalizer 的存在是为了保证关联资源被正确清理绕过它极有可能残留孤儿资源。正确的做法是先检查是否有依赖服务再根据实际情况考虑是否需要延伸排查。6. 排障实战从 CrashLoopBackOff 到恢复的完整链路理论讲了一堆还是要拿一个真实场景把前面这些命令串起来。CrashLoopBackOff 是日常排障里最常见的状态之一Pod 启动后持续崩溃控制器反复重启它最后进入指数退避的循环。我处理这类问题的固定链路分五步。6.1 第一步从状态和事件锁定方向第一步定位问题 Pod。在命名空间里执行kubectl get pods第二步describe 看事件kubectl describe pod my-app-xxxxxEvents 里如果出现Back-off restarting failed container说明容器是被控制器判定为启动失败而重启的原因需要进日志确认。如果出现的是Liveness probe failed: HTTP probe failed with statuscode: 500说明不是启动直接崩溃而是探针检测失败后容器被杀问题方向完全不一样。6.2 第二步用 previous 日志读取崩溃现场先拉取崩溃前的日志kubectl logs -p my-app-xxxxx-p是解决这类问题的关键。当前容器日志可能已经被新进程覆盖上次崩溃前打印的异常栈往往就在 previous log 里。多容器 Pod 别忘了加-c。6.3 第三步进入容器或临时调试容器如果日志里看不出问题或者容器内部环境异常就进入容器内排查kubectl exec -it my-app-xxxxx -- sh这个命令会进入正在运行的主容器在里面执行 ps、netstat、cat 等命令检查进程和端口。有些人会误以为 exec 也能进一个 CrashLoopBackOff 的容器实际上容器已经不在运行状态时exec 是进不去的。这时可以用临时调试 Pod 或临时容器kubectl debug -it my-app-xxxxx --imagebusybox --targetmy-app--target指定的是要共享进程命名空间的目标容器进入后可以查看目标容器的进程、网络和文件系统。如果环境里还没有启用临时容器特性也可以用最笨但有效的办法直接用同样的镜像开一个独立的调试 Podkubectl run debug --imagemy-app:1.0.0 -it --rm --restartNever -- sh6.4 第四步修复、验证和一次真实踩坑复盘根据定位结果修复。如果是探针配置太激进比如 LivenessProbe 的 initialDelaySeconds 设成 0导致容器还在启动就被干掉那就调大这个值如果日志显示监听端口和探针端口不一致那就统一端口。修复后重新应用 YAML再用kubectl rollout status观察状态。这里再补一个我踩过的印象很深的坑有一个服务一直 CrashLoopBackOffdescribe 显示探针失败但我怎么改探针都无济于事。后来看日志才发现应用自身启动时要连一个外部依赖的 Redis连接超时时间长达 20 秒而在 k8s 里存活探针每 10 秒就执行一次探针端口根本没有建立监听于是容器永远被探针干掉。最后不是靠调探针解决的而是优化了应用启动逻辑把依赖后置成异步初始化。这个案例说明探针问题往往不单纯是 YAML 参数问题根因可能在应用层。7. 提效技巧补全、别名、dry-run 与个人速查手册到了最后一定要聊效率因为速查手册的目的不是让你背命令而是当你在终端前犹豫“这个命令怎么写”的时候最短时间内找到答案并完成任务。7.1 命令补全和别名kubectl 自带补全脚本初始化一下就能在终端里按 Tab 自动补全source (kubectl completion bash) echo source (kubectl completion bash) ~/.bashrc如果用的是 zsh把 bash 换成 zsh 即可。配好补全后资源类型和命令动词都能自动补齐前面说的“deploymen 拼错”这类问题基本不会再出现。命名很长的上下文也可以用kubectl config use-context prod-eks-xxx时按 Tab 补全。别名方面我最常用的是把 kubectl 缩成 kalias kkubectl然后再加上常用的组合比如alias kgpkubectl get pods alias kgpakubectl get pods -A -o wide alias kdesckubectl describe alias klogskubectl logs -f --tail100这里要注意不要为了追求少打字把别名弄得太隐晦。我曾经把kgpwo这类缩写别名写在手册里过了一个星期自己都不记得了最后还是老老实实删掉。别名只保留“一眼就能看懂”的长命令组合否则反而添乱。7.2 dry-run 与 diff改动前先演练一遍有些高风险操作不能等到 apply 才知道结果好在 kubectl 提供了 dry-run 预演能力kubectl apply -f deployment.yaml --dry-runclient这条命令只会在本地做校验不会真正提交给 API Server。如果在服务端做校验可以用kubectl apply -f deployment.yaml --dry-runserver更推荐的习惯是在正式 apply 前输出一份差异化对比kubectl diff -f deployment.yaml它会把你本地 YAML 与线上当前对象做对比以 diff 形式显示哪些字段会被新增、修改、删除。这比 dry-run 更直观能最大程度避免“我以为改的是这个实际上线上已经是另一个状态”的情况。7.3 快速资源观测和节点维护除了改资源日常还得看资源使用和节点状态。kubectl top是快速查看 Pod 和节点实际使用量的命令kubectl top nodes kubectl top pods -A --sort-bycpu这两条命令在定位“为什么某个 Pod 频繁重启”时很有用。如果节点内存使用率长期接近 100%那么很多新 Pod 可能一直处于 Pending 状态事件里的原因就是 Insufficient memory。需要做节点维护时我会先用 cordon 标记节点不可调度再驱逐现有 Podkubectl cordon node-1 kubectl drain node-1 --ignore-daemonsetsdrain 会把该节点上的普通 Pod 优雅驱逐到其他节点DaemonSet 因为属于系统保留需要忽略。维护完成后再把节点置回可调度kubectl uncordon node-1这个组合属于集群维度的高频操作虽然不属于 kubectl 最开始的几个动词但必须写进速查手册。如果你负责的集群超过三五台机器节点维护迟早会用上。7.4 把手册放进 dotfiles 长期维护整理速查手册这件事我建议不要只存在某个文档里而是把它放进自己的 dotfiles 仓库中。既可以存成一份 Markdown也可以直接把常用命令以注释和别名放在.bashrc/.zshrc里换新机器时一次性拉取即可。我自己的做法是每次在群里看到别人分享一个冷门但好用的 kubectl 命令就花十秒钟验证一下然后追加到对应的分类里。时间久了这本手册就成了你自己在 Kubernetes 上踩过所有坑的缩影。真正有用的速查手册不是别人给你的是自己从命令行里的每一次排障中沉淀出来的。这条经验比任何一个具体命令都值得收藏。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进