前言
学 Kubernetes 的第一步,我觉得不是先背一堆命令,也不是马上搭集群,而是先把几个核心对象之间的关系想清楚。
很多人刚开始接触 k8s,会把它理解成“一个高级 Docker 管理器”。这个理解不算错,但太浅了。Docker 更关心一个容器怎么构建、怎么运行;Kubernetes 更关心一组应用应该以什么状态持续运行。
所以这篇先不讲太多组件细节,只从 Pod、ReplicaSet、Deployment 这些最常见的对象开始,把 k8s 的基本思路梳理一下。
Pod 是最小调度单元
在 k8s 里,最小调度单元不是容器,而是 Pod。
一个 Pod 里可以有一个或多个容器。大多数业务服务只有一个主容器,但 Pod 这个抽象允许把关系非常紧密的容器放在一起运行,比如主应用容器加一个日志采集 sidecar。
Pod 里的容器共享网络命名空间,所以同一个 Pod 内的容器可以通过 localhost 互相访问。它们也可以共享 volume,用来交换文件或配置。
但这不代表应该把一堆服务都塞进一个 Pod。Pod 不是小型虚拟机。只有生命周期、部署节奏、资源边界都高度一致的容器,才适合放在同一个 Pod 里。
不要直接管理裸 Pod
学习时可以写一个 Pod YAML 来理解字段,但真实项目里一般不会直接管理裸 Pod。
原因很简单:Pod 会死。
节点故障、镜像拉取失败、探针失败、资源被驱逐,都可能导致 Pod 被删除或重新创建。如果只是手写一个裸 Pod,它没有更高层的控制器负责维持副本数量。
Kubernetes 真正重要的地方在于控制循环。你声明“我希望系统里有 3 个可用副本”,控制器就持续观察当前状态。如果只剩 2 个,它会再拉起 1 个。如果多了,它会删掉多余的。
这个思路和手工运维不一样。手工运维更像是执行动作,k8s 更像是持续对齐目标状态。
ReplicaSet 负责副本数量
ReplicaSet 的职责很直接:保证某类 Pod 的副本数量符合预期。
比如希望有 3 个 nginx Pod:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-rs
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
这里最关键的是 selector 和 template.metadata.labels 要对得上。ReplicaSet 通过 label 找到自己管理的 Pod。如果 selector 写错,控制器就可能找不到 Pod,或者误管了不该管的 Pod。
不过项目里也很少直接写 ReplicaSet。因为它只解决副本数量,不负责发布策略。
Deployment 是最常用入口
实际写业务服务时,最常见的是 Deployment。
Deployment 会在底层创建 ReplicaSet,再由 ReplicaSet 创建 Pod。它比 ReplicaSet 多了一层发布管理能力,可以做滚动更新、回滚、版本记录。
一个典型 Deployment 大概是这样:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: registry.example.com/order-service:2024-01-08
ports:
- containerPort: 8080
这段 YAML 看起来只是启动了 3 个服务实例,但背后其实包含了几个重要约定:
1.应用用镜像交付。 2.副本数量由控制器维持。 3.Pod 通过 label 被识别。 4.发布时通过更新模板触发滚动升级。
理解了这些,再看各种平台上的“部署应用”按钮,其实大多都是在生成类似的对象。
Label 是对象之间的胶水
k8s 里很多对象不是靠固定名称强绑定,而是靠 label 和 selector 关联。
Deployment 用 selector 找 Pod,Service 用 selector 找后端 Pod,监控和日志系统也经常用 label 做筛选。
所以 label 不能随便写。至少要有一些稳定字段,比如:
labels:
app: order-service
tier: backend
part-of: mall
我比较不建议把经常变化的信息放进核心 selector,比如版本号、构建号、临时环境标识。版本可以作为补充 label,但不要让 Service 这类长期对象依赖它,否则发布时容易出现流量切换异常。
label 设计得越稳定,后面接入 Service、Ingress、监控、日志和灰度发布时越省事。
声明式不是只写 YAML
k8s 经常被说成声明式系统,但声明式不等于“把命令改成 YAML”。
真正的区别是:你描述期望状态,控制器负责把实际状态推进过去。
比如你把 Deployment 的 replicas 从 3 改成 5,k8s 会创建两个新 Pod。你把镜像 tag 改了,k8s 会触发滚动更新。你删掉一个 Pod,控制器会发现副本不足并重新创建。
这也意味着我们排查问题时,不能只看最后一个命令是否执行成功,而要看对象状态有没有收敛:
kubectl get deployment order-service
kubectl describe deployment order-service
kubectl get pods -l app=order-service
如果期望状态和实际状态不一致,就要顺着事件、Pod 状态、镜像拉取、调度结果继续查。
小结
Kubernetes 入门时,先把 Pod 和控制器关系弄清楚,比记命令更重要。
Pod 是应用运行的最小调度单元,但业务服务通常不直接管理 Pod,而是通过 Deployment 声明副本数量和发布策略。ReplicaSet 在中间保证副本数量,label 和 selector 负责把这些对象关联起来。
如果能先接受“声明期望状态,控制器持续对齐”这个思路,后面再学 Service、Ingress、配置、存储和排障,就不会觉得 Kubernetes 只是很多 YAML 堆在一起。
If you enjoyed this, leave a comment~