Kubernetes基础概念详解:Pod、Deployment、Service与控制器模式
2026/9/11 9:12:28 网站建设 项目流程

先从一个真实的场景说起。我早年维护过好几台物理机,每一台上面都跑着十几个由 Docker 启动的容器,当时最头疼的问题是:某台机器负载一高,我得手动把容器迁移到另一台;某个容器挂了,依赖它的上游服务会报错,我得赶在监控告警之前手动把它拉起来。后来容器越上越多,从十来个涨到几十个,这套纯手动的玩法彻底玩不转了。这时候 Kubernetes(下文简称 k8s)进入我的视野,它解决的并不是"怎么把容器跑起来"这个问题,而是"怎么让几百个容器在几十台机器上有条不紊地跑、挂了自动重启、流量均匀分配、配置统一管理"这一整套集群编排问题。

这篇是 k8s 系列的第二篇,上一篇已经聊了 k8s 是什么、组件装起来长什么样,这一篇我打算把基础概念讲透。如果你已经能跑起一个集群,但对 Pod、Deployment、Service 这些对象之间的关系还停留在"会用、但说不清"的阶段,或者正准备背 k8s 面试题却总觉得知识点是散的,那这篇文章就是为你写的。我会尽量用一些生活化的类比来解释,把我踩过的坑和验证过的结论一并放进来。

1. 从容器到集群:k8s 到底解决了什么

1.1 单机时代的痛点

在没有 k8s 之前,用 Docker 跑应用其实已经很舒服了。你只需要写一个 Dockerfile,构建出镜像,然后docker run,一个东西就跑起来了。但当你开始认认真真部署生产环境,问题就来了:

  • 容器跑在某一台机器上,机器宕了,容器也没了。没人帮你把它换台机器拉起。
  • 应用要扩容,你手动在多台机器上挨个执行docker run,还要记得改负载均衡的配置。
  • 发一个新版本,你需要先停掉旧容器,再启动新容器,整个过程中服务会有一段不可用时间。
  • 容器之间要互相访问,你得自己维护一套 IP 和端口的映射关系,容器一重启 IP 变了,配置就乱套。

这些问题的根源在于:容器本身是一个单机概念,它不具备跨机器的协作编排能力。大家需要的,是一个能把成百上千台机器上的容器当成一个整体来管理的"调度系统"。

1.2 k8s 用一句大白话概括

k8s 的核心思想,可以概括成一句话:你告诉它"我要运行 3 个 Nginx 副本,每个副本监听 80 端口",然后什么都不用管了,它负责保证这 3 个副本长期存在、能正常对外提供服务。

这种模式叫做声明式管理。你声明期望状态,k8s 不断把实际状态向期望状态靠拢。这和传统命令式管理的区别,一个是"你给我把这件事做了",另一个是"我要这个结果,过程你自己看着办"。声明式是理解 k8s 一切行为的关键钥匙。

1.3 为什么单单 k8s 成了事实标准

业界不是没有别的编排工具,比如 Docker Swarm、Apache Mesos。但最终 k8s 胜出了。原因有几个层面:

  • 生态最大:几乎所有云厂商都提供了托管的 k8s 服务,任何软件想集成容器编排能力,第一选择都是对接 k8s。
  • 抽象模型设计得好:k8s 的 API 对象设计非常通用,从无状态应用、有状态应用、批处理任务到定时任务,都有对应的编排对象,而这套抽象和底层云厂商无关。
  • 可扩展性强:如果你觉得默认能力不够,可以用 CRD(自定义资源定义)+ Controller 这种机制去扩展它,很多高级功能都是在这个基础上长出来的。

提示:对于刚入门的人来说,不需要一开始就去对比 Swarm 和 k8s 的优劣。你只需要知道,今天在简历上写"熟悉 Docker"远没有"熟悉 Kubernetes"有分量,行业选择已经给出答案了。

2. 架构里的两个世界:控制面与工作节点

2.1 一个现实中的分工类比

理解 k8s 架构,我建议你先忘掉技术术语,想一个公司的运作方式。一个公司有管理层和一线员工:管理层负责定目标、做计划、下发指令、检查进度;一线员工负责实际干活,把任务落地。

k8s 也严格分成了这两个世界:

  • 控制面(Control Plane):相当于公司的管理层,负责整个集群的管理决策。
  • 工作节点(Node):相当于一线员工,实际运行容器的地方。

控制面如果挂了,集群里的容器并不会立刻停止运行,但你会失去管理集群的能力,就像公司管理层集体休假,员工照样按惯性上班,但遇到突发状况没人拍板。

2.2 控制面里都有谁

控制面通常由四个组件组成,在真正的生产环境中,它们一般以多副本的形式部署在多台机器上,保证高可用。我们来逐个拆解:

kube-apiserver:整个集群的"前台接待"。所有管理操作——无论是kubectl get pods还是创建一个 Deployment——都必须经过它。它负责认证、鉴权、校验请求,然后把数据写入 etcd。它是唯一一个会直接和 etcd 通信的组件。你可以把它理解成公司里唯一的对外窗口,任何其他部门想查资料、报备,都必须通过这里。

etcd:集群的"存档库"。它是一个分布式键值存储数据库,保存着集群的全部状态数据,比如你声明了哪些对象、它们当前处于什么状态。这里要强调一点:etcd 里不存业务数据,只存 k8s 集群自身的状态数据。所以即便整个集群崩溃,只要 etcd 的数据还在,你就能把集群恢复到崩溃前的状态。

kube-scheduler:集群的"人事调度员"。当你创建了一个新 Pod,它负责决定这个 Pod 应该被放到哪个 Node 上运行。它的决策依据包括节点资源是否足够、有哪些亲和性约束、是否有 taint/toleration 规则等。调度算法的朴素目标很简单:让每个 Pod 都落在"最合适"的机器上。

kube-controller-manager:集群的"监工"。它内部运行着多个控制器,比如节点控制器、副本控制器、端点控制器等。每个控制器都围绕着"把实际状态变成期望状态"这个目标工作。举个例子,Node Controller 会定期检查每个 Node 的健康状态,如果发现某个 Node 挂了,它会去把这个 Node 上的 Pod 在其他 Node 上重新创建出来。

2.3 工作节点上有什么

工作节点上运行着三个核心组件:

kubelet:节点上的"现场管理员"。它是控制面下派到每个节点的代表,负责接收控制面下发的 Pod 创建指令,然后调用容器运行时(如 containerd)真正地启动容器。kubelet 还负责定期上报节点的资源使用情况、容器运行状态给控制面。

kube-proxy:节点上的"路由器"。它负责实现 Service 的负载均衡功能。当流量要访问一个 Service 时,kube-proxy 会根据规则把流量转发到具体的某个 Pod 上。这个组件最容易在面试时被问到实现原理,常见的有 iptables 模式和 IPVS 模式,现在生产环境普遍用 IPVS 模式,因为它在大规模场景下更高效。

容器运行时(Container Runtime):真正启动容器的最小单元。以前大家用 Docker,现在 more 常见的是 containerd 或 CRI-O。对使用者来说,你基本上感知不到底层是哪一个运行时,它们的启动参数、日志风格略有不同,但遵循同一个 CRI(容器运行时接口)标准。

2.4 一次完整的请求流转过程

把以上组件串起来,看一条完整的时间线。你执行kubectl apply -f deployment.yaml

  1. kubectl 把请求发送到 kube-apiserver。
  2. kube-apiserver 校验通过后,把 Deployment 对象写入 etcd。
  3. Deployment Controller(运行在 kube-controller-manager 里)发现有一个新 Deployment,于是创建 ReplicaSet,进而创建 Pod 对象(此时 Pod 处于 Pending 状态)。
  4. kube-scheduler 监听到一个新的待调度 Pod,评估各节点的资源情况,决定把它调度到 Node A 上,并把调度结果写回 apiserver。
  5. Node A 上的 kubelet 发现有一个新 Pod 被调度到自己身上,于是调用容器运行时拉取镜像、启动容器。
  6. 容器启动成功后,kubelet 回写状态,Pod 进入 Running 状态。
  7. 当整个 k8s 体系稳定后,你会发现,从声明 Deployment 到 Pod 真正运行起来,完全是自动化的,中间没有任何人工介入。

注意:这个流转过程你不需要背下来,但最好能自己顺着这个链路走一遍。面试中被问到"创建一个 Pod 的完整流程"时,能按上面的顺序讲清楚,才是真的理解了架构,而不是死记硬背。

3. 三个最核心的对象:Pod、Deployment 与 Service

3.1 Pod:k8s 的最小调度单元

Pod 是 k8s 中最基础的对象,也是容器运行的实际载体。要理解 Pod,先要打破一个固有印象:k8s 不直接运行容器,Pod 才是最小的调度、部署和伸缩单元

一个 Pod 可以包含一个或多个容器,同一 Pod 内的容器共享网络命名空间、共享存储卷。它们可以通过 localhost 互相访问,这在设计上有一种有趣的类比:Pod 就像同一台主机上的多个进程,彼此之间有着天然的亲近关系和非常高效的通信方式。

最常见的场景是一个 Pod 只跑一个容器。但需要"主容器 + 辅助容器"配合的场景,比如主容器对外提供 Web 服务,旁边一个 Sidecar 容器负责收集日志、转发指标,把它们放在同一个 Pod 里就很合理。同一个 Pod 里的容器还会被调度到同一台 Node 上,保证它们永远在一起,不会发生"主容器在上海、附属容器在北京"的极端情况。

Pod 是短暂的、可以被随时销毁和重建的。所以你对 Pod 直接操作要有一个心理预期:今天它可能叫 nginx-abc123,明天重启后就会变成 nginx-def456,名字、IP 都可能变。正如我们后面要讲的,直接用 Pod 的 IP 去访问服务是非常不推荐的。

3.2 Deployment:声明期望状态的"岗位编制表"

Deployment 是大多数无状态应用的标准编排对象。它的核心职责是管理 ReplicaSet(副本集),而 ReplicaSet 的职责是保证指定数量的 Pod 一直存在。

用一个类比来理解:Deployment 就像公司里一张"岗位编制表",上面写着"前端团队需要保持 3 个岗位在位"。如果某一天有 1 个岗位的人辞职了(Pod 挂了),HR 会立刻招一个新的顶上;如果业务量暴涨,人不够用了,你把编制数从 3 改成 5,系统会自动多拉起 2 个。

实际操作中,你只需要在 YAML 里写一个 Deployment 对象,k8s 会自动帮你管理 ReplicaSet 和 Pod 的生命周期,不需要你手动去创建 Pod。发新版本时,你只需要修改镜像版本号并重新 apply,Deployment 会以滚动更新的方式逐步替换旧 Pod,整个过程对外服务不中断。

apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80

这里有几个关键字段需要解释:

  • replicas:期望的 Pod 副本数量。
  • spec.template:描述 Pod 的模板。Deployment 创建的所有 Pod 都基于这个模板。
  • selector.matchLabels:Deployment 通过这个标签选择器来管理它名下的 Pod。这个标签的匹配关系要仔细设计,一旦创建后修改标签,会引发对象管理关系断裂。

3.3 Service:稳定不变的"公司前台总机"

前面说 Pod 的 IP 是会变化的,这就带来一个问题:如果有一个后端服务,客户端怎么才知道当前访问哪几个 Pod?谁来做负载均衡?答案是 Service。

Service 提供的是一个稳定的虚拟 IP(ClusterIP)和 DNS 名字。客户端只需访问这个固定的地址,Service 会把流量负载均衡到后端的多个 Pod 上。可以理解成公司的总机号码——不管底下员工换了多少人、换了谁接听,外面的人只需要打总机,总机会帮你转接到合适的人。

这里一个很核心的知识点:Service 是依靠标签选择器和它后面的 Pod 建立关联的。它并不关心你 Pod 的生死,只要满足某个标签的 Pod 存在,流量就会送过去。

apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80

在这个例子里,Service 会把访问 80 端口的流量,转发到所有带app: nginx标签的 Pod 的 80 端口上。注意区分porttargetPortport是 Service 对外暴露的端口,客户端访问的是这个;targetPort是后端 Pod 实际监听的容器端口。

3.4 Namespace:集群里的"分隔墙"

Namespace 用来把集群里的资源进行逻辑隔离。比如你在测试环境和生产环境共用同一个集群,就可以创建testprod两个 Namespace,把不同环境的资源放进去。需要注意的是,Namespace 隔离的主要是管理层面的资源,而不是网络层面的安全隔离——默认情况下,不同 Namespace 里的 Pod 还是可以互通的,这一点在做安全设计时容易踩坑。

查看当前环境下的所有命名空间:kubectl get namespaces。几乎所有资源对象都可以放进某个 Namespace 里,除了少数集群级别的资源(如 Node、PersistentVolume)不能。

4. 网络模型与流量入口:从 ClusterIP 到 Ingress

4.1 为什么每个 Pod 都需要一个"私有 IP"

k8s 的网络模型设计有几个硬性要求:每个 Pod 都有自己独立的 IP,Pod 内的容器共享这个 IP;Pod 之间可以直接通过这个 IP 互相访问,不需要 NAT;不管这两个 Pod 在不在同一个节点上,网络行为是一致的。

这个设计最大的好处是:你把应用从一个节点迁移到另一个节点时,应用内部的网络逻辑完全不用改。对应用而言,它始终认为自己处在一个"所有 Pod 都能直连"的大二层网络里。

要实现这种效果,k8s 本身不提供网络实现,而是依赖 CNI(容器网络接口)插件。常见的 CNI 插件有 Calico、Flannel、Cilium 等。不同插件的底层实现各有侧重,Flannel 简单易用,Calico 性能好且支持网络策略,Cilium 基于 eBPF 提供了更强的可观测性和安全能力。我个人的选择倾向是,测试环境随便用一个,生产环境建议认真调研 Calico 或 Cilium。

提示:一个非常经典的坑是,在部署集群时没有安装 CNI 插件,导致所有 Pod 的 IP 都是空的,kubectl get pods看到的状态是 ContainerCreating / CrashLoopBackOff。遇到这种情况先别慌,检查一下有没有安装网络插件。

4.2 Service 的三种类型怎么选

Service 根据暴露方式的不同,可以分为四种类型(default 是 ClusterIP)。这里重点讲最常用的三种:

类型访问方式应用场景
ClusterIP集群内部通过虚拟 IP 访问仅内部服务调用,如后端 API 对前端服务暴露
NodePort通过每个节点的 IP + 指定端口访问需要从集群外部访问,但不希望依赖云厂商负载均衡器
LoadBalancer通过云厂商的负载均衡器访问云环境直接对外暴露服务,自带弹性 IP 和健康检查

ClusterIP 是默认且最典型的类型。它的 IP 是虚拟的,只在集群内部能路由到,外部网络直接访问这个 IP 是不通的。

NodePort 是 ClusterIP 的扩展。它在每个节点上都开一个端口(默认范围是 30000-32767),访问任意节点IP + NodePort就能把流量送进集群。它的缺点是端口数量有限、且需要自己管理节点的 IP 列表,适合做小规模验证。

LoadBalancer 要依赖云厂商实现,比如阿里云、AWS 都提供相应的插件。它创建时会自动创建一个云负载均衡器实例,并把流量转发到 NodePort 上,所以本质上 LoadBalancer 是在 NodePort 之上又套了一层。

4.3 Ingress:七层路由的"门卫"

Service 工作在四层(TCP/UDP),而 Ingress 工作在七层(HTTP/HTTPS),可以基于域名和路径做路由。假设你只有一个对外 IP,但需要给多个服务提供访问入口——api.example.com打到后端 API 服务,web.example.com打到前端 Web 服务,这用 Service 的 LoadBalancer 做,需要申请多个负载均衡器,成本很高;用 Ingress 做一个统一的入口,按域名分流,就优雅得多。

Ingress 本身只是一组转发规则,真正执行转发工作的是 Ingress Controller,比如 Nginx Ingress Controller、Traefik。部署完 Ingress Controller 后,你再创建 Ingress 对象,它才会真正生效。这个先后顺序经常有人搞错——先创建 Ingress 对象,再部署 Controller,结果发现规则一直不生效。

5. 配置与敏感数据:ConfigMap 和 Secret 的正确用法

5.1 为什么不能把配置写死在镜像里

在没有配置管理之前,很多人会把配置直接打进镜像里。然后每次环境不同(开发、测试、生产)就要重新构建一个镜像。这样做的问题是:

  • 镜像版本和配置强绑定,配置一改,镜像版本就要变。
  • 配置文件里如果包含数据库密码等敏感信息,镜像一旦被推送到私有仓库,等于把密钥散播出去了。
  • 临时想调一个环境变量,需要重新走一遍 CI/CD 流程,效率太低。

k8s 用 ConfigMap 和 Secret 把配置从镜像中抽离出来,让配置和代码解耦。

5.2 ConfigMap:明文配置的存放处

ConfigMap 可以存储键值对,也可以存储完整的配置文件内容。创建一个 ConfigMap:

apiVersion: v1 kind: ConfigMap metadata: name: app-config data: app.properties: | log.level=info cache.enabled=true MAX_CONNECTIONS: "100"

Pod 引用 ConfigMap 有两种方式,一种是环境变量,一种是挂载成文件。个人建议优先用挂载文件的方式,因为环境变量一旦 Pod 启动后就不会再更新,而挂载文件的方式可以通过更新 ConfigMap 让配置在运行中热更新(不过具体能否热更新成功,和应用自身有没有监听文件变化有关)。

5.3 Secret:敏感信息的安全存法

Secret 的用途和 ConfigMap 几乎一样,只是专门用来存敏感信息。它的值在 etcd 里是以 base64 编码存储的,注意是编码而不是加密,任何人只要对 etcd 有读取权限,就能轻松解码。所以在生产环境,通常还会配合 KMS 等外部加密方案来做加密。

使用 Secret 的典型场景是存储数据库密码、API Token、TLS 证书等。把它挂载成文件后,容器内看到的是明文文件,应用进程可以正常读取。

apiVersion: v1 kind: Secret metadata: name: db-secret type: Opaque data: password: bXktc2VjcmV0LXBhc3N3b3Jk

注意:Secret 的 base64 编码不是安全措施,只是避免在 YAML 文件里直接出现明文的权宜之计。提交 Secret 的 YAML 到 Git 仓库时,务必谨慎,最安全的做法是使用 sealed-secrets 或 external-secrets 等工具,让密钥不落盘。

5.4 存储卷:让数据不被"容器销毁"带走

容器是有状态的,但默认情况下容器里的文件系统是可写的,并且会随着容器销毁而消失。如果应用需要持久化数据(如数据库文件、日志文件),就必须给 Pod 挂载存储卷。

k8s 提供了多种卷类型:

  • emptyDir:临时卷,跟 Pod 生命周期一致,Pod 删除后卷中的数据也会被清空。适合存放临时文件、缓存数据。
  • hostPath:把节点上的某个目录挂载到容器里,适合单节点测试,但生产环境不推荐,因为 Pod 迁移到其他节点后数据不在。
  • PersistentVolumeClaim(PVC)+ PersistentVolume(PV):这是生产环境的正确姿势。PV 是集群管理员提供的存储资源(可以对接云上的云盘、NAS 或本地存储),PVC 是应用对存储资源的申请。两者关系,可以理解成"供应商"和"订单"。

如果你部署的是数据库这种有状态应用,就需要使用 StatefulSet 而不是 Deployment。StatefulSet 会为每个副本提供一个稳定的网络标识和独立的存储卷,这是 k8s 编排有状态应用的利器,不过篇幅有限,这里先不展开讲了。

6. 声明式 API 与控制器:理解一切自动化的底层逻辑

6.1 控制器模式是 k8s 的灵魂

如果只用一个词来概括 k8s 的核心机制,我会选"控制器模式"。前面提到过,你声明期望状态,k8s 保证实际状态向期望状态收敛。这个"保证"是怎么实现的,答案就是控制器模式。

控制器是一个循环运行的进程,它做的事情可以归纳为三步:

  1. 观察实际状态:从 apiserver 监听资源对象的当前状态。
  2. 对比期望状态:拿实际状态和你声明的期望状态做对比,找出差异。
  3. 执行变更:通过 apiserver 下发指令,让实际状态向期望状态靠近。

整个循环周而复始,每秒都在发生。kube-controller-manager 里有几十个控制器在同时工作,Deployment Controller、ReplicaSet Controller、Namespace Controller、ServiceAccount Controller 等,各管一摊,互不干扰。

6.2 为什么说"声明式"比"命令式"更适合大规模运维

命令式操作是你告诉系统"做什么",系统执行完就结束了。声明式操作是你告诉系统"要什么结果",系统会在后台持续保证这个结果成立。这个差异在大规模环境下非常关键:

  • 命令式的一次操作只覆盖一个瞬间,后续需要手工维护。
  • 声明式只要你不修改期望状态,系统会一直往这个状态靠。有人误删了你 3 个 Pod,ReplicaSet Controller 会在几秒内把它拉回 3 个;有人手动给 Pod 改了镜像标签,控制器照样会把它的实际状态改回期望状态。

我在实际维护中有一个很深的体会:k8s 里的 Pod 就像"韭菜",你割了(误删),它还会再长回来。如果你希望某些资源不要被控制器"管着",你就不应该用 Deployment、StatefulSet 这些控制器对象去创建它,直接用 Pod 创建就行(虽然这样没有自动恢复能力)。理解了这一层,你在排查很多"为什么我改了没用"的问题时,就能更快想到是不是控制器在捣乱。

6.3 一次实际排查:为什么我的 Pod 一直重启

说一个我自己踩过的坑。有一次我发现某个服务的 Pod 每隔几十秒就重启一次,kubectl get pods显示的状态一直是CrashLoopBackOff。一开始我以为是应用崩溃了,但kubectl logs看到的日志里明明显示应用正常启动了,最后一条日志是"listening on 8080"。

排查到后面才发现,问题出在存活探针上。我在部署 YAML 里配置了livenessProbe,指定了一个健康检查路径/healthz,但这个接口在应用里根本不存在,探针返回 404,kubelet 判定容器不健康,于是反复杀掉重启容器。CrashLoopBackOff 的根因并不一定是应用崩溃,很可能只是探针配置不合理或者就绪检查失败

这里补充一下探针的三个类型,这也是 k8s 面试的高频考点:

  • livenessProbe(存活探针):判断容器是否需要重启。如果失败,kubelet 会杀掉容器并重启它。
  • readinessProbe(就绪探针):判断容器是否具备接收流量的能力。如果失败,Service 会把该 Pod 从后端列表中移除,但不会重启它。
  • startupProbe(启动探针):用于启动特别慢的应用,在启动成功之前,不启动另外两个探针。

正确做法是,readiness 探针的路径要跟应用实际提供的健康检查接口一致,liveness 探针的时间要留足应用启动的时间余量,避免启动慢导致反复被杀。

7. 必须掌握的几个 kubectl 命令和踩坑经验

7.1 高频命令速查

网上 kubectl 命令速查表很多,但大多数都太全了,反而不利于记忆。我根据自己的日常使用,整理了一份真正高频的命令清单:

# 查看资源 kubectl get pods kubectl get deployment kubectl get svc kubectl get nodes # 查看详细信息和事件 kubectl describe pod <pod-name> kubectl get events --sort-by='.lastTimestamp' # 查看日志(-f 表示持续输出) kubectl logs -f <pod-name> # 进入容器 kubectl exec -it <pod-name> -- /bin/sh # 创建/更新资源 kubectl apply -f deployment.yaml # 删除资源 kubectl delete -f deployment.yaml # 查看所有命名空间的资源 kubectl get pods -A

这里我想多说一句:describe的输出信息量远大于get。当你遇到 Pod 一直 Pending、一直 ImagePullBackOff 的时候,第一反应不应该是上网搜,而是先kubectl describe pod <pod-name>,看看底部 Events 区域写了什么。绝大多数调度失败、镜像拉取失败、卷挂载失败的问题,Events 里都会直白地告诉你原因。

7.2 我总结的 5 条实操心得

  1. 先学会看 Events,再学会搜谷歌。k8s 的问题排查链路基本是:get看状态,describe看事件,logs看应用日志,这三级层层递进。不要一开始就陷入加班查资料的怪圈。

  2. 资源请求和限制一定要配。很多人部署应用不写resources.requestsresources.limits,这会带来两个问题:一是 Pod 被调度到某个节点时,调度器认为这个 Pod 不占资源,导致节点超卖;二是 Pod 在 CPU 密集场景下可能把节点资源吃满,影响同节点其他 Pod。生产环境请务必配置资源配额,这是根基。

  3. 删除 Pod 之前,想清楚它是不是 Deployment 管的。如果你直接kubectl delete pod xxx,Deployment 会在数秒内创建一个新 Pod,你会误以为"删不掉"。正确做法是删除 Deployment 或把 replicas 改成 0。

  4. 标签命名要有规范。很多人用标签时随便起名,导致 Service 选择器选错了 Pod,流量发到一个完全不相干的服务上。我常用的标签至少包含app(应用名)、env(环境)、version(版本号)三部分。

  5. 不要在生产集群上反复折腾。如果你还在学习阶段,我强烈建议用 kind(Kubernetes in Docker)或者 minikube 建一个本地单机集群用于练习。生产环境的改动都要走 review + 灰度流程,用测试集群验证思想,才能避免把生产环境搞炸。

7.3 升级版本前一定要做的事

k8s 的版本迭代非常快,每三个月左右会发一个小版本。很多人升级集群时踩到最大的坑是:旧的 API 版本被移除,导致集群中现有的 YAML 文件无法再被kubectl apply,甚至有些资源直接被忽略。

升级前一定要做的三件事:

  1. 查看当前集群版本:kubectl version
  2. 查看目标版本相对于当前版本的弃用 API 变更:官方文档的"Deprecated API Migration Guide"会列出所有被移除的 API 版本。
  3. 把现有的 YAML 清单更新到新的 API 版本,重新 apply 一次,确保兼容。

我遇到过最典型的情况是,集群从 1.20 升级到 1.26 之后,旧的extensions/v1beta1Ingress YAML 完全失效,导致所有 Ingress 规则悄然消失。这种问题隐蔽性很强,因为不是报错,而是规则静默不生效。

8. 从基础到进阶:下一步该怎么走

如果你能跟着这篇文章把 Pod、Deployment、Service、ConfigMap、存储、控制器模式这些概念都理解透了,恭喜你,k8s 的地基已经打牢了。接下来能走的方向大致有几条:

  • 服务网格方向:研究 Istio 或 Linkerd,理解在 k8s 之上做流量治理(熔断、限流、灰度发布)的思路。
  • 可观测性方向:把 Prometheus + Grafana 与 k8s 集成,熟悉如何采集指标、如何做告警,这是生产运维的核心命脉。
  • 平台工程方向:研究云原生 CI/CD(如 Argo CD)、GitOps 流程,理解"声明式"在交付流水线中的延伸。
  • Operator 方向:学习如何用 CRD + Controller 把复杂应用(如数据库)的运维经验固化成代码,实现"应用自运维"。

我个人在实际操作中的体会是,不要贪多,先把这 8 个章节讲的内容做实。找一个测试集群,把一个 Nginx 应用从 Deployment 部署,到 Service 暴露,到配置 ConfigMap 和 Secret,再挂上存储卷,把这套流程从头到尾自己手动走一遍,走完你就超过一半的入门者了。很多云原生岗位的面试官问到基础题,考察的其实就是你对这几个对象之间关系的理解是否到位,而不是你会不会背命令。

最后再分享一个小技巧:学习过程中遇到任何名词不懂,第一反应应该是去 k8s 官方文档搜,而不是直接看博客。官方文档的"概念"章节写得非常清晰,每篇都会有足够详细的示意图,很多博客实际上是在二手转述官方内容,信息损耗不小。把这个习惯坚持下去,你对 k8s 的理解深度会明显超出同龄人。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询