K8s实操全记录:从Pod部署到线上故障排查与资源优化

发布时间:2026/10/22 3:19:54
K8s实操全记录:从Pod部署到线上故障排查与资源优化 上篇写到哪儿了就是聊完K8s那些基础概念Controller、etcd、调度器各管哪摊事以及我当时为什么觉得“这玩意儿迟早得学”。当时说好了这篇直接把我在真实项目里从零开始用K8s的经历捋一遍。今天不念概念全是大白话实操记录包括第一次写YAML的狼狈、三个绕了半天才想明白的概念、某次服务扩容后下班路上的线上事故以及这几天折腾下来我认为最重要的一组习惯。1. 从概念到集群第一次把Pod跑起来的完整经过先交代背景。我之前所在的项目是一套给内部运营用的管理后台单体应用部署方式是经典的“一台服务器扔个jar包systemd管着”。上线流程是打包、上传、备份、替换、重启。那时候团队只有两三个人这套流程勉强够用。但后来业务方提了新需求要接一堆定时任务还要按不同租户跑隔离的数据同步单体越来越胖每次改一点东西都要全量发布谁受得了。下定决心迁移K8s的诱因很实际不是赶时髦是部署频率实在扛不住了。当时我们从“一天发布一次”变成了“一天想发布三次”单机手动部署成了整个团队的瓶颈。在那之前我只看过文档没正经用过K8s所以第一步是在自己电脑上搭一个单节点集群把官方示例跑起来。1.1 本机环境搭建与第一个YAML的认知冲击如果你也是零基础起步我建议别一开始就上完整集群本地先用轻量方案。我当时没装重型环境直接选了内存占用较小的单节点方案一条命令启动然后配好kubectl上下文就开始玩。我当时部署的第一个例子是一个后端API服务模拟“用户信息查询”接口。写第一份Deployment YAML的时候整个人是懵的满屏缩进、apiVersion、kind完全不知道哪些字段属于核心字段。踩的第一个坑印象深刻imagePullPolicy没指定系统默认用的是IfNotPresent但我本地根本没有那个镜像拉取自然失败Pod状态一直停在ImagePullBackOff。排查方法很简单用kubectl describe pod看Events。那一瞬间我理解了为什么大家说K8s排错先看Events因为Pod当前状态只是结果原因全在Events里什么拉不到镜像、端口对不上、健康检查失败全部列在那里。kubectl get pods -n default kubectl describe pod pod-name -n default1.2 第一次成功对外访问端口映射与临时调试镜像问题解决后Pod起来了但访问服务又卡住了。我心想Pod都Running了怎么curl就是不通后来才意识到Pod有自己的IP但那个IP默认只在集群内部可访问要对外提供服务必须通过Service或端口转发。临时调试最粗暴的办法是kubectl port-forward把宿主机端口直接映射到Pod端口kubectl port-forward pod/pod-name 8080:8080然后浏览器访问localhost:8080就能看到接口返回了。跑通那一刻的感受原来K8s真正改变的是“部署思维”。之前我脑子里全是“一台机器上的进程”到了这儿一切都变成“声明期望状态”你告诉它“我要3个副本每个副本跑什么镜像、开什么端口”剩下的由系统自动维护。2. Service、Deployment、Namespace三个概念在命令行里才真正明白概念贴我上篇写过但理解概念和实际操作完全是两码事。这篇我要重点讲这三个概念是怎么通过实际操作“长”进脑子里的每一个都对应我踩过的具体场景。2.1 Deployment的滚动更新从重建到原地替换的转变最早我写Deployment时复制、删除、重启全靠运维直觉。真正让我重视它的是一次“改配置”的场景。我在本地集群里更新了环境变量用kubectl set env触发更新系统自动创建了一个新ReplicaSet新Pod起来、健康检查通过后旧Pod才被终止。这个过程中最有价值的是一个细节调整如果新版本配置有误导致健康检查不过滚动更新会卡住旧的Pod不受影响、继续对外服务。这一点极其关键等于系统自带“变更保护”坏版本不会把整个服务拖垮。如果你只是改了镜像tag常规做法是kubectl set image deployment/my-app my-appregistry.example.com/my-app:v1.2.0或者直接改YAML再kubectl apply。建议用apply方式因为整个文件都在配置管理里方便回溯。2.2 Service的ClusterIP与DNS服务发现到底解决了什么问题没接触Service之前我始终不理解“服务发现”在哪方面有用。后来我为两个服务做了联调场景一个订单服务一个用户服务用户服务要通过服务名访问订单服务的接口。如果不做Service你必须在代码里写死某个Pod的临时IP可Pod一旦重启IP就变了这个方案根本没法用。有了Service就有了一个稳定的虚拟IP和一个集群内DNS名字比如http://order-service:8080无论后端Pod怎么重建这个访问入口都不变。这个稳定入口的价值不仅在于方便更在于它让故障转移对调用方透明。哪个Pod挂了Service会自动把流量路由到存活的Pod上调用方完全无感知。这才是生产环境需要的东西。我在本地做过一次直观测试手动删掉当前提供服务的Pod然后一直循环请求Service的地址结果只出现了极短的断流紧接着下一个请求就打到新Pod上了服务没掉线。2.3 Namespace好像没用直到多环境并存时才香Namespaces一开始被我当成“花架子”直到我把开发环境、测试环境的一些服务放在同一个集群里跑时才体会到隔离的必要性。两个环境里都有config-service这个名字如果没有Namespace隔离必然冲突。用Namespace做区分后每个环境各占一个独立“目录”互不干扰权限也可以分开授。可以通过下面这种方式切换上下文kubectl config set-context --current --namespacedev这句命令配好之后后续所有命令默认都作用于dev环境再也不用每条命令都加-n dev了。3. 规模一上来就暴露问题一次高峰掉线故障的完整排查链路真正的考验不是功能跑通是流量和复杂度的考验。迁移完第一个服务没多久我们就遇到了第一次线上事故。这里把完整链路写下来比直接告诉你答案有用得多因为以后你大概也会按这个顺序排查。3.1 现象接口间歇性超时不是完全不可用故障现象很有迷惑性服务不是彻底挂掉是“时好时坏”。高峰期每分钟大约几十次请求其中有少量请求超时其他正常。因为不是全面故障一开始还以为是网络抖动差点错过了真正的问题。大约十分钟后超时比例越来越高我们才决定动手排查。第一步登录看节点状态和Pod状态kubectl get nodes kubectl get pods -o wide kubectl top nodes3.2 发现CPU软限制与单节点资源耗尽真相浮出水面kubectl top nodes一出来就发现问题了。节点CPU使用率显示是60%左右看起来并不高但再往下看部分Pod的CPU用量已经顶到了上限。我们后来才意识到一件事kubectl top看到的是节点实时使用率而Pod实际能用的资源要同时受requests和limits限制。我们的服务当时只写了requests没写limits。K8s的调度和限制逻辑是这样的requests用来决定Pod被调度到哪台机器、能不能放得下limits用来限制Pod实际能用多少资源。只有requests没有limits时Pod虽然被正常调度但如果节点上其他Pod把资源抢走当前服务就可能在CPU被压缩、内存不足的情况下“硬撑”表现就是响应时快时慢。直白点说就像合租房里每户都报了“我打算住两个人”但真住进来十个人谁力气大谁占地方谁都没明确划上限结果公共区域被占满谁都跑不快。加limits就是给每户明确规定“最多住几个人”超出就得排队。我们当时为每个Pod分别设置了合适的CPU和内存上限同时为请求量做了适当预留。当然这个过程需要对业务容量有一定评估我当时参考了平时监控里的峰值往上留了差不多1.5倍余量才定下来。更新后很快有变化CPU压缩的情况明显减少接口超时比例直接下降整条链路的响应稳定了很多。3.3 配置即使未必正确也要有日志留存和快速恢复预案这个故障给了我们另一个教训最小配置看起来能跑遇到真实流量就露怯。而且还暴露了另一个短板——我们的业务日志只输出到容器标准输出没在本地留存排查问题时想看历史日志都找不到地方只能实时跟踪这非常被动。后来我们调整了两件事一是为每个服务加上了stdout日志的采集统一收走集中查询二是把“一键回滚”写成文档每次发版前明确标记上一个稳定镜像版本这样一旦新版本有问题一条命令就能滚回去kubectl rollout undo deployment/my-app这套“配置写成代码、日志统一收集、回滚一键执行”的组合拳才是K8s给我们带来的真正价值。不是省了手动发布那几分钟而是让整个系统的可观测性和可恢复性上了一个台阶。4. 这轮折腾留下的五条实操建议折腾了这几天踩了不少坑也积累了一些可以沉淀下来的规则。说它们是纪律也好是习惯也罢反正在这次迁移里它们全都真正救过我。4.1 不管环境多小资源requests和limits永远写全requests不写调度器没法评估容量Pod可能在资源不足的节点上苟活limits不写一旦流量上来节点资源瞬间被榨干所有Pod互相影响。所以这个习惯必须从一开始就建立哪怕在本地开发也一样写清楚。这个字段不是给机器看的是给你自己的“资源预算”。4.2 镜像永远使用不可变tag“latest”这类tag看着方便实际是雷。你根本无法确定线上跑的是哪个版本回滚也没法回。我们后来强制镜像tag必须跟代码版本号挂钩比如v1.2.0、v1.2.1确保每次部署都能追溯。把版本号和发布时间对应上排查时能少掉很多头发。4.3 健康检查不是好看的配置是系统的自动修复开关初期我把readinessProbe和livenessProbe视为麻烦事能省则省。后来发现它们才是Pod生命周期管理的核心。readiness告诉系统“现在可以接流量了”liveness告诉系统“这家伙是不是已经死了死了就重启”。没有健康检查流量可能打到还没就绪的Pod上就会出现“部署成功但请求全失败”的诡异场景。4.4 日志与指标一开始就要想清楚容器是“用完即走”的形态日志在本地文件里没有任何意义。迁移到K8s的第一天就应该把日志采集和指标监控安排好。这块补起来成本不低但越晚补越痛苦。如果团队实在没有专门的监控平台也至少要用命令行工具定期看资源水位以及把关键事件做成简单报警。4.5 部署单点化本身就是一个故障源最后说个体会比较深的点。把单体拆成微服务时大家往往更关注拆几个服务、怎么拆、怎么通信忽略了一个前置条件每个服务必须能够独立部署和独立回滚。这里最实用的标准就是看发布频率如果一个服务改动不能单独发版必须连带其他服务一起上线那这个拆分就是名义上的拆分。我自己在迁移时有个很朴素的衡量方式如果改一行配置或一行业务逻辑我要动整个集群那说明拆分不够彻底如果能按服务独立发布、独立扩缩容才算真正吃到了K8s的红利。这个标准后来也成了我们内部互相评审的“口头禅”。5. 给同路人的最后几句心里话这篇写到这里基本把从零实操到线上排障的路数都过了一遍。回头看看K8s给我最大的冲击不是它的功能有多全而是它把之前分散在各处的手工习惯逼成了统一的声明式描述这个转变是根本性的。如果你也在某个夜里对着YAML文件发愁我想说这是正常的。当年我第一次看到那几百行配置也头皮发麻但熬过第一个完整跑通的服务后面再接触Istio、Helm这些周边你就会发现它们的难点其实都是同一个起点——先把Pod、Service、Deployment这三个概念在命令行里玩熟。还有个小经验想分享给刚上手的同学别一上来就追求“高可用”“多集群”先把单集群内的一套最小服务治理清楚比较切合实际。K8s的高可用是建立在一堆优质习惯之上的不是装好了就自动有的。等你把滚动更新、健康检查、资源配额这些基础项都变成肌肉记忆之后再聊多集群容灾才不会慌。这么说吧K8s这扇门跨过去之后真正值钱的不是那些命令而是你对“系统如何自愈”的理解方式。希望这篇记录能帮你把这种理解方式提前复制过去少走我走过的弯路。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询