☰
绕不开的Kubernetes:核心概念、容器编排与生产实践
2026/9/30 3:29:20 网站建设 项目流程

1. 为什么你绕不开 Kubernetes

我最早接触 Kubernetes 是在 2018 年,当时公司要上一套微服务,服务数量从十几个一下子涨到上百个,运维同学每天被“哪个实例挂了”“依赖服务连不上”“配置又改错了”这类问题折磨到心态爆炸。老办法是脚本加自动化平台,但一次发布失败之后,发现在脚本里打补丁只会让系统越来越难维护。后来决定整体迁到 Kubernetes,过程痛苦,但两年后再回头看,这套架构至少帮我们省掉了三个专职运维的岗位。

如果你现在正准备学 Kubernetes,或者已经在公司内部被要求“把服务容器化一下”,我建议你先别急着敲命令,先把下面这些概念吃透。Kubernetes 不是某个单一工具,它是一个完整的容器编排生态,要理解它,必须先理解它要解决什么问题、由哪些核心组件组成、以及你手上的服务在它上面跑起来之后整个生命周期长什么样。

这篇文章不打算做那种“从入门到放弃”的教程,我会从实际使用的角度,把这几年踩过的坑、梳理清楚的逻辑、还有最值得花时间理解的部分一次性讲清楚。无论你是开发、运维还是测试,只要你的业务开始向微服务和容器化靠拢,这篇文章都能给你一个清晰的认知框架。

2. 它到底在解决什么问题

2.1 容器只是第一步,编排才是关键

很多人会把 Docker 和 Kubernetes 混为一谈,这是一个非常大的误区。Docker 解决的是“怎么把应用装进一个标准化的箱子里”,而 Kubernetes 解决的是“怎么管理成千上万个这样的箱子”。

拿日常生活来类比:Docker 是打包盒,能把饭菜装进去;Kubernetes 是中央厨房,它知道哪个菜该送到哪个桌,哪个厨师忙坏了需要调休,哪个灶台坏了需要检修,哪个客人点的菜需要加急处理。你不需要关心每一份菜是怎么被端上桌的,只要告诉厨房“我要 20 桌客人,每桌四菜一汤”,剩下的事它自动搞定。

具体到技术场景,Kubernetes 管理容器主要围绕这五类能力:

  • 调度:把容器放在最合适的节点上,比如某台机器内存充裕、某台机器离数据更近。
  • 伸缩:流量涨了自动加实例,流量降了自动减实例,不浪费资源。
  • 自愈:容器挂了自动重启,节点挂了自动把服务迁移到其他节点。
  • 服务发现与负载均衡:服务之间互相访问,不再需要手动记录 IP 和端口。
  • 发布与回滚:新版本上线后如果出问题,可以一键回退到旧版本。

这五项能力单独拿出来每一项都有开源方案可以做,但把它们组合在一起,并且做到大规模下依然稳定,这就是 Kubernetes 最核心的价值。

2.2 从单体到微服务的必然之路

我见过很多团队,一上来就拍脑袋说“我们要用 Kubernetes,因为大家都在用”,结果上了之后发现更累了。原因很简单:如果你们的服务本来就是单体架构,十几个模块打成一个大包,Kubernetes 带来的收益非常有限,反而引入了额外的复杂度。

Kubernetes 真正发挥价值的前提,是你的应用已经或者计划拆分成多个可以独立部署的组件。只有到了这个阶段,你才会遇到这些痛:

  • 服务数量多到脚本管不过来
  • 多个环境(开发、测试、预发、生产)配置同步靠“复制粘贴”
  • 发布一次系统,需要手动登录十台机器执行命令
  • 某个服务负载高,无法快速扩出多个实例
  • 线上故障后,无法快速定位到具体实例

如果你手头已经有这几个痛点,Kubernetes 就是对症的药。如果一条都不占,那我的建议是再等等,别为了技术而技术。

3. 核心架构:一张图拆解控制面与数据面

3.1 控制面:集群的“大脑”

Kubernetes 集群从逻辑上分成两个大的部分:控制面(Control Plane)和工作节点(Worker Nodes)。控制面负责做决策,工作节点负责干苦力。

控制面上面跑着五个核心组件:

  • kube-apiserver:整个集群的唯一入口。无论是用户、其他组件还是命令行工具 kubectl,所有操作请求都必须经过它。它相当于公司前台,任何人都得先到前台登记,才能进到内部办事。
  • etcd:集群所有数据的存储仓库。配置信息、服务状态、节点信息全部存在这里。它相当于公司档案室,数据的一致性至关重要,所以 etcd 本身是一个分布式的键值数据库,需要做集群化部署。
  • kube-controller-manager:负责维持集群的期望状态。比如你声明“我要 3 个副本”,而实际只有 2 个,控制器就会发现偏差,然后出手补上缺失的副本。它相当于质检员,时刻检查实际情况和计划是否一致。
  • kube-scheduler:负责为新创建的 Pod 选择一个合适的节点。它会综合节点资源余量、调度策略、亲和性规则,选出最优目标。它相当于人事部的排班员,知道谁适合上哪个班次。
  • cloud-controller-manager:这是和云厂商打交道的组件,如果你跑在自建机房,这个组件通常是空的。它负责对接负载均衡、存储卷等云资源。

3.2 工作节点:真正干活的机器

每个工作节点上都运行着三个必需的组件:

  • kubelet:节点上的管家,接收控制面下发的指令,负责启动容器、监控容器状态、向控制面汇报情况。它被要求保持忠诚可靠,绝不能擅自做决定。
  • kube-proxy:负责节点上的网络规则,把访问某个 Service 的流量转发到具体的 Pod 上。它相当于门卫,告诉来访者“你要找的人住在几号楼几层几室”。
  • 容器运行时:比如 containerd 或 CRI-O,负责真正地拉起容器进程。Docker 也可以作为运行时,但在新版 Kubernetes 中已经不推荐了。

3.3 我们常说的“对象”是什么

熟悉 Kubernetes 的人经常会说“一切皆对象”。这个说法听起来玄乎,其实很简单:所有你希望集群做的事,都可以通过声明式的 YAML 文件告诉 Kubernetes。

你写一个 YAML,描述“我要跑一个 Nginx,三个副本,暴露 80 端口”,然后提交给 API Server,剩下的工作全部由控制器完成。你不需要告诉它“先创建容器再创建网络再挂载存储”,你只需要描述最终状态,Kubernetes 自己想办法实现。

这个思路其实和 Linux 的哲学很像:一切都是文件,一切都是配置。声明式的好处是——你的基础设施状态是可审计的、可复现的,谁改了什么,改成了什么,全都有据可查。

4. 关键对象逐个拆解:Pod、Service、Deployment

4.1 Pod:最小调度单元

Pod 是 Kubernetes 里最小的部署单元,也是初学者最容易理解错的地方。很多人以为 Kubernetes 直接管理容器,其实不对,它管理的是 Pod。

一个 Pod 里可以有一个或多个容器,这些容器共享同一个网络命名空间和存储卷。最常见的场景是一个 Pod 里只跑一个主容器,但偶尔也会有一个“边车”容器,专门负责日志收集、流量代理等辅助任务。

举个例子:假设你有一个 Java 应用,它打印日志到本地文件,你可以让应用容器正常跑业务,再挂一个 Filebeat 容器在同一个 Pod 里,Filebeat 直接把应用日志读走发到 Elasticsearch。两个容器共享同一个文件目录,非常方便。

Pod 的另一个特点是它是“短命”的。它可能会因为节点故障、资源不足、探针失败而被销毁重建。记住这一点很重要,后面很多设计都会围绕“Pod 随时会挂”这个前提展开。

4.2 Service:稳定的访问入口

既然 Pod 随时会挂、随时会重建,而且重建后的 IP 会变化,那么其他服务该通过什么地址访问它呢?这就是 Service 的用途。

Service 给一组 Pod 提供一个稳定的虚拟 IP 和 DNS 名称。你只需要记住服务名,比如order-service,不管背后 Pod 的 IP 怎么变,你访问order-service永远能到正确的后端。

具体实现上,Service 通过标签选择器(Label Selector)来匹配一组 Pod。Kubernetes 会给 Service 创建一个 Endpoint 列表,里面是这个 Service 背后所有 Pod 的 IP 和端口。kube-proxy 负责把访问 Service 的流量负载均衡到这些 Endpoints 上。

4.3 Deployment:声明期望状态

Deployment 可能是你日常打交道最多的对象。它描述了应用的期望状态——镜像版本、副本数量、更新策略等。

举个例子,你要跑一个 Web 服务,期望 5 个副本,镜像版本是v1.2.3。Deployment 会确保集群里始终有 5 个副本在运行。当你升级镜像到v1.2.4的时候,Deployment 会采用滚动更新的方式,先起一个新副本,等它 Ready 了,再杀掉一个旧副本,直到全部替换完成。

如果新版本出了问题,你可以用一条命令回滚到之前正常的版本:

kubectl rollout undo deployment/web-server

这个能力在生产环境的价值不可估量,我亲眼见过因为数据库迁移失败导致新版本连环报错,结果十秒钟内回滚到旧版本,线上无感知。

4.4 其他常用对象

除上面三个之外,还有几个对象值得提前了解:

  • Namespace:用来做逻辑隔离,比如区分 dev、test、prod,或者区分不同团队。
  • ConfigMap / Secret:把配置和敏感信息从镜像里解耦出来,改配置不需要重新打镜像。
  • Ingress:七层负载均衡,通过域名或路径把外部流量转发到集群内部的 Service。
  • PersistentVolume / PersistentVolumeClaim:给 Pod 挂载持久化存储,比如数据库数据。

这些对象全部通过 YAML 定义,提交给 API Server 后,由对应的控制器负责实现。学习 Kubernetes 的过程,其实就是在学习和这些对象打交道的过程。

5. 一次完整的上线流程,理解集群的工作逻辑

5.1 从创建 Deployment 到 Pod 运行

假设我们用下面的 YAML 来部署一个简单的 Web 服务:

apiVersion: apps/v1 kind: Deployment metadata: name: web-server namespace: default spec: replicas: 3 selector: matchLabels: app: web-server template: metadata: labels: app: web-server spec: containers: - name: nginx image: nginx:1.26 ports: - containerPort: 80

执行kubectl apply -f deployment.yaml之后,集群内部会发生这些事:

  1. kubectl 把 YAML 内容发送给 API Server。
  2. API Server 校验无误后,把信息写入 etcd,并生成 Deployment 对象。
  3. Deployment Controller 发现期望副本数是 3,实际为 0,于是创建 3 个 ReplicaSet,每个 ReplicaSet 又创建对应的 Pod 定义。
  4. Scheduler 为每个 Pod 选择一个合适的节点,并把调度结果写回 API Server。
  5. 节点上的 kubelet 监听到有新的 Pod 分配给自己,就通知容器运行时拉取镜像、启动容器。
  6. kubelet 持续监控容器状态,通过探针判断容器是否健康。如果探针失败,就会触发重启策略。

整个链路听起来很长,但实际上从提交到三个 Pod 全部 Running,通常只需要十几秒到几十秒,取决于拉取镜像的速度。

5.2 对外暴露服务的两种方式

Pod 创建好了,外网怎么访问?根据场景不同,有两种常见做法。

第一种,如果你只需要集群内部访问,创建 ClusterIP 类型的 Service:

apiVersion: v1 kind: Service metadata: name: web-server-svc spec: selector: app: web-server ports: - protocol: TCP port: 80 targetPort: 80

这样集群内部其他服务可以通过http://web-server-svc访问。

第二种,如果要暴露到集群外部并支持域名访问,推荐使用 Ingress。先创建一个 Service,再创建一个 Ingress 规则,把域名web.example.com指向这个 Service。Ingress Controller 收到流量后,会按规则转发到对应的 Service,再由 Service 转发到具体的 Pod。

我曾经见过一些团队为了省事,直接把 Service 类型设为 NodePort,让外部流量通过高端口访问集群节点。这个方法在小规模场景下没问题,但生产环境基本不推荐,因为你需要手动维护端口分配、避免端口冲突,而且负载均衡能力非常有限。

5.3 发布升级和故障恢复时发生了什么

假设你要把 nginx 镜像从1.26升级到1.27,在更新 Deployment 后,它会先生成一个新的 ReplicaSet,然后逐渐创建新 Pod、销毁旧 Pod,全程服务不会中断。

如果某个新 Pod 因为镜像不存在而启动失败,Deployment 会暂停后续更新,保留旧版本继续服务。这种设计能保证你在升级过程中万一出问题,旧服务还能扛住流量。

再假设集群里某个节点突然宕机了。Kubernetes 默认会在五分钟左右判定该节点失联,然后把它上面的 Pod 调度到其他节点重新创建。这个时间窗口值可以调整,但默认值足够应对大多数情况。

6. 生产环境常用操作速查

6.1 高频命令整理

下面的命令是我在实际工作中每天都会用到的,整理成表格方便查阅。

操作目的命令
查看所有 Pod 状态kubectl get pods -A
查看某个 Pod 的详细信息kubectl describe pod <pod-name>
查看 Pod 日志kubectl logs -f <pod-name>
查看节点资源使用情况kubectl top nodes
进入 Pod 内的容器kubectl exec -it <pod-name> -- /bin/sh
创建或更新资源kubectl apply -f <file.yaml>
删除资源kubectl delete -f <file.yaml>
查看 Deployment 状态kubectl get deployment
向后回滚版本kubectl rollout undo deployment/<name>
调整副本数kubectl scale deployment/<name> --replicas=5

6.2 查看集群信息

# 查看集群节点列表 kubectl get nodes # 查看集群版本信息 kubectl version --short # 查看所有命名空间 kubectl get namespaces

如果是刚安装完集群,先跑这三条命令,确认控制面和节点都正常,再往下做其他操作。

6.3 初始化集群的关键参数说明

在安装阶段,最常用到的命令是kubeadm init,它会输出类似这样的信息:

[init] Using Kubernetes version: v1.26.0 [preflight] Running pre-flight checks

这些输出并不是废话,它们对应安装过程中的关键环节:

  • init阶段开始初始化控制面,拉取必要的镜像。
  • preflight阶段检查硬件、系统配置、端口占用、内核模块是否满足要求。如果哪一项不满足,会在这里直接报错并终止安装。

如果你看到 preflight 阶段报了错误,不要急着搜“怎么跳过检查”,先把问题原因搞清楚。大多数情况下,就是一些常规的系统设置没配好,比如 Swap 没有关闭、iptables规则缺失、容器运行时未配置完成。把这些基础问题解决掉,后面的步骤会顺畅很多。

7. 安装和初始化环境踩坑记录

7.1 系统要求和前置条件

无论你用的是云服务器还是自建物理机,安装 Kubernetes 都需要满足一些硬性条件。这些条件看起来琐碎,但每一项不过关都会导致后面出现问题。

  • 操作系统:Ubuntu 20.04+、CentOS 7.9+ 或兼容的 Linux 发行版。
  • 硬件:控制面节点至少 2 核 4GB 内存,工作节点至少 2 核 2GB。
  • Swap:必须关闭,否则 kubelet 无法正常工作。可以用swapoff -a临时关闭,并编辑/etc/fstab永久关闭。
  • 内核参数:需要配置net.bridge.bridge-nf-call-iptables等参数,以保证 iptables 规则对桥接流量生效。
  • 容器运行时:推荐安装 containerd,并在配置中启用 SystemdCgroup。
  • 端口:根据组件不同,需要开放 6443、2379、2380、10250、10259 等端口。

7.2 我踩过的三个坑

第一个坑是忘记关闭 Swap。有一次装好集群后,kubelet 一直处于 CrashLoopBackOff 状态,查看日志发现明确提示 “Running with swap on is not supported”,当时才想起来系统里还开着 Swap。关闭之后重启 kubelet,问题立刻解决。

第二个坑是镜像拉取超时。国内网络环境下,直接从官方源拉取k8s.gcr.io的镜像非常容易超时。后来我在初始化时给kubeadm init指定了一个国内可访问的镜像仓库地址,整个初始化速度从半小时缩短到两三分钟。

第三个坑是内存不足导致 Pod 被反复驱逐。我有一台 2GB 内存的节点,跑了几个 Java 服务之后,节点内存耗尽,Kubernetes 开始大量驱逐 Pod,导致服务全断。后来给节点配置了更完善的内存请求和限制,并为关键服务设置了高优先级,才彻底稳定下来。

7.3 托管集群可能是更香的选择

如果你所在的公司没有专职运维团队,或者不想自己维护 Kubernetes 的控制面,使用云厂商的托管集群服务是更省心的方案。自己搭建集群能让你学到更深的知识,但生产环境的选择永远是“能用有限的资源换取最大的可靠性”。

托管集群的区别是:控制面组件由云厂商替你维护,只需要自己管理工作节点的资源池。你还可以启用自动扩缩容节点,流量高峰自动加虚拟机,低谷自动回收。对于中小团队来说,这是性价比极高的方式。

8. 常见问题排查思路

8.1 Pod 一直 Pending 不运行

如果你的 Pod 长期处于 Pending 状态,先执行kubectl describe pod <pod-name>,查看事件部分。最常见的原因有三个:

  • 节点资源不足,Scheduler 找不到能满足规格要求的节点。
  • 有污点(Taint)阻止了 Pod 调度,且 Pod 没有对应的容忍(Toleration)。
  • 项目选择了不存在的节点标签。

排查的顺序很简单:先看有没有可用节点,再看节点剩余资源,再看节点是否有污点。

8.2 Pod 一直 CrashLoopBackOff

CrashLoopBackOff 的意思是容器启动后立刻崩溃,Kubernetes 尝试重启,但每次都失败,于是进入指数退避的状态。

这时最有效的排查方式是看日志:

kubectl logs <pod-name> --previous

--previous参数可以查看上一次崩溃容器的日志。常见的崩溃原因包括:启动命令写错、环境变量缺失、依赖的数据库连不上、配置文件路径不对。

这里有一个排查技巧:先审一遍 YAML 的 command 字段和启动参数,再确认 ConfigMap 是否注入成功,然后再判断是否外部依赖的问题。不要一上来就怀疑程序代码,很多时候是配置问题。

8.3 外网访问不通

如果服务在集群内访问正常,但从外部访问不了,第一步先确认你用的是 NodePort 还是 Ingress。如果是 NodePort,确认端口是否能通、防火墙是否放行;如果是 Ingress,检查 Ingress Controller 是否正常运行、域名解析是否指向正确、证书是否有效。

我还见过一个情况,Ingress 规则没有问题,但后端 Service 的selector写错了,导致 Endpoints 列表为空,流量进来后直接被丢弃。检查 Service 的 Endpoints 是最快定位问题的方法:

kubectl get endpoints

如果 Endpoints 为空,基本就是 Selector 没匹配上 Pod 的标签。

9. 学习路径建议

9.1 不要一开始就追求“高可用集群”

很多新手拿到教程,第一步就想搭一个三主三从的高可用集群,结果被证书、负载均衡、etcd 集群搞得头大,往往还没开始玩就放弃了。

我的建议是先搭单节点集群,把对象模型、调度逻辑、网络模型这些核心概念玩明白,再考虑加节点、做高可用。单节点集群和真实集群在对象使用上几乎没有区别,你只需要一台 4GB 内存的机器就够了。

9.2 按这个顺序学效率最高

  • 脚本:Docker 的基础操作,容器怎么跑、镜像怎么写。
  • Pod、Deployment、Service、Ingress 四个对象。
  • ConfigMap、Secret、Namespace 的日常用法。
  • 探针、资源请求与限制、滚动更新策略。
  • StatefulSet、PV/PVC、Operator。
  • 网络插件工作原理,比如 Calico、Flannel。
  • 可观测性体系,Prometheus、Loki、Tracing。

每到一个阶段,都手动把示例跑一遍,不要只看文档。尤其是探针和资源限制这两块,自己动手改参数、观察行为变化,比背十篇文档都管用。

9.3 遇到问题时的阵地

  • 官方文档永远是第一来源,不急躁,按目录逐节读。
  • 报错信息直接搜报错原文,尽量把日志贴全。
  • 动手调试时多用describe和logs,不要上来就重启资源。

Kubernetes 的学习曲线确实陡,但它值得投入时间。它带来的不是某一个具体的“银弹功能”,而是把基础设施变成了代码、变成了数据、变成了可以被审计和恢复的状态。这也是为什么它能在短短几年内成为云原生时代的通用语言——理解了它的核心模型,再去看任何云厂商的容器服务,都会觉得似曾相识。

最后说一句我自己的体会:学 Kubernetes 最难的其实不是技术本身,而是转变思维。你不再是登录一台服务器手动修问题,而是思考这个集群应该如何自我修复。当你习惯了“描述期望状态”而不是“下达操作指令”的时候,你能管理的不只是业务软件,还有整个基础设施的无限可能性。

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

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

立即咨询