为什么需要容器编排
当你只有一个 Docker 容器时,docker run 就够了。但当你的系统由数十个微服务组成、需要在数百台服务器上运行时,问题就变得复杂:如何保证服务高可用?容器崩溃后谁来重启?流量如何分发?配置如何统一管理?这些问题催生了容器编排系统。
没有编排时的痛点
- 手动 SSH 到每台机器部署
- 容器崩溃后需人工干预
- 无法自动伸缩应对流量高峰
- 服务发现靠硬编码 IP
- 滚动更新复杂且容易出错
- 资源浪费,无法统一调度
Kubernetes 解决了什么
- 声明期望状态,自动收敛
- Pod 失败自动重启迁移
- HPA 基于指标自动扩缩容
- 内置 DNS 服务发现
- 内置滚动更新与回滚
- Bin-packing 优化资源利用
Google Borg:K8s 的精神前身
Kubernetes 并非凭空而来。它的设计深受 Google 内部使用了十余年的集群管理系统 Borg 的启发。Borg 在 Google 内部管理着数十万台机器上的作业调度,积累了大量实战经验。
Google 集群管理演进 ┌─────────────────────────────────────────────────────────┐ │ Google Borg (2003) │ │ · 管理 Google 全部在线/离线作业 │ │ · BorgMaster(控制平面) + Borglet(节点代理) │ │ · 引入 alloc(资源分配单元,Pod 的前身) │ └─────────────────┬───────────────────────────────────────┘ │ 开源 + 重新设计 ┌─────────────────▼───────────────────────────────────────┐ │ Kubernetes 1.0 (2014 开源) │ │ · Pod = alloc + 容器 │ │ · 声明式 API (Borg 是命令式配置文件) │ │ · 开放生态,CRD/Operator 扩展 │ └─────────────────────────────────────────────────────────┘
声明式 API vs 命令式 API
这是 Kubernetes 最核心的设计思想之一。声明式(Declarative)意味着你只告诉系统"我想要什么",而不是"如何去做"。Kubernetes 会持续对比当前状态与期望状态,并自动执行必要的操作来使两者一致。
命令式(以 Docker 为例)
# 告诉系统"如何做"
docker run -d --name web nginx
docker stop web
docker rm web
docker run -d --name web nginx:1.25
# 容器崩溃 → 你不知道 → 服务中断
声明式(Kubernetes)
# 告诉系统"想要什么"
spec:
replicas: 3
image: nginx:1.25
# K8s 自动保证 3 个副本运行
# Pod 崩溃 → K8s 自动重建
同一份 YAML 文件,无论 kubectl apply 执行多少次,最终结果都相同。这使得 GitOps 成为可能——将 YAML 存入 Git,任何时候都能重现集群状态。
控制循环(Control Loop)原理
Kubernetes 的自愈能力来自于遍布整个系统的控制循环(也叫 Reconciliation Loop,调和循环)。每个 Controller 都在不断地执行同一个逻辑:
控制循环示意 ┌──────────────────────────┐ │ 期望状态 (Desired) │ │ replicas: 3, nginx:1.25 │ └───────────────┬──────────┘ │ ┌──────────────▼──────────────┐ │ Controller │ │ ① 观察当前状态 (Observe) │ │ ② 计算差异 (Diff) │◄──┐ │ ③ 执行操作 (Act) │ │ 循环 └──────────────┬──────────────┘ │ │ └──┘ ┌──────────────▼──────────────┐ │ 当前状态 (Actual) │ │ 当前 Pod 数量、健康状态等 │ └─────────────────────────────┘
以 Deployment Controller 为例:
- 观察:从 etcd 读取 Deployment 的
spec.replicas=3,以及当前实际运行的 Pod 数量 - 差异:当前只有 2 个 Pod(有一个崩溃了),差距为 1
- 操作:调用 API Server 创建 1 个新 Pod
- 返回第 1 步,持续循环
K8s 与 Docker Compose 的区别
很多人入门 K8s 时都会问:Docker Compose 不是已经能编排多个容器了吗?为什么还需要 K8s?
| 对比维度 | Docker Compose | Kubernetes |
|---|---|---|
| 运行范围 | 单台机器 | 多节点集群(可达数千节点) |
| 高可用 | 无,单点故障 | 节点故障自动迁移 Pod |
| 自动扩缩容 | 手动 scale | HPA 基于 CPU/内存/自定义指标 |
| 滚动更新 | 有中断 | 零停机滚动更新 |
| 服务发现 | 容器名 DNS(限单机) | 跨节点 Service DNS |
| 存储 | 本地 Volume | PV/PVC 对接云存储/NFS/CSI |
| 适用场景 | 本地开发、小规模单机 | 生产级、多节点、微服务 |
本地开发仍然推荐使用 Docker Compose,快速简单。将 K8s 部署在测试/生产环境。可以使用 Kompose 工具将 docker-compose.yml 转换为 K8s YAML 文件作为起点。
第一个 Kubernetes YAML
让我们看一个完整的 YAML 文件,感受声明式 API 的形态:
apiVersion: apps/v1 # API 版本,决定该资源类型的 schema
kind: Deployment # 资源类型
metadata:
name: hello-k8s # 资源名称,集群内唯一(同 namespace)
namespace: default # 所属命名空间
labels: # 标签,用于选择器筛选
app: hello-k8s
spec: # 期望状态(最重要的部分)
replicas: 3 # 期望运行 3 个 Pod 副本
selector:
matchLabels:
app: hello-k8s # Deployment 通过此标签关联 Pod
template: # Pod 模板
metadata:
labels:
app: hello-k8s # Pod 携带的标签,必须匹配 selector
spec:
containers:
- name: web # 容器名,Pod 内唯一
image: nginx:1.25 # 容器镜像
ports:
- containerPort: 80 # 容器监听端口(文档性质,不影响网络)
# 应用此 YAML(创建或更新)
kubectl apply -f hello-k8s.yaml
# 查看 Deployment 状态
kubectl get deployment hello-k8s
# 查看自动创建的 Pod
kubectl get pods -l app=hello-k8s
# 删除资源
kubectl delete -f hello-k8s.yaml
不同资源类型使用不同的 apiVersion。Deployment 用 apps/v1,Pod 用 v1,Ingress 用 networking.k8s.io/v1。写错会导致 no matches for kind 错误。可用 kubectl api-resources 查询所有资源类型及其 apiVersion。
核心术语速查
K8s 引入了大量概念和运维复杂度。小团队或单机服务请评估是否真的需要 K8s。如果你只有 2-3 个服务,Docker Compose + 单台 VPS 可能是更好的选择。K8s 的收益在团队规模较大、服务数量较多(10+ 微服务)时才能真正体现。