课程目录(第 1 章 / 共 33 章)
为什么需要 Kubernetes
从「一台服务器跑一个程序」讲到容器编排,用最少的术语说清 Kubernetes 到底替你解决了什么问题。
学完这一章,你将能够
- ✓说得出容器解决了什么问题、又留下了什么新问题
- ✓用一句话向别人解释 Kubernetes 的作用
- ✓理解「声明式」和「控制器」这两个贯穿全课程的核心词
从一台服务器说起
假设你写了一个网站,要让它 24 小时在线。最开始的做法很朴素:租一台服务器,把程序跑在上面。
用户 → 服务器(跑着你的程序)单机时代有两个绕不过去的问题:
- 程序之间会打架:两个应用依赖不同版本的 Python、不同的系统库,装在一起就互相破坏。
- 机器会挂:硬盘坏了、内存吃满被系统杀掉,服务就断了,而你往往在用户投诉后才知道。
于是有人发明了虚拟机:一台物理机切成多台,各自装各自的系统。隔离问题解决了,但每台虚拟机都要装一个完整的操作系统,启动慢、占用大。
容器解决了什么,没解决什么
容器把「应用 + 依赖」打包成一个镜像,共享宿主机的内核,启动只要几百毫秒。用一条命令就能跑起来:
docker run -d -p 8080:80 --name web nginx:1.27这一步之后,你的应用有了一个可复制的标准件。但把它放到生产环境,你马上会撞上一堆新问题:
| 问题 | 具体表现 |
|---|---|
| 谁来启动它 | 机器重启后,容器不会自己起来 |
| 挂掉怎么办 | 进程崩溃了,没人帮你重新拉起来 |
| 流量变大了 | 你要手动找机器、手动 docker run、手动改负载均衡 |
| 机器不够了 | 你得决定这个容器该放到哪台机器上 |
| 升级怎么做 | 停旧的、起新的,中间用户会看到 502 |
| 配置和密钥 | 每个环境的数据库地址不同,怎么注入 |
| 出问题看哪里 | 容器日志散落在不同机器上 |
单靠 docker run,这些都得靠人肉运维脚本。Kubernetes 就是为了接管这一层工作而生的。
Kubernetes 到底做了什么(一句话)
你把「期望的状态」写下来交给它,它负责让现实世界不断向这个状态靠拢。
比如你说「我要 3 个 nginx 副本,每个都要健康」,那么:
- 某个 Pod 挂了,它重新拉一个起来;
- 某台机器断电了,它把上面的 Pod 调到别的机器;
- 你把 3 改成 5,它自己去起 2 个新的;
- 你换了镜像版本,它会一个一个替换,保证服务不中断。
这句话里藏着两个贯穿全课程的关键词。
声明式(Declarative)
你不写「第一步做什么、第二步做什么」,而是写「最终要变成什么样」:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3 # 我期望有 3 个副本
template:
spec:
containers:
- name: web
image: nginx:1.27剩下的「怎么达成」由 Kubernetes 负责。这和 docker run 的命令式思路完全不同,也是新手最容易不适应的地方:别问「怎么操作」,要问「我想让它是什么样」。
控制器(Controller)
Kubernetes 内部有一批「控制器」,每个控制器只做一件事:盯着某类资源,发现现实和期望不一致就动手修正。Deployment 控制器管副本数,ReplicaSet 控制器管 Pod 数量,Node 控制器管节点健康……
理解了这个模式,后面学的每一个资源(Deployment、Service、Ingress、Job)你都能问同一个问题:它是谁在盯着?期望状态写在哪里?
不用 Kubernetes 也能理解的对照
同样一件事,两种做法对比一下就很清楚了。
| 目标 | 传统做法 | Kubernetes 做法 |
|---|---|---|
| 跑 3 个实例 | 手动在 3 台机器上 docker run | replicas: 3 |
| 进程挂了自动重启 | 写 supervisor / systemd | 由 kubelet 与控制器负责 |
| 机器故障迁移 | 人工发现、人工重部署 | 自动重新调度 |
| 发布新版本 | 手动滚动替换 | kubectl set image,自动滚动更新 |
| 扩容到 10 个 | 手动开机器 | kubectl scale --replicas=10 |
| 服务发现 | 维护一份机器 IP 列表 | Service + DNS,自动感知 Pod 变化 |
Kubernetes 不解决什么
前面讲的都是它能替你做什么,边界同样重要。把 Kubernetes 当成「万能运维机器人」是新手最容易失望的地方:它只对「你声明的容器期望状态」负责,其它事情仍然是你的责任。
| 它不负责 | 你仍然要自己做 |
|---|---|
| 业务代码正确性 | 写代码、修 bug、做压测 |
| 数据一致性与备份 | 数据库主从、事务、定期备份 |
| 镜像怎么构建 | 写 Dockerfile、跑 CI 产出镜像 |
| 集群外的网络与域名 | 公网负载均衡、DNS 解析、CDN |
| 应用层安全 | 镜像扫描、密钥轮转、最小权限 |
| 成本与容量规划 | 节点规格、伸缩策略、清理闲置资源 |
换句话说,它把「容器怎么可靠地跑起来」这一层标准化了,但它不替你决定应用长什么样,也不替你负责数据。
一个常见误解
「上了 Kubernetes 就不会宕机」是错的。它只能保证你声明的东西被持续纠偏,不会阻止你把内存上限设得太小、不会阻止你把数据库跑成单副本、也不会自动发现慢查询。可靠性来自架构设计,Kubernetes 只是其中一个环节。
一套应用在 Kubernetes 里的样子
先建立一个整体印象,后面每一章都会拆开讲其中一块。假设你要部署一个「前端 + API + 数据库」的应用,在 Kubernetes 里的典型构成是:
用户
│
▼
Ingress(域名与路由规则)
│
▼
Service(稳定的虚拟 IP + DNS)
│
▼
Deployment(管理多个 Pod 副本)
│
▼
Pod(真正运行容器的最小单元)
├── ConfigMap / Secret(配置)
└── PVC(持久化存储)每一层对应一站,按这个顺序学下去:
| 层次 | 它解决什么问题 | 对应章节 |
|---|---|---|
| Ingress | 把外部域名的请求按路径转发进集群 | 第 8 章 Ingress |
| Service | 给一组 Pod 一个稳定的访问入口 | 第 7 章 Service |
| Deployment | 保证副本数、滚动更新与回滚 | 第 6 章 Deployment |
| Pod | 真正运行容器的最小调度单元 | 第 4 章 第一个 Pod、第 5 章 用 YAML 描述 Pod |
| ConfigMap / Secret | 把配置和密钥从镜像里拿出来 | 第 9 章 ConfigMap 与 Secret |
| PVC | 让数据活过 Pod 重启 | 第 10 章 存储卷与 PV/PVC |
横切在所有资源之上的还有:命名空间与权限(第 12 章 命名空间与 RBAC)、健康探针与资源配额(第 11 章 探针与资源管理)、排障手段(第 15 章 排障手册)。
动手感受一下「没有人帮你重启」是什么体验
这一章不需要集群,只需要 Docker。我们要亲手制造一次「容器挂了没人管」的场景,这样你才会真正理解 Kubernetes 的价值。
动手练习:手动当一次「人肉控制器」
第一步,跑一个容器,记下它的 ID:
docker run -d --name web nginx:1.27
docker ps --filter name=web第二步,进容器把 nginx 的主进程杀掉,模拟「应用崩溃」:
docker exec web nginx -s stop
docker ps -a --filter name=web你会看到 STATUS 变成 Exited——容器退出了,而且没有任何人把它拉起来。
第三步,手动扮演 Kubernetes 控制器要做的事:
docker start web
docker ps --filter name=web现在停下来想三个问题:如果这台机器上有 30 个容器呢?如果是凌晨三点崩溃呢?如果你想让这个「检查并重启」的动作自动化,你需要写多少脚本?
第四步,清理:
docker rm -f web这段练习就是 Kubernetes 存在的全部理由:它把第三步那个动作,变成了集群默认行为。
怎么学才不迷路
新手最容易掉的坑是按「kubectl 命令大全」来学,结果记住了一堆命令但串不起来。建议按这个顺序:
- 先建立心智模型:知道每个资源解决什么问题(本模块 + 第 2 章 集群架构)。
- 再动手跑起来:用 第 3 章 把本地集群装好,之后每章都跟着敲。
- 永远用 YAML 而不是命令:
kubectl run只是临时试验,真正的工作方式是kubectl apply -f。 - 出问题先 describe 再 logs:第 15 章 排障手册 会给你一套固定的排查顺序。
一句话记忆法
Pod 是「跑起来的进程」,Deployment 是「保证一直有这么多进程」,Service 是「给这群进程一个固定入口」,Ingress 是「给入口配个域名和路径」。学每一章时,都把它挂到这句话上。
常见坑:这一章最容易想错的地方
这一章还没有集群,踩的坑都在认知上。下面这张表是新手最常见的几种误解,以及怎么自己确认。
| 现象 | 原因 | 怎么确认 | 怎么办 |
|---|---|---|---|
以为它会自动重启手工 docker run 的容器 | 它只托管自己创建、且有控制器盯着的 Pod | docker ps 里那个容器退出后,kubectl get pods 里根本找不到它 | 用工作负载对象创建 Pod,别手工管容器 |
把 docker run 当成 Kubernetes 的做法 | 两者思路不同:命令式 vs 声明式 | 问自己「我写的是步骤还是目标状态」 | 一律写 YAML 再 kubectl apply -f |
| 以为裸 Pod 挂了会自己回来 | 裸 Pod 没有控制器盯着,期望里只有「这个对象存在」 | kubectl delete pod 之后不会再出现同名 Pod | 用 第 6 章 Deployment 管理 Pod |
| 以为上了集群就不用管数据 | 它不负责存储的一致性与备份 | 看你的数据库几个副本、备份放在哪 | 存储与备份单独设计,见 第 10 章 存储卷与 PV/PVC |
| 以为「声明式」就是瞬间生效 | 控制器只做纠偏,达成期望需要时间 | kubectl get pods -w 能看到中间的过渡状态 | 接受最终一致,别指望秒级同步 |
自测题
自测:为什么删掉一个裸 Pod 之后,它不会自己回来?(点击展开答案)
因为「会不会回来」取决于有没有人持有它的期望状态。裸 Pod 的期望就是「这个对象存在」,你删除后对象从 etcd 里消失,没有任何控制器认为这是异常,所以不会重建。
而 Deployment 并不直接持有 Pod:它声明 replicas: 3,由 ReplicaSet 控制器不断比较「现有 Pod 数」和「期望 3」,一旦你删掉一个,实际数变成 2,控制器就创建新的补上。自动恢复来自控制器,而不是来自 Pod 本身。
自测:同样是「起 3 个 nginx」,为什么声明式比写脚本更抗故障?(点击展开答案)
命令式脚本只在执行的那一刻起作用:脚本跑完了,现实再怎么偏离也没人管。声明式则把「期望」持久化到 etcd 里,控制器通过 watch 持续观察实际状态,一旦不一致就动手修正。
所以机器重启、进程崩溃、你手动删了对象,最终都会重新向期望收敛。这也是为什么在 Kubernetes 里改期望(改 YAML)比改现状(删 Pod)更有效。
自测:既然控制器会纠偏,为什么还会出现故障?(点击展开答案)
因为纠偏是「事后」的,而且只针对你声明的部分。控制器先看到实际偏离,再采取动作,中间必然有时间窗口;更重要的是,它不会质疑你的期望是否合理——你把内存 limit 设得比应用真实需求小,它会忠实地让容器反复被杀。
一句话:控制器保证「现实向期望靠拢」,不保证「期望本身是对的」。
小结
- 容器解决的是「打包与隔离」,Kubernetes 解决的是「可靠地运行与调度」。
- Kubernetes 的核心思路是声明式:写期望状态,由控制器不断把现实拉回期望。
- 一个典型应用由 Ingress → Service → Deployment → Pod 逐层承载,旁边挂着配置、存储、权限与健康检查。
- 它不解决业务正确性、数据一致性、镜像构建与安全加固,边界要提前想清楚。
相关章节:第 2 章 集群架构 讲清这套机制跑在哪些组件上;第 6 章 Deployment 是「控制器」最典型的落地例子。
练习
- 用一句话向同事解释:为什么有了 Docker 还需要 Kubernetes?
- 翻到本站的速查表,找出「查看节点」和「查看所有命名空间的 Pod」两条命令,先混个眼熟。
- 想一下你手头最熟悉的一个服务,如果把它搬到 Kubernetes,需要哪些资源?(不用真的写,列出名字即可,学完 第 5 章 用 YAML 描述 Pod 再回来对照。)
下一章我们会把集群拆开看:控制平面和节点上分别跑着什么,为什么一个集群至少需要哪些组件。