把应用装进 Docker 只是第一步;当容器多到几十上百个,你就需要 Kubernetes 来统一调度。本文拆开它的三个核心对象——Pod、Deployment、Service,帮你建立可落地的认知框架,而不是被一堆概念劝退。
一、为什么容器之后还要 Kubernetes
Docker 解决了”环境一致性”问题,我们在Docker 网络模式详解里聊过容器如何联网;但它没有回答:几百个容器该调度到哪台机器?某个挂了谁来拉起?流量怎么稳定地找到当前健康的实例?这就是 Kubernetes(常缩写为 K8s)要解决的编排(Orchestration)问题。
K8s 的价值可以浓缩成四件事:调度(把 Pod 放到资源合适的节点)、自愈(副本挂了自动补)、弹性(按需扩缩容)、服务发现(用稳定名字访问变动的实例)。理解这三件套,就理解了 K8s 八成的能力。
举个直观的例子:你声明”我要 3 个 nginx 副本”,调度器会扫描所有节点,挑出 CPU、内存余量足够的节点把 Pod 放上去;之后哪怕某个节点宕机,控制器也会把上面的 Pod 重新调度到健康节点。你只管”要什么”,不用管”放哪”。
二、Pod:Kubernetes 的最小调度单元
2.1 Pod 不是容器,是容器的”宿舍”
很多新手会把 Pod 和容器画等号,其实 Pod 是调度、网络、存储的最小单位。一个 Pod 可以包含一个或多个容器,它们共享同一个网络命名空间(localhost 互通)和存储卷。最常见的形态是”一个主容器 + 一个辅助容器(如日志收集 sidecar)”,但入门阶段可以先理解为”一个 Pod 跑一个容器”。
当确实需要多容器协作时,sidecar 模式非常常见:主容器跑业务,旁边挂一个轻量容器负责日志收集或网络代理,二者通过 localhost 直接通信、共享磁盘,部署却只用一个 Pod 描述,生命周期天然绑定。理解这一点,你就不会再纠结”这个进程到底该放哪个容器”。
2.2 写一个最小 Pod
# pod.yaml —— 一个 Pod 里跑一个 nginx 容器
apiVersion: v1
kind: Pod
metadata:
name: nginx-demo
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
注意 labels 这一行——它看似只是个标签,却是后面 Service 找到 Pod、Deployment 管理 Pod 的唯一依据。没有 label,K8s 的世界就乱了套。
三、Deployment:让副本与发布可控
3.1 ReplicaSet 与自愈
直接手捏 Pod 有个致命问题:Pod 被删就真没了。Production 里几乎不会直接创建 Pod,而是通过 Deployment 间接管理。Deployment 会维持你声明的副本数(replicas),某个 Pod 意外退出,控制器立刻拉起一个新的补上,这就是”自愈”。
3.2 滚动更新与回滚
Deployment 真正的杀手锏是滚动更新:你只改镜像版本,K8s 会逐个用新 Pod 替换旧 Pod,保证服务不中断;一旦新版本有问题,一条命令就能回滚到上一个稳定版本。下面是一份标准 Deployment 清单:
# deployment.yaml —— 维持 3 个副本并支持滚动更新
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
默认滚动策略会保证”先起新、再下旧”,你还可以用 maxSurge 控制最多多起几个临时副本、用 maxUnavailable 控制最多允许几个旧副本同时离线,从而精确权衡发布速度与可用性。这比手写一堆重启脚本可靠得多。
发布与回滚都是一行命令的事:
# 滚动更新镜像
kubectl set image deployment/nginx-deploy nginx=nginx:1.28
# 观察发布状态
kubectl rollout status deployment/nginx-deploy
# 出问题一键回滚
kubectl rollout undo deployment/nginx-deploy
四、Service:给 Pod 一个稳定的访问入口
4.1 三种类型 ClusterIP / NodePort / LoadBalancer
Pod 随时可能被调度到不同节点、IP 也会变,直接用 Pod IP 访问是灾难。Service 就是一层稳定的虚拟访问入口:它用 label 选择器把请求转发到后端一组 Pod,对调用方屏蔽了 Pod 的生灭变动。三种常用类型对比如下:
| 类型 | 访问范围 | 典型用途 |
|---|---|---|
| ClusterIP | 仅集群内部 | 服务间调用、后端 API |
| NodePort | 节点 IP + 固定端口 | 临时调试、裸金属暴露 |
| LoadBalancer | 云厂商外部 IP | 生产对外服务 |
4.2 用 Service 串起前后端
# service.yaml —— 给 Deployment 暴露稳定入口
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
selector:
app: nginx # 匹配 Pod 的 label
ports:
- protocol: TCP
port: 80 # Service 自身端口
targetPort: 80 # 转发到 Pod 的端口
type: ClusterIP
selector 里的 app: nginx 与前面 Deployment 的 label 严丝合缝,正是这一处把”动态 Pod 集合”和”稳定入口”绑定了起来。想要从集群外访问,把 type 改成 NodePort 或 LoadBalancer 即可;生产环境更推荐用 Ingress(本质是增强版七层路由,和我们在Nginx 反向代理里讲的思路一脉相承)。
Service 背后依赖 kube-proxy 在节点上维护转发规则(早期用 iptables,新版本多用 IPVS),把虚拟 IP 的流量负载均衡到后端 Pod。对调用方来说,backend:8080 永远可用,至于后面是 3 个还是 30 个 Pod,完全无感。这一层”稳定的名字”,正是微服务能自由扩缩容的前提。
五、三者如何协作:一个 Spring Boot 应用的上线链路
把三件套串起来,就是一个真实的上线闭环。以我们写过的Spring Boot 3 升级服务为例:先在 CI(参考GitHub Actions 实战)里构建镜像并推送到镜像仓库,再用 Deployment 跑起多副本,最后用 Service 把流量导进去。
# 1) 构建镜像并推仓库(CI 用 GitHub Actions 自动化)
# 2) 按顺序提交清单
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# 3) 验证
kubectl get pods -l app=nginx
kubectl get svc nginx-svc
你会发现整个交付物就是几份声明式 YAML:你想要什么状态,K8s 负责让现实逼近它。这种”声明式”思维,正是云原生区别于传统脚本运维的核心。
六、新手避坑清单
6.1 label 对不上,什么都连不通
Deployment 的 selector.matchLabels、Pod template 的 labels、Service 的 selector 三者必须一致。这是新手 80% 连不通的根源,排查时先 kubectl get pods --show-labels 核对。
6.2 别直接用 Pod,也别把数据库塞进 K8s
无状态服务(Web、API)最适合上 K8s;而自带持久化、有状态依赖的组件(MySQL、Redis 主节点)若非要上,也要用 StatefulSet + PV,而不是裸 Pod。新手期建议先把无状态服务跑顺,有状态组件先用托管服务或单独部署。真正落地时,配合命名空间(Namespace)做环境隔离、用 ResourceQuota 约束团队配额,才能把 K8s 从”能跑”变成”可控的生产平台”。
- 资源限制:给容器设 requests/limits,否则一个 Pod 吃满节点内存会拖垮整台机器。
- 健康检查:配好 liveness / readiness 探针,K8s 才知道何时重启、何时放流量。
- 镜像标签:生产禁用
latest,固定版本才能保证回滚可预期。
七、总结
Pod 是最小调度单元,Deployment 负责副本数与滚动发布,Service 提供稳定访问入口——三者组合,就覆盖了从”跑起来”到”稳稳对外服务”的主路径。下一步可以深入 Ingress、ConfigMap、探针与健康检查,逐步把这套声明式能力用熟。你准备先把哪个服务搬上 K8s?欢迎在评论区聊聊你的迁移计划。




