很多人刚开始学 k8s 的时候,第一反应是先去找一本大部头的 PDF 或者完整视频课程,然后被 Pod、Deployment、Service、Ingress 这些名词轮流砸晕。我当年也一样,看完文档的第一感受是:这玩意到底和 Docker 有什么区别?我直接在服务器上 docker run 不行吗?
先说结论:k8s(Kubernetes,中间那个 8 代表 “ubernete” 这 8 个字母)是目前事实上的容器编排标准。Docker 解决的是“在一台机器上把应用打包成容器跑起来”的问题,k8s 解决的是“几十台机器上几百个容器怎么调度、怎么保证不挂、怎么滚动升级不中断”的问题。这篇文章我会按自己真实的学习路径,把 k8s 的整体轮廓过一遍——解决什么问题、核心概念怎么记、环境怎么搭、常用命令有哪些、控制器和弹性伸缩是怎么回事,最后给你一份新手高频坑位排查手册。适合三类人:正准备入行云原生的开发者,正在做容器化改造的运维或后端,以及面试前需要快速建立 k8s 认知框架的人。
1. 到底什么是 k8s:先别急着敲命令,搞懂它在解决什么问题
1.1 Docker 玩得好好的,为什么还要 k8s
先回到最原始的痛点。假设早期你一个人维护一个小服务,流程很顺:代码写好,打包成 Docker 镜像,服务器上 docker run 一跑就完事。但业务一上来,情况立刻变复杂:服务从 1 个变成 10 个,机器从 1 台变成 5 台,每个服务的副本还要根据流量弹性伸缩。这时候你会发现,“登录某台服务器手动部署”这套老办法根本撑不住。
Docker Compose 能解决一部分问题,但它的本质是“单机编排”。想象一个电商后端,包含网关、用户、订单、商品、支付、消息队列 worker 六类服务,每类服务 3 个副本,分布在 5 台机器上。现在要升级订单服务,你会怎么做?先在每台机器上 pull 新镜像?还是在负载均衡上摘流量?如果订单服务所在的机器宕机了,谁把它自动拉起来?流量瞬间涨了 3 倍,你又怎么在 10 分钟内把订单服务从 3 副本扩到 10 副本?
这些问题的答案,正是 k8s 存在的意义。它把“人肉运维规则”固化成系统能力:调度器决定 Pod 落在哪台机器上,控制器保证副本数不漂移,Service 提供稳定的服务发现和负载均衡,探针能识别“进程还活着但服务已不可用”的假死状态并自动拉起。说白了,你只需要告诉 k8s“我期望这个服务有 3 个副本、用哪个镜像、开哪个端口、需要多少内存”,剩下的协调动作全部交给它。
1.2 一个生活化类比:从“厨师”到“餐厅经营”
把单容器想象成一位厨师在厨房里炒菜,你喊一声“菜单来一份”,他把菜端出来。但一家餐厅要正常运转,光有厨师远远不够:你需要排班表保证每个岗位随时有人(对应调度),传菜路径保证菜准确送到对应桌台(对应 Service),菜品标准保证做出来的东西不是时好时坏(对应声明式期望状态),卫生巡检保证后厨不出问题(对应健康检查),客人突然爆满时还要加桌加人(对应弹性伸缩)。
k8s 在集群里扮演的,就是这套“餐厅经营系统”的负责人。开发者和运维只需要把“菜品标准”(比如 Deployment、Pod 的定义)写清楚,剩下的事情——Pod 在哪台机器跑、挂了怎么拉起、升级时先停哪个再起哪个——都交给控制循环去反复协调。这是我对新手解释 k8s 最直观的方式:它不是单一工具,而是一整套“让容器化应用在机器集群中稳定运行”的调度与管理机制。
1.3 k8s 不是什么:先把边界划清楚
划清边界比背概念更重要。第一,上了 k8s 不等于高可用。如果你只部署 1 个副本,机器一挂服务照样停。高可用需要你设计副本数、使用多节点、配置 PDB(PodDisruptionBudget)防止发布时副本被全部打掉。第二,k8s 并没有“取代 Docker”。它现在通过标准的 CRI(容器运行时接口)对接底层运行时,常见选择是 containerd、CRI-O 等。第三,不是所有项目都适合上 k8s。一个内部小工具、一个单机应用、一个生命周期只有几分钟的脚本任务,用 docker-compose 或普通容器反而更省心。k8s 的复杂度摆在那里,业务至少要到“多服务、多副本、需要自动扩容或快速发布”这个规模,收益才会真正显现。
这也是每次看到有人问“k8s 和 docker 区别”时,我最想说的一句大实话:两者根本不是替代关系,而是不同层级的配合关系。先有容器,再有容器编排。把这一点想明白,后面的学习会顺很多。
2. 核心概念别硬背:给每个名词配一个“中文外号”
2.1 Pod:最小调度单元,不是“单个容器”
很多新手以为 k8s 直接管容器,其实 k8s 最小的调度和部署单元是 Pod。一个 Pod 里可以有一个容器,也可以有多个容器,这些容器共享同一个网络命名空间(共享 IP 和端口)、共享存储卷,并且永远被调度到同一台机器上。
为什么要多包一层 Pod,而不是直接管容器?因为有些场景下,多个进程必须“绑定”在一起才有意义。最典型的是 Sidecar 模式:主容器跑业务逻辑,旁边一个容器负责日志采集、流量代理或配置刷新,两者同生共死,共享命运。把 Pod 想成一个“合租的房子”就很形象:里面住了几个室友,共享一个门牌号(IP)和公共区域(Volume),但各自干各自的活。对外访问的是整个房子的门牌号,而不是某个房间。
2.2 Deployment 的中文名,我愿叫它“排班主管”
有人问过:如果给 k8s 的 Deployment 一个中文名字,应该叫什么?我觉得最贴切的是“排班主管”。你告诉它:“这个服务我希望保持 3 个副本在线,镜像版本是 v2.0,就绪探针 30 秒超时。” 然后它每隔一段时间检查一次集群里的实际状态,发现只有 2 个副本就补建 1 个,发现有 5 个就杀掉 2 个。发布新版本时,它按策略先启动新 Pod、等就绪了再停旧 Pod,这叫滚动更新;更新出问题,还能一键回滚到上一个版本。
你要记得一个关键点:Deployment 管理的是无状态服务,每个 Pod 都可以被随意替换、删除、重建。如果你的应用有状态(比如数据库、需要稳定网络标识的中间件),就要换 StatefulSet,这个在第 5 章会展开对比。
2.3 Service 与 Ingress:稳定的门牌号和流量入口
Pod 是会“漂移”的。今天在 node1,明天节点重启就可能落在 node2,而且 Pod IP 每次创建都会变化。如果你的服务 A 要调用服务 B,总不能每次都去查最新 IP 吧?Service 就是解决这个问题的。Service 创建后会得到一个稳定的虚拟 IP 和 DNS 名字,通过标签选择器(selector)找到后端的一组 Pod,把请求转发过去,自带简单的负载均衡。你可以把 Service 理解成“总机”:不管后端的员工怎么流动,你拨打总机号码都能找到对应的人。
Ingress 则更靠外。Service 的 ClusterIP 只能在集群内部访问,想让外部用户通过域名访问你的服务,就要使用 Ingress。Ingress 可以按域名、按 URL 路径把流量路由到不同 Service,相当于“值班前台”:访客说找销售部,它分流到销售部;说找技术部,它分流到技术部。
在 k8s 里,一条完整的对外流量路径通常是:客户端 → Ingress → Service → Pod。背下这条链路,你对 k8s 的访问模型就有画面了。
2.4 其它高频配角:Namespace / ConfigMap / Secret / PV
除开上面几个主角,下面这些“配角”出现频率也非常高,建议一开始就认识。
Namespace 是逻辑隔离空间,作用很像公司里的部门划分。同一个集群里,可以建 dev、staging、prod 三个命名空间,把不同环境的资源隔开,再通过 RBAC 控制谁能看哪个空间。注意它不是强隔离,更不是安全边界,Pod 之间的网络默认还是互通的。
ConfigMap 和 Secret 都是配置管理手段。ConfigMap 存普通配置(日志级别、开关项),Secret 存敏感信息(数据库密码、API Key)。它们最大的价值是让配置从镜像中解耦——改配置不用重新打包镜像,改完在 Pod 声明里引用即可。但要注意一个常见误解:Secret 只是 Base64 编码,不是加密,真正需要加密的敏感数据还要配合外部密钥管理系统。
PV/PVC 是持久化存储抽象。PV(PersistentVolume)相当于一个仓库,PVC(PersistentVolumeClaim)相当于你向仓库申请的空间租约。数据库、消息队列这类有状态服务需要把数据写到 PV 上,这样 Pod 被删重建后数据还在。记住一句顺口溜:“Pod 会死、IP 会变、配置要解耦、数据要外置”,这就是 k8s 设计者希望你使用它的方式。
我把这些高频概念整理成了一张速记表,方便你贴在显示器旁边:
| 名词 | 一句话人话 | 生活类比 |
|---|---|---|
| Pod | 一组绑定的容器,共享网络和存储 | 合租室友 |
| Deployment | 管理无状态服务副本数和滚动更新 | 排班主管 |
| Service | 给一组 Pod 提供稳定访问入口和负载均衡 | 总机/前台 |
| Ingress | 按域名/路径分发外部 HTTP/HTTPS 流量 | 门口分流员 |
| Namespace | 集群内的逻辑隔离空间 | 不同楼栋 |
| ConfigMap | 不打包进镜像的普通配置 | 员工手册 |
| Secret | 存放敏感配置的配置对象(注意不是加密) | 保险柜 |
| PV/PVC | 持久化存储的抽象 | 仓库与租约 |
3. 环境搭建三条路:学习、生产、托管怎么选
3.1 本地快速上手:minikube 与 kind
环境搭建是入门的第一道坎。很多人一上来就搜“k8s集群搭建”,照着教程在云服务器上装 kubeadm,装到一半网络插件起不来、Dashboard 打不开,半小时热情全没了。我的建议是:如果只是学习,第一步不要碰生产级安装,先在本地把最小集群跑起来。
minikube 是最常见的本地学习方案。一条minikube start就能在你的电脑上启动一个单节点 k8s 集群,默认通过容器或虚拟机隔离,支持大部分核心功能,还自带 Dashboard。如果你希望用 Docker 容器直接模拟一个“假多节点”环境,kind 更合适——它把 k8s 节点跑在 Docker 容器里,启动快、资源消耗小,特别适合 CI 里快速验证 yaml。本地学习的期望值要放对:目的是熟悉核心概念和 kubectl 手感,而不是复刻生产架构。那些“三台 master 高可用”的复杂方案,等理解了基本原理再上也不迟。
3.2 生产级自建:kubeadm 与 KubeKey
想在生产自建 k8s,社区最主流的路线是 kubeadm。它算是官方出品的半自动安装工具,完整流程大概是:所有机器装好容器运行时(通常 containerd)→ 在 master 节点执行kubeadm init→ 按提示配置 kubeconfig → 安装 CNI 网络插件(Calico 或 Cilium)→ worker 节点执行kubeadm join加入集群。这套流程能让你理解每个组件的位置,比如 kube-apiserver、kube-controller-manager、kube-scheduler、kubelet、etcd 是怎么组合起来的,所以我很推荐有耐心的新手完整走一遍。
如果你不想纠结复杂参数,KubeKey 这类工具会更省心,它把 kubeadm 的底层流程封装成一条命令或一个配置文件,支持一键部署高可用集群,还可以顺带装上 KubeSphere 运维面板。热词里有人问“三台 master 怎么保证高可用 kubekey”,这里稍微展开:常规做法是在三台 master 前面放一个负载均衡入口(VPC 内的 SLB,或者 keepalived+nginx 都行),三个 kube-apiserver 挂到负载均衡后面,etcd 以三副本方式部署。KubeKey 会自动生成这套高可用配置,省掉不少手工步骤。这套架构的稳定性关键,反而不在 k8s 组件本身,而在 etcd 三节点的网络质量和负载均衡的健康检查配置。
3.3 别忘了证书续期:1 年后的定时炸弹
这是一个非常容易被忽视、但到点必然爆雷的问题。用 kubeadm 搭建的集群,默认签发的静态证书有效期大多是 1 年。集群跑满一年后,kube-apiserver 和 etcd 之间的通信证书、kubectl 访问用的 admin.conf 等都会陆续过期,现象就是 kubectl 突然报 x509 证书过期,整个集群管理面陷入瘫痪。
解决办法不复杂:定期执行kubeadm certs renew all,然后更新 kubeconfig,重启相关组件即可。生产环境建议把续签写成定时任务,比如每月检查一次证书剩余时间,临近 30 天时自动续签。否则一旦过年放假没人值班,节后回来发现集群全线失联,那个体验我已经见过太多了。如果你用的是云托管集群,这个事通常由云厂商帮你处理;但自建集群必须自己立项,把它写进运维手册和监控告警里。
3.4 单节点、托管集群与迁移到云上怎么权衡
说完搭建方式,再看部署形态。很多团队的 k8s 旅程始于一个很朴素的需求:先有一整套业务环境在单节点 k8s 上跑通,再考虑迁到云上。单节点自建的好处是成本极低、环境可控,适合做功能验证和测试;但单点故障和性能上限都摆在那里。
如果最终目标是云上运行,我更建议直接使用托管集群,把控制面的运维复杂度外包出去。热词里有个场景很有代表性:本地单节点 k8s 上跑着若依微服务整套环境,希望不停服、不丢数据地迁移到阿里云 ECS,迁移后用 JMeter 做高并发压测。这种迁移看起来简单,踩坑点却不少。
首先,镜像要处理。本地私有仓库的镜像需要推送到目标环境能访问的镜像仓库。其次,数据是重中之重。数据库这类有状态服务要做持久卷级迁移,推荐先在目标集群把 PV 建好,把数据通过备份恢复或在线同步导过去,再切换流量,才能做到不丢数据。再次,网络要改。单节点环境的 NodePort 在云上通常要换成 LoadBalancer 类型,或者前置云 SLB + Ingress,域名和证书也要提前切好。最后才是压测——用 JMeter 验证新集群的承载能力,同时配合 Prometheus 监控看资源水位,才能知道“能跑”和“扛得住”之间的差距。
4. kubectl 常用命令:会这一张表就够日常用了
4.1 查看类:get / describe / logs
kubectl 是操作 k8s 的入口,命令很多,但日常 80% 的场景其实集中在几个命令上。查看类三件套是 get、describe、logs。
kubectl get nodes看节点状态,kubectl get pods -n demo看 demo 命名空间下的所有 Pod。get 后面还可以跟很多资源类型:svc、deploy、events 等,输出的是精简列表,适合快速看全局。
kubectl describe是进阶版,它会输出某个对象的详细信息和事件流水。新手排查 Pod 卡住时,最该做的就是kubectl describe pod <pod-name> -n demo,看底部的 Events 区域,那里会明确写出调度失败、镜像拉取失败、探针失败等真实原因。这里的优先级通常高于 logs。
kubectl logs看容器日志,加 -f 持续跟踪,新版本还支持直接看一个 Deployment 下所有 Pod 的日志流。我的排查习惯是:先 get 看整体状态,再 describe 看事件,最后才 logs 看应用日志。顺序反过来,你会在应用日志里找半天,最后发现根因是镜像标签写错。
4.2 操作类:apply / delete / exec / scale / rollout
日常操作里,kubectl apply -f xxx.yaml是最常用的部署方式,它是声明式的:无论文件里描述的是新建还是更新,apply 都会把实际状态向期望状态逼近。kubectl delete删除资源,但注意删除 Pod 不等于移除应用,Deployment 会立刻重新拉起一个新 Pod,这是正常现象。
需要进容器排障时,用kubectl exec -it <pod-name> -n demo -- /bin/sh。临时调整副本数用kubectl scale deployment demo --replicas=5。发布新镜像版本时,用kubectl set image deployment/demo demo=nginx:1.25,或者直接改 yaml 后 apply。kubectl rollout status deployment/demo可以等待滚动更新完成,kubectl rollout undo deployment/demo一键回滚。kubectl port-forward svc/demo 8080:80可以把集群内服务临时映射到本地端口,调试时非常实用。
我把最常用的命令整理成了一张速查表,可以直接抄:
| 分类 | 命令 | 典型用途 |
|---|---|---|
| 查看 | kubectl get nodes / pods / svc / deploy | 查看各类资源状态 |
| 详情 | kubectl describe pod -n | 看事件和详细状态 |
| 日志 | kubectl logs -f | 跟踪容器日志 |
| 部署 | kubectl apply -f manifest.yaml | 声明式创建/更新资源 |
| 扩容 | kubectl scale deployment --replicas=N | 手动调整副本数 |
| 更新 | kubectl set image deployment/ = | 滚动更新镜像 |
| 回滚 | kubectl rollout undo deployment/ | 回滚到上一个版本 |
| 进入容器 | kubectl exec -it -n -- /bin/sh | 进容器调试 |
| 本地映射 | kubectl port-forward svc/ 8080:80 | 把集群服务映射到本地端口 |
4.3 新手最容易犯的 4 个“反模式”
有些反模式,我几乎每次带新人都会看到,提前说出来能帮你省时间。
第一,用kubectl run nginx --image=nginx直接创建 Pod。这样创建的 Pod 没有任何控制器保护,节点一挂 Pod 就永久消失。正确做法是用 Deployment 或 yaml 管理。
第二,排查时不停执行kubectl delete pod,而不是看事件。Pod 反复重启大概率是配置或探针问题,删了只会让控制循环再拉一个新 Pod,问题依旧。
第三,在生产环境直接用命令改配置,但不落到 Git。k8s 的价值之一就是基础设施即代码,改完 yaml 应该提交版本库,否则线上状态就是一个黑盒。
第四,全局不加 -n 参数。默认命名空间很容易串环境,在 prod 命名空间执行删除之前,先kubectl config get-contexts看一眼当前 context 是谁。
5. 控制器与弹性伸缩:应用“永远在线”靠的是谁
5.1 控制器家族怎么选
Deployment 负责无状态应用,但它只是控制器家族的一员。按应用类型选控制器,是入门进阶必须掌握的一课。
Deployment 适合 Web API、后端服务等无状态应用,Pod 可以被任意替换。StatefulSet 适合数据库、Redis、Zookeeper 等有状态应用,它保证每个 Pod 有稳定的网络标识(比如 podname-0、podname-1)、稳定的存储卷绑定,并支持按顺序启停。很多新手把数据库塞进 Deployment,一扩缩容就发现数据乱套,这就是没理解两种控制器的设计差异。
DaemonSet 适合日志采集(Filebeat/Fluentd)、监控 Agent(node-exporter)这类必须“一机一个”的场景。Job 跑一次性任务(批量导入、数据清洗),CronJob 按定时表达式跑,类似 crontab 的 k8s 版。
选型可以记一句口诀:无状态上 Deployment,有状态上 StatefulSet,每节点一个上 DaemonSet,跑批任务用 Job、定时任务用 CronJob。
| 控制器 | 适合场景 | 关键特征 |
|---|---|---|
| Deployment | 无状态服务(API、Web) | 可随意替换 Pod |
| StatefulSet | 有状态服务(DB、Redis) | 稳定网络标识+持久卷 |
| DaemonSet | 每节点一个(日志、监控Agent) | 自动跟随节点 |
| Job/CronJob | 一次性/定时任务 | 执行完自动退出 |
5.2 高并发不是某个组件,而是组合拳
热词里有人问“k8s 用于处理高并发的组件是哪个”,这个问题值得先纠正一下:k8s 的高并发能力从来不是某一个组件单独扛住的,而是一套组合拳。
最常见的自动扩缩组件叫 HPA(HorizontalPodAutoscaler)。它通过 metrics-server 定期获取 Pod 的 CPU/内存等指标,当指标超过阈值时自动调整 Deployment 的副本数。比如订单服务 CPU 使用率超过 70%,HPA 自动从 3 个副本扩到 10 个;压力下降后再缩回 3 个。进阶玩法可以配合 KEDA 监听队列深度一类的自定义指标。但 HPA 只管“应用副本的横向扩缩”,如果整个集群的机器都不够塞新 Pod,你还需要 Cluster Autoscaler 或云厂商的节点组能力,让机器本身也能弹性伸缩。
更底层的是 Ingress 和 Service 的负载均衡能力。流量先经过 Ingress Controller 分发到 Service,再被转发到各个 Pod,配合 Pod 的 readinessProbe(就绪探针),可以保证只有真正能处理请求的 Pod 才会被转发流量。生产常用还有 PDB(PodDisruptionBudget),它保证节点维护或集群升级时,至少有多少副本存活,不会一次性全部不可用。
一句话总结:高并发 = Service/Ingress 分流 + HPA 弹性扩缩 + readiness 探针保护 + 节点充足余量,四者缺一不可。
6. 学完基础后值得跟进的 3 个方向
6.1 监控告警:Prometheus 全家桶分工
集群搭好、应用跑起来之后,第一件要紧事就是把监控补上。社区最主流的方案是 Prometheus + Grafana,很多团队直接部署 kube-prometheus-stack 这个全家桶 chart,一条命令就能拉起整套监控体系。
全家桶里每个组件分工明确:Prometheus 负责拉取和存储指标;node-exporter 部署在所有节点上采集主机指标(CPU、内存、磁盘);kube-state-metrics 把 k8s 对象的状态(Pod 数量、Deployment 副本数、HPA 状态等)暴露成指标;Grafana 负责可视化,社区现成的 K8s 运维 Dashboard 非常多,导入即可用。再加上 Alertmanager 配置告警规则,比如 Pod 重启次数异常、节点磁盘使用率超 80%、证书即将过期,都能及时推送消息。
我见过很多团队把监控部署完就当完事了,其实更关键的是告警阈值和联系人。没有告警的监控只是“事后博物馆”,要在事情发生前 15 分钟知道风险,而不是事故后翻图表复盘。
6.2 效率工具:从 kubectl 到可视化面板
kubectl 虽强,但纯文字界面在排查多资源问题时效率有限。这里说几个常见的效率工具。
k9s 是很多老手离不开的终端 UI,纯键盘操作,列表里直接按 d 进入 describe、按 l 看日志、按 s 进入 shell,输入 / 还能模糊搜索,比反复敲一长串命令快很多。Lens 是桌面客户端,图形化查看集群资源和实时状态,适合不习惯终端的人。如果团队需要更完整的平台化管理,可以上 Rancher 或 KubeSphere 这种多集群管理平台,它们能在一个 Web 界面里管理多个集群、项目和权限。国内还有 Kuboard,中文界面、上手门槛低,对新手特别友好。
至于官方提供的 Dashboard(也就是 k8s 原生管理页面),功能中规中矩,胜在原生可靠。要注意它的访问一般需要 Bearer Token 或 kubeconfig 认证,默认给的权限很大,务必按最小权限原则为不同成员创建独立 ServiceAccount,不要直接拿管理员 token 到处乱贴。
6.3 Service Mesh、GPU 调度与微服务迁移实战
再往后走,就进入偏进阶的方向了。热词里出现过的 Dubbo Mesh(把 Dubbo 这类微服务框架放进 Service Mesh 治理),本质上是把服务发现、熔断、限流、可观测性下沉到基础设施层。Istio 和 Linkerd 是目前主流的 Service Mesh 实现,学习曲线不低,但如果你所在团队正在做微服务拆分、又对框架层面的治理能力不满意,这个方向值得深入研究。
GPU 调度是 AI 场景绕不开的话题。k8s 要调用 GPU,前提是节点上装好 GPU 驱动,并部署对应厂商的 device plugin(比如 NVIDIA 的 nvidia-device-plugin)作为 DaemonSet。部署完成后,GPU 就成了 k8s 的一种可调度资源,Pod 声明resources.limits["nvidia.com/gpu"]: 1就能申请一块 GPU,调度器会自动把它分配到有 GPU 的节点。常见坑有三个:驱动版本和容器运行时不匹配、没装 device plugin、Pod 请求的卡数超过节点总量导致一直 Pending。排查思路仍然可以回到第 7 章的 describe 大法。
再说回热词里那个若依微服务迁移案例。这种“整套微服务 + 压测验证”的场景,其实就是前面所有知识点的综合演练:集群搭建在第 3 章,核心对象编写在第 2、4 章,控制器选型在第 5 章,监控验证在 6.1 节。迁移完成后用 JMeter 打高并发,观察 HPA 是否按预期扩容、Prometheus 里资源水位是否合理,就能把“能用”变成“扛得住”。
7. 新手踩坑实录:6 个高频问题排查手册
先给你一张高频问题速查表,后面逐个展开:
| 症状 | 首选排查命令 | 高频原因 |
|---|---|---|
| ImagePullBackOff | kubectl describe pod xxx | 镜像名错 / 仓库认证 / 网络问题 |
| Pending | kubectl describe pod xxx | 资源不足 / 污点 / 节点选择 |
| NotReady | journalctl -u kubelet -f | kubelet 异常 / CNI 插件 |
| CrashLoopBackOff | kubectl logs pod --previous | 启动报错 / 探针配置 |
| 证书过期 | kubeadm certs check-expiration | 一年有效期未续签 |
| Service 不通 | kubectl get endpoints | selector 标签不匹配 |
7.1 镜像拉取失败:先分清是网络、认证还是镜像名问题
几乎所有人都会第一次遇到 ImagePullBackOff。看到这个状态先别慌,按顺序排查三件事。
第一,看 Events。kubectl describe pod <pod-name>底部的 Events 会写明具体错误。如果报的是镜像 404 或 manifest unknown,多半是镜像名写错或 tag 不存在;如果报的是 authentication required,说明私有镜像仓库需要认证,要在 Pod 里指定 imagePullSecrets;如果报的是 dial tcp 超时、connection refused 这类网络错误,该配镜像加速器的配加速器、该检查仓库连通性的检查连通性。
第二,检查镜像策略。imagePullPolicy 有 Always、IfNotPresent、Never 三种,分别表示总是拉取、本地没有才拉、只从本地找。本地测试如果用 minikube,经常需要先把镜像导入 minikube 的容器运行时;如果策略是 Always,镜像已经存在本地也照样会去仓库拉,网络不通就会失败。
第三,确认节点和认证是否匹配。多节点集群里,镜像拉取发生在实际调度到的节点上,如果你只在某个节点手动 docker pull 了私有镜像,但调度到了另一个节点,照样拉不下来。规范做法是每台节点都能访问镜像仓库,或者统一配置 imagePullSecrets。
7.2 Pod 一直 Pending:不要急着删,要看调度失败原因
Pod 一直 Pending,说明它还没被调度到任何节点。最常见原因有:集群资源不足(所有节点的 CPU/Memory 都不满足 requests)、节点存在污点(taint)而 Pod 没有对应容忍(toleration)、NodeSelector 选中不了合法节点、或者单节点集群里 master 默认不调度业务 Pod(需要去除 NoSchedule 污点或调整调度配置)。
排查步骤很固定:kubectl describe pod <pod-name>,看 Events。如果是 0/1 nodes are available,后面会跟每个节点失败的原因,比如 Insufficient cpu、Insufficient memory、node(s) had taint。根据原因对症下药即可:加机器、调小 request、加 toleration,或者把业务 Pod 调度到 master。
这里提醒一句:很多新手看到 Pending 就反复重启集群或者直接删 Pod,这基本是无效操作,因为控制循环已经在不停重试。正确思路永远是从 Events 找线索,一次 describe 往往胜过十次盲目动手。
7.3 节点 NotReady:多半是 kubelet 和 CNI 的锅
节点状态变成 NotReady,基本等于这台机器上的容器运行时和集群失联了。第一步先看 kubelet 服务状态:systemctl status kubelet,再用journalctl -u kubelet -f看日志。常见原因包括:容器运行时配置没对齐(比如 containerd 的 sandbox_image 指向的镜像拉不下来)、kubelet 证书过期、节点负载过高导致心跳超时。
另一个高发原因是 CNI 网络插件没装好。集群初始化之后 CoreDNS 一直 CrashLoopBackOff,大概率就是没装 Calico 或 Cilium。安装之后还要确认插件 Pod 正常运行,不同插件的默认网段不能和服务器实际网段冲突,否则节点之间通信数据包会被路由丢掉,表现为 Pod 之间时而通时而不通。
处理这种问题最忌讳“头痛医头”。先确认是 kubelet 进程的问题还是网络插件的问题,再决定重启哪个服务。否则你重启十次 kubelet,问题可能出在 /etc/cni/net.d/ 下的配置没清理干净。
7.4 CrashLoopBackOff:先看日志,再查探针
CrashLoopBackOff 表示容器启动后又退出,反复循环。排查第一入口永远是容器日志:kubectl logs <pod-name> -n <ns>,看进程退出的真实原因。如果日志是空的,再看看上一个崩溃实例:kubectl logs <pod-name> --previous,很多应用在首次启动时打印了错误,但容器退出后日志被新实例覆盖了。
如果日志显示应用其实启动成功了,但 k8s 仍然认为它不健康,那就要查探针配置。startupProbe 适合启动慢的应用,如果设置不当,可能在镜像启动完成之前就被 livenessProbe 杀掉;readinessProbe 如果指定了不存在的路径,应用会一直进不了 Ready 状态,流量切不进来。常见的坑还有:应用需要环境变量,但 Secret 或 ConfigMap 没挂载成功;镜像里默认监听端口和探测端口不一致;数据库连接串没通导致应用启动即报错退出。从日志 → 配置 → 探针这个顺序走,基本都能定位。
7.5 证书过期:别等它爆雷
证书过期往往是最安静的故障,前期毫无症状,一到时间点全部集中爆发。症状常表现为:kubectl 连接集群报 x509: certificate has expired or is not yet valid;kube-apiserver 日志里大量 TLS 报错;节点上的 kubelet 无法与 apiserver 正常通信。
解决流程参考 kubeadm 的官方做法:在 master 节点执行kubeadm certs renew all把集群静态证书全部续签,然后刷新 kubeconfig(重新生成 admin.conf 并更新本地配置),最后重启 kubelet 让 apiserver 等静态 Pod 重新载入证书。生产环境务必把续签流程做成定时任务或预检脚本,每月检查一次证书剩余天数,并在到期前 30 天自动处理。经历过一次节假日期间集群全线失联之后,你会明白这套机制有多重要。
# 查看所有证书的过期时间 kubeadm certs check-expiration # 续签全部证书 kubeadm certs renew all7.6 Service 不通:先看 Endpoints
Service 不通是典型的“看起来简单、实际一个标签对不上就完蛋”的问题。排查顺序:kubectl get svc看 Service 类型,kubectl get endpoints <svc-name>看这个 Service 是否成功关联到 Pod。如果 Endpoints 列表为空,说明 Service 的 selector 和 Pod 的 labels 没对上,这是最常见的原因——多写或少写一个标签,Pod 就永远进不了 Service 的负载池。
还有一个容易踩的坑:不要拿集群外机器直接访问 ClusterIP 类型的 Service。ClusterIP 只在集群内部可达,外部访问要用 NodePort、LoadBalancer 或 Ingress。如果你在云上环境用 NodePort 访问不到,看看云安全组是否放行了对应端口;本地调试就用kubectl port-forward svc/<name> 8080:80最省事。另外,Service 的 targetPort 必须和 Pod 容器真正监听的端口一致,很多 Service 半通不通的案例,最后都发现是 targetPort 写成了端口名,而容器根本没定义这个 port name。
最后说一点个人体会。学 k8s 最大的坎不是命令记不住,而是思维方式要从“命令式”切换到“声明式”。你以前习惯了在服务器上手动 docker run、systemctl restart,每一步都要自己控制;而在 k8s 的世界里,你的核心工作是描述“期望状态”,剩下的控制循环会不断把现实收敛到期望。
很多新手焦虑于“我 apply 之后怎么没动静”,其实 k8s 正在后台偷偷协调。多建 Pod、多删 Pod、故意搞坏一个镜像看看 k8s 会怎么反应,把手弄脏永远比看十本 PDF 有用。先把这篇文章里的概念在 minikube 上挨个实践一遍,再考虑集群搭建和监控,入门就能少走很多弯路。