课程目录(第 1 章 / 共 33 章)
课程/认识 Kubernetes

为什么需要 Kubernetes

1 章 / 共 33·12 分钟·入门概念容器编排

从「一台服务器跑一个程序」讲到容器编排,用最少的术语说清 Kubernetes 到底替你解决了什么问题。

学完这一章,你将能够

  • 说得出容器解决了什么问题、又留下了什么新问题
  • 用一句话向别人解释 Kubernetes 的作用
  • 理解「声明式」和「控制器」这两个贯穿全课程的核心词

从一台服务器说起

假设你写了一个网站,要让它 24 小时在线。最开始的做法很朴素:租一台服务器,把程序跑在上面。

text
用户 → 服务器(跑着你的程序)

单机时代有两个绕不过去的问题:

  • 程序之间会打架:两个应用依赖不同版本的 Python、不同的系统库,装在一起就互相破坏。
  • 机器会挂:硬盘坏了、内存吃满被系统杀掉,服务就断了,而你往往在用户投诉后才知道。

于是有人发明了虚拟机:一台物理机切成多台,各自装各自的系统。隔离问题解决了,但每台虚拟机都要装一个完整的操作系统,启动慢、占用大。

容器解决了什么,没解决什么

容器把「应用 + 依赖」打包成一个镜像,共享宿主机的内核,启动只要几百毫秒。用一条命令就能跑起来:

bash
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)

你不写「第一步做什么、第二步做什么」,而是写「最终要变成什么样」:

yaml
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 runreplicas: 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 里的典型构成是:

text
用户


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:

bash
docker run -d --name web nginx:1.27
docker ps --filter name=web

第二步,进容器把 nginx 的主进程杀掉,模拟「应用崩溃」:

bash
docker exec web nginx -s stop
docker ps -a --filter name=web

你会看到 STATUS 变成 Exited——容器退出了,而且没有任何人把它拉起来

第三步,手动扮演 Kubernetes 控制器要做的事:

bash
docker start web
docker ps --filter name=web

现在停下来想三个问题:如果这台机器上有 30 个容器呢?如果是凌晨三点崩溃呢?如果你想让这个「检查并重启」的动作自动化,你需要写多少脚本?

第四步,清理:

bash
docker rm -f web

这段练习就是 Kubernetes 存在的全部理由:它把第三步那个动作,变成了集群默认行为。

怎么学才不迷路

新手最容易掉的坑是按「kubectl 命令大全」来学,结果记住了一堆命令但串不起来。建议按这个顺序:

  1. 先建立心智模型:知道每个资源解决什么问题(本模块 + 第 2 章 集群架构)。
  2. 再动手跑起来:用 第 3 章 把本地集群装好,之后每章都跟着敲。
  3. 永远用 YAML 而不是命令kubectl run 只是临时试验,真正的工作方式是 kubectl apply -f
  4. 出问题先 describe 再 logs第 15 章 排障手册 会给你一套固定的排查顺序。

一句话记忆法

Pod 是「跑起来的进程」,Deployment 是「保证一直有这么多进程」,Service 是「给这群进程一个固定入口」,Ingress 是「给入口配个域名和路径」。学每一章时,都把它挂到这句话上。

常见坑:这一章最容易想错的地方

这一章还没有集群,踩的坑都在认知上。下面这张表是新手最常见的几种误解,以及怎么自己确认。

现象原因怎么确认怎么办
以为它会自动重启手工 docker run 的容器它只托管自己创建、且有控制器盯着的 Poddocker 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 是「控制器」最典型的落地例子。

练习

  1. 用一句话向同事解释:为什么有了 Docker 还需要 Kubernetes?
  2. 翻到本站的速查表,找出「查看节点」和「查看所有命名空间的 Pod」两条命令,先混个眼熟。
  3. 想一下你手头最熟悉的一个服务,如果把它搬到 Kubernetes,需要哪些资源?(不用真的写,列出名字即可,学完 第 5 章 用 YAML 描述 Pod 再回来对照。)

下一章我们会把集群拆开看:控制平面和节点上分别跑着什么,为什么一个集群至少需要哪些组件。