1. 项目概述:Kubernetes 到底是什么
先说结论:Kubernetes(以下简称 K8s)是一个开源的容器编排平台,解决的问题是“怎么把一大堆容器稳定、高效、自动化地跑起来”。如果你只是单机跑几个 Docker 容器,K8s 确实用不上;但一旦你的服务数量超过几十个、需要滚动升级、自动扩缩容、跨机器调度、故障自愈,手写脚本管理就会迅速失控,这时候 K8s 几乎是事实标准。
从 v1.26.0 这个版本号也能看出一些趋势:K8s 的迭代节奏非常快,大约每三个月发布一个大版本,v1.26 属于 2022 年底到 2023 年初的版本,引入了不少关键特性。现在社区已经推进到更新的版本了,但核心架构和基础概念依然是相通的,学会 v1.26 的概念,换到新版本依然是同一套心智模型,只是功能更多、默认行为更稳。
这篇博文适合谁看?两种人:一是刚接触容器编排、想搞懂 K8s 到底在解决什么问题的后端或运维工程师;二是已经用过 Docker、但面对 K8s 一堆概念(Pod、Service、Deployment、Namespace)感到混乱的初学者。我会从“为什么要做容器编排”讲起,逐步拆解架构、核心对象、资源调度、排错思路,最后给出一套能直接上手的入门路径——包括初始化集群时kubeadm init输出里那一大串[preflight]检查到底在查什么。
2. 为什么需要 Kubernetes:从“手动管理容器”到“声明式编排”
2.1 容器时代的第一道坎:单机搞定不了的事
Docker 把应用和依赖一起打包成镜像,解决了“在我机器上能跑”的问题,但紧接着就撞上新的墙:一台物理机资源有限,CPU、内存、磁盘总有耗尽的一天。你当然可以多买几台机器,每台都手动docker run,可一旦容器数量多起来,麻烦就来了——
- 容器分布在哪些机器上,靠记忆?机器挂了,上面的容器谁负责重启?
- 流量进来,哪个容器处理?要不要做负载均衡?
- 发布新版本,怎么做到不停机?直接
docker stop再docker run用户会骂的。 - 高峰期想多开几个副本,低峰期想缩回来,手动操作显然跟不上节奏。
这些问题的本质是:容器的生命周期需要被“管理”,而不仅仅是被“创建”。K8s 就是为管理而生的一套控制体系。
用生活化的类比来说,如果 Docker 是“预制菜”——把菜洗好切好,装在统一的盒子里,方便运输和烹饪;那 Kubernetes 就是“中央厨房的自动化烹饪流水线”——它知道你有多少份食材(容器副本数)、哪个灶台空着(节点调度)、火候大了怎么办(自动扩缩容)、某个灶台坏了由哪个备用灶顶上(故障自愈)。你只需要告诉流水线“我要 3 份番茄炒蛋”,它自己安排一切。
2.2 声明式 API:你只需要描述“想要什么”
K8s 最核心的设计哲学,我愿称之为“声明式管理”。传统运维是命令式思维:先做什么、再做什么、遇到什么情况执行什么命令,全流程由你来设计。K8s 反过来——你只需要提交一份 YAML,声明“我要跑 3 个副本的 Nginx”,剩下的“怎么跑、跑在哪、挂了怎么拉起、流量怎么进去”,全部由控制平面自行决定。
这一点极其重要,它把运维人员从“写一堆 if-else 脚本”里解放出来,变成“描述终态”的人。你的 YAML 是“愿望清单”,K8s 的控制循环(Control Loop)则是那个不断检查“现实是否符合愿望,不符合就纠正”的强迫症机器人。这种模式在规模上去之后优势巨大,因为系统自动处理的东西越多,人能犯的错就越少。
我见过很多从传统运维转过来的同事,初期最大的别扭就在这:总想手动kubectl scale去扩容,而不是改 YAML 让 Deployment 去协调。实际上,手动命令确实能应急,但正式的业务变更应当全部走声明式配置,这样才能被审计、被版本管理、被 GitOps 流程控制。
3. 核心概念拆解:先把这些词搞明白,再谈别的
3.1 Pod:K8s 最小调度单元,一组亲密容器
很多人初学 K8s 会有一个惯性思维:K8s 管理的是容器吧?严格说,K8s 的最小调度单位是Pod,不是一个容器。Pod 里可以装一个容器,也可以装多个容器,这些容器共享同一个网络命名空间、共享存储卷、通过 localhost 互相通信。
为什么要有 Pod 这一层抽象?因为有的时候,几个容器必须部署在同一台机器上才算合理——比如一个容器负责读取日志文件,另一个容器负责把日志转发到远端存储。这两个容器生命周期应该绑定、网络必须互通、磁盘要共享,如果把它们拆成两个独立调度的容器,调度器可能把它们分到不同节点,整个设计就崩了。
Pod 还有一个重要特性:Pod 是“可牺牲”的。K8s 把 Pod 当作“一次性物体”,它随时可能因为节点故障、资源驱逐、手动删除而消失,而且消失后 IP 会变。所以 Pod 本身不适合被直接访问,这也是为什么我们通常不直接创建 Pod,而是通过 Deployment 等控制器来管理它们。
实操里一个经验:能用kubectl run直接创建一个裸 Pod 做调试没问题,但生产环境别这么干。裸 Pod 没有控制器管理,删了就是真的没了,不会被重建。
3.2 Deployment:声明副本数,剩下的交给控制器
Deployment 是 K8s 里最常用的工作负载控制器,用来管理无状态应用。你告诉它“我要 5 个副本”,它会创建 ReplicaSet,ReplicaSet 再负责创建 Pod 并保证副本数始终为 5。某个 Pod 被删了,控制循环会立刻创建一个新的顶上。
Deployment 的另一个核心能力是滚动更新(Rolling Update)。升级镜像版本时,它会分批替换旧 Pod,而不是一次性全部干掉。默认策略下,它会先起几个新 Pod,等它们 Ready 了再下线等量的旧 Pod,整个过程服务不中断。线上发布如果只允许停机窗口,你需要的是 Deployment + 滚动更新,这正是它存在的意义。
使用 Deployment 时的配置重点有三个:
- replicas:期望副本数,平时按负载评估,高峰期可以临时调整。
- strategy.rollingUpdate:滚动更新的参数,比如
maxUnavailable和maxSurge,控制更新过程中最多允许多少旧 Pod 不可用、最多允许多出多少新 Pod。 - template:Pod 模板,里面定义了镜像、端口、资源请求等。
我实测下来,maxUnavailable: 0, maxSurge: 1是保守且常用的组合——先起一个新 Pod,确认健康后再杀一个旧 Pod,牺牲一点更新速度换取稳定性。
3.3 Service:给“易变的 Pod”一个稳定的入口
上面说了 Pod 是易变的,IP 随时可能更换,那么问题来了:客户端怎么访问服务?
答案是Service。Service 是一组 Pod 的稳定访问入口,它本身拥有一个固定的虚拟 IP(ClusterIP),并且通过 Selector(标签选择器)动态匹配到一组 Pod 上。只要 Pod 上的标签和 Service 的 selector 匹配,这个 Pod 就会自动加入服务后端,不需要你手动改任何配置。
Service 的类型主要有四种:
- ClusterIP:集群内部虚拟 IP,仅供集群内访问,默认类型。
- NodePort:在每个节点上开放一个端口(30000-32767),外部流量可以通过
节点IP:端口访问。 - LoadBalancer:云平台会分配一个负载均衡 IP,流量先到 LB,再转发到节点上的 NodePort。
- Headless Service:不分配 ClusterIP,配合 StatefulSet 获取 Pod 的独立 DNS 记录,常用于有状态应用。
初学阶段很多人会在 Service 和 Ingress 之间犯迷糊。简单区分:Service 是四层负载均衡,负责“把流量转到 Pod”;Ingress 是七层路由,负责“根据域名或 URL 路径决定流量转给哪个 Service”。打个比方,Service 是总机接线员,你拨内部号码它会接通对应分机;Ingress 是前台,会根据你说“我要找技术部”把你带到对应区域。
3.4 Namespace:逻辑上的“虚拟集群”
Namespace 解决的是“多个团队/多个环境共用一套 K8s 集群”的隔离问题。它的隔离是逻辑层面的——不同 Namespace 里的资源互不可见(默认情况下),但底层还是同一批节点。
K8s 默认有三个 Namespace:default(无特殊声明时资源都在这里)、kube-system(运行系统组件)、kube-public(公共资源)。实践中,我建议按业务环境或团队划分:dev、test、prod各一个 Namespace,权限控制(RBAC)、资源配额(ResourceQuota)、网络策略都可以挂在 Namespace 级别上。
有个常见的坑:很多人刚用 K8s 时,看到kubectl get pod只显示几个 Pod 就开始怀疑人生,其实是忘了加-n 某个namespace参数。默认只会看default这一个命名空间,系统组件都在kube-system里。
4. 架构详解:控制平面和数据平面各司其职
4.1 控制平面组件:集群的大脑
一个标准的 K8s 集群由两部分组成:控制平面(Control Plane)和工作节点(Worker Node)。控制平面负责决策和状态维护,工作节点负责实际运行 Pod。下面挨个过一遍控制平面的核心组件:
- kube-apiserver:整个集群的“唯一入口”。所有请求(kubectl 命令、组件间通信)都必须经过它,它负责认证、授权、准入控制,然后才把数据写入 etcd。它是我眼里最重要的组件——所有组件都不直接访问 etcd,而是通过 API Server 间接读写。
- etcd:集群数据的“最终存储”。一个 key-value 数据库,存着所有对象的状态(期望状态和实际状态)。它是 K8s 的“真相来源”,如果 etcd 挂了,整个集群就瘫痪了。生产环境一定要给 etcd 做定期备份。
- kube-controller-manager:一个“控制器集合体”。节点控制器、副本控制器、端点控制器、服务账户控制器全在这里面,各自负责维持期望状态。
- cloud-controller-manager:云厂商相关的逻辑控制器,比如云负载均衡、云存储卷的创建和删除。自建集群通常不需要,云上托管的 K8s 才会用到。
- kube-scheduler:调度器。它决定“新创建出来的 Pod 应该落在哪个节点上”,依据是节点的资源余量、亲和性规则、污点容忍等条件。
如果用公司结构来类比:API Server 是前台秘书,所有申请都交到它手里;etcd 是档案室,记录整个公司所有员工的资料;controller-manager 是各部门经理,定期对照公司规章制度检查工作进度;scheduler 是人力资源调配主管,决定新人分到哪个部门。
4.2 工作节点组件:干活的工人
每个工作节点上有两类核心组件:
- kubelet:节点上的“监工”。它的职责是和 API Server 通信,确保本节点上的 Pod 都在运行状态。它会定期向 API Server 上报节点状态和资源使用情况,如果某个 Pod 的健康检查失败,它会负责杀掉容器并上报状态。
- kube-proxy:节点上的“网络转发员”。它负责维护 Service 对应的网络规则,让发往 Service 的流量能到达某个真正的 Pod。在 iptables 模式下,它通过创建 iptables 规则实现转发;在 IPVS 模式下,则利用 IPVS 模块,性能更好、负载均衡算法更多。
还需要说的一个组件是容器运行时(Container Runtime)。K8s 通过 CRI(Container Runtime Interface)和运行时通信,最常用的是 containerd 和 CRI-O,Docker 本身已经不适合直接作为 K8s 的运行时了(v1.24 起移除了 dockershim)。我看到不少老教程还在提 Docker 可以直接跑 K8s,其实新版本基本都是用 containerd,这点对新入门的人是个容易踩的坑。
4.3 Addons:锦上添花的生态组件
除了核心组件,一个功能完备的集群还需要一些插件(Addon):
- CNI 网络插件:负责集群网络的打通,常见有 Calico、Flannel、Cilium。没有 CNI,Pod 之间是无法通信的。
- CoreDNS:集群内 DNS 解析服务,让 Pod 可以通过服务名访问别的服务。
- Ingress Controller:配合 Ingress 资源工作的落地组件,常见是 Nginx Ingress Controller 或 Traefik。
- Metrics Server:提供节点和 Pod 的 CPU/内存监控数据,
kubectl top命令依赖它,还能为 HPA(水平自动扩缩容)提供数据来源。
从我个人的搭建经验来看,Calico + CoreDNS + Metrics Server是三件套标配,几乎每次初始化集群都要装,一个都不能少。
5. 集群搭建实战:kubeadm init 输出逐行解读
5.1 初始化前的准备:kubeadm 的前置检查
聊完概念,来点接地气的实操。假设你要用 kubeadm 搭建一套 K8s 集群(v1.26.0),执行kubeadm init后,控制台会先打印一大段[init]和[preflight]日志。很多人看到这堆输出会有点慌,其实它的信息量很大,值得逐段理解。
拿开头那两行热词来说:
[init] using kubernetes version: v1.26.0这行表示 kubeadm 将安装的 Kubernetes 版本是 v1.26.0,也意味着 kubeadm 和 kubelet 的版本必须与之兼容。
紧接着是:
[preflight] running pre-flight checks这是 kubeadm 在正式安装前做的一堆环境和配置检查,常见检查项包括:
- 内核参数:要求
net.bridge.bridge-nf-call-iptables为 1,否则网络转发可能有问题。 - swap 状态:默认要求关闭 swap。K8s 在设计上不支持在启用 swap 的节点上调度,关闭后 Kubelet 才会正常工作。
- 端口占用:检查 6443(API Server)、2379/2380(etcd)等端口是否被占用,有服务占用会直接失败。
- 容器运行时连接:kubeadm 会尝试通过 CRI 连接到 containerd,连接不上会报错。
- 资源充足性:检查 CPU、内存是否满足最低要求(至少 2 核 2G,生产建议更高)。
- modprobe 模块加载:需要加载
br_netfilter、overlay等内核模块,不然网络和存储层会出问题。
这些[preflight]检查是防患于未然的重要手段。我处理过好几个失败的初始化案例,十有八九都能在这阶段找到线索——比如 swap 没关、containerd socket 路径不对、内核参数没持久化。与其失败之后反复折腾,不如先把 preflight 阶段通过。
5.2 初始化成功后的三类关键信息
如果一切正常,kubeadm init会在最后输出几段关键信息,我看到很多新手会直接跳过。这里提个醒:这些信息一定要保存下来,尤其是前两条:
- kubeadm join 命令:包含 token 和 CA 证书哈希,后续所有工作节点加入集群都要用到。token 默认有效期 24 小时,过期了可以用
kubeadm token create重新生成。 - KUBECONFIG 配置路径:通常是
/etc/kubernetes/admin.conf,是集群管理员访问集群用的配置文件。你需要把它复制到~/.kube/config,否则kubectl命令连不上集群。 - 必须安装的 CNI 插件提示:初始化完成后集群处于
NotReady状态是正常的,直到装好 CNI 插件,节点才会转为Ready。
安装完 CNI 插件后,你可以用kubectl get nodes确认节点状态。如果状态还是NotReady,别急着怪 CNI,先看kubectl describe node,很多情况下是因为 kubelet 没启动、证书过期、或容器运行时配置有误。
5.3 单机测试环境的替代方案
如果你的本子只有 2 核 4G 资源,不想玩整套集群,我强烈建议先试试kind(Kubernetes in Docker)或minikube。kind 会把控制平面和工作节点都跑成 Docker 容器,适合快速测试 YAML、练手 kubectl 命令;minikube 则更适合体验“有单节点集群真实感”的开发场景。
我个人对初学者的建议是:先 minikube 或 kind 玩一周,把 Pod、Deployment、Service 的 YAML 写熟了,再上 kubeadm 搭真实集群。一步到位搭集群难度不大,但排错环境复杂,容易把信心磨没了。先小后大,学习曲线会平滑很多。
6. 常见问题与排查技巧实录
6.1 Pod 一直 Pending 或 CrashLoopBackOff?
这两个状态是新手遇到最多的。Pending一般意味着调度失败,排查方向是:
- 节点资源是否不足:
kubectl describe node查看 Allocatable 和 Requests。 - 是否存在污点不匹配:
kubectl describe node查看 Taints,Pod 需要对应容忍。 - 是否有 PVC 绑定超时:
kubectl describe pod看事件。
CrashLoopBackOff表示容器启动后反复崩溃。先看日志:kubectl logs <pod-name>,如果是上一个容器已崩溃,加上--previous。常见的崩溃原因有镜像启动命令报错、配置文件挂载路径不对、探针(LivenessProbe)配置太激进导致容器反复被杀。
我见过一个相当隐蔽的问题:健康检查路径写错,/healthz写成了/health,探针一直失败,Pod 反复重启,日志里又找不到错误,因为应用本身是正常启动的。遇到这种“看起来没问题但就是重启”的情况,先把探针停了试试,基本能定位。
6.2 kubectl 报 connection refused 怎么办?
kubectl无法连接集群,先检查自己的 kubeconfig 是否正确。kubectl config view看当前上下文,确认 server 地址和证书有效。本地 kind 集群经常发生集群重建后 kubeconfig 未更新的问题,重新执行kind export kubeconfig即可。
如果是远程 API Server 连不上,可能是防火墙没放行 6443 端口。我在一台云服务器上搭 K8s 时,折腾了半天发现安全组规则只开放了 22 端口,加一条 TCP 6443 入站规则就好了。这类“环境级”问题,检查顺序应该是:网络连通性 -> 防火墙/安全组 -> 证书有效性 -> kube-apiserver 服务状态。
6.3 节点重新加入集群时报错?
kubeadm join失败,大概率是 token 过期或 CA 哈希不匹配。重新生成即可:
kubeadm token create --print-join-command这条命令会直接打印完整的 join 命令,复制执行就行。还有种情况是节点之前已加入过集群,/etc/kubernetes下有残留配置,需要先执行kubeadm reset清理干净再 join。
6.4 常用排查命令速查表
| 场景 | 命令 | 说明 |
|---|---|---|
| 查看所有命名空间的 Pod | kubectl get pods -A | 只敲kubectl get pods会默认只显示 default 命名空间 |
| 看 Pod 的详细事件 | kubectl describe pod <name> | 事件尾部往往藏着失败原因 |
| 看容器日志 | kubectl logs <name> | 单容器 Pod 可以直接用,多容器要加-c <container> |
| 监听资源变化 | kubectl get pods -w | watch 模式,实时刷新状态变更 |
| 把 YAML 渲染成可提交的配置 | kubectl create deployment demo --image=nginx --dry-run=client -o yaml | 不实际创建,只输出 YAML 样板 |
| 故障节点清出集群 | kubectl drain <node> --ignore-daemonsets | 会把节点上的 Pod 驱逐到其他节点 |
| 节点恢复后重新可调度 | kubectl uncordon <node> | 先把节点置为可调度,Pod 才会重新被调度上来 |
我通常的做法是“先 describe 后 logs”:遇到异常 Pod,先describe看事件和状态,再logs看应用日志。很多人一上来直接看日志,容易忽略调度失败、镜像拉取失败这类基础设施层面的原因。
注意:
kubectl drain是运维高危操作,执行前确认节点上的业务 Pod 已由 Deployment/StatefulSet 管理,否则被驱逐的裸 Pod 不会自动重建。
7. 实际业务中怎么选型:什么样的场景该上 K8s
7.1 适合上 K8s 的三个信号
我经常被问到“我们该不该上 K8s”。这个问题没有标准答案,但有三个很明确的信号可以参考:
- 服务数量多且有相互调用关系:比如微服务架构里 20 个以上服务,服务间通过 DNS 互相发现,用 K8s 的 Service 和 CoreDNS 天然合适。
- 流量波动剧烈,需要自动扩缩容:K8s 的 HPA 可以根据 CPU/内存或自定义指标自动调整副本数,高峰期扩、低峰期缩,比人工盯监控靠谱得多。
- 已有容器化基础,但部署靠手动脚本:说明你已经走到“容器化之后但还没自动化”的阶段。把一堆
docker run脚本改成 Deployment YAML,是收益最高的一步。
反之,如果你的业务只有两三个服务、流量平稳、可以接受深夜停机发布,那 K8s 的复杂度就是纯成本。技术选型最怕跟风,K8s 再流行,它在 5 个服务的小团队里也是杀鸡用牛刀。
7.2 按环境取舍功能模块
就算决定上 K8s,也不用一把梭全上。按我的经验,不同阶段的功能取舍是这样的:
| 阶段 | 核心功能 | 暂时放一放 |
|---|---|---|
| 第一阶段:容器编排入门 | Deployment、Service、Namespace、kubectl 基础 | Ingress、HPA、StatefulSet |
| 第二阶段:稳定性和自动化 | HPA、滚动更新策略、资源 requests/limits、Pod 探针 | Operator、自定义控制器 |
| 第三阶段:复杂业务治理 | Ingress + 证书管理、NetworkPolicy、RBAC、服务网格 | 多集群、联邦管理 |
这样逐级深入的好处是,每一层都能真正吃透再往下一层走,而不是被 K8s 庞大的生态吓退。
8. 新手入门路线图:一份可以直接照着走的计划
8.1 阶段一:概念建立期(第 1 周)
目标不是搭建集群,而是把概念框架建立起来。我建议的顺序是:
- 先读 Docker 基础,保证对“镜像、容器、卷”有直观理解。
- 再学 Pod、Deployment、Service 三个核心对象,用 minikube 或 kind 反复创建、修改、删除。
- 用
kubectl explain查看每个资源的字段说明,比翻文档快得多。
比如kubectl explain deployment.spec.replicas会直接告诉你这个字段的含义和类型,这个命令在日常工作中也特别常用,遇到不熟悉的资源字段,先explain一下再填值,能少踩很多 YAML 校验的坑。
8.2 阶段二:集群实操期(第 2-3 周)
这一阶段建议用 kubeadm 搭一套三节点集群(1 控制平面 + 2 工作节点),真实体验一次集群初始化和节点加入的过程。搭完后做三件事:
- 部署一个有状态 MySQL,体会 StatefulSet、PersistentVolumeClaim、Headless Service 是怎么配合工作的。
- 配置 Ingress Controller,把服务暴露到集群外,理解域名解析到 Service 的完整链路。
- 部署 Metrics Server,配置 HPA,压测一个应用,观察自动扩缩容行为。
这三件事做完,你对 K8s 的认知会从“纸面概念”变成“肌肉记忆”。
8.3 阶段三:原理深挖期(第 4 周起)
当你已经能熟练部署应用后,建议深入三个方向:
- 网络模型:CNI 插件到底怎么实现 Pod 间通信的?service 的 iptables/IPVS 规则长什么样?这对排查复杂网络问题极其重要。
- 调度机制:nodeSelector、亲和性、污点与容忍怎么组合使用?为什么有些 Pod 会调度到你不期望的节点?
- 控制器模式:自己写一个简单 Operator,理解“观察、对比、纠正”的控制器循环,这是理解 K8s 生态一切复杂功能的钥匙。
这三个方向是 K8s 进阶的分水岭,能啃下来,你在团队里基本就是 K8s 方向的“专家”了。
我个人在实际操作中最深的体会是:K8s 入门不难,难的是在复杂场景下建立正确的直觉。很多问题表面上千奇百怪,但底层都是“期望状态和实际状态不一致”导致的。遇到任何诡异现象,先问一句:控制循环卡在哪一步?搞清楚这个问题,九十以上的问题都能迎刃而解。
最后分享一个小技巧:没事多敲kubectl get events --sort-by='.lastTimestamp' -A,看看集群里最近发生的所有事件。很多潜在问题在爆发之前,都会先在事件里不经意的露个脸。养成了看事件的习惯,你就能比监控报警更早发现异常,这大概是运维 K8s 最实用的一项软技能。