本文边界:本文聚焦 Service 与 Deployment 的职责划分和对象选型,不展开 Deployment 的完整资源机制或每种发布操作的细节。
推荐深读:Deployment 详解:Kubernetes 集群的"应用部署控制器" 与 rollout 与 rolling update 的区别。
说明:本文由 Codex 根据作者提供的主题、素材与公开资料辅助生成,属于 AI 生成/整理内容,非作者原创。请读者自行甄别并交叉验证。
先说结论#
Deployment 和 Service 解决的是两个不同问题。
Deployment 管应用实例;
Service 管访问入口。更具体一点:
| 对象 | 解决的问题 |
|---|---|
Deployment | 要运行几个 Pod,Pod 用什么镜像,如何更新和回滚 |
Service | 客户端如何稳定访问一组动态变化的 Pod |
所以它们不是二选一关系。
大多数无状态服务会同时需要:
Deployment + ServiceDeployment 保证 Pod 运行,Service 给这些 Pod 提供稳定访问方式。
Deployment 管什么#
Deployment 是工作负载控制器。
它负责把应用维持在期望状态:
期望副本数:3
期望镜像:nginx:1.27
期望标签:app=web
期望探针、资源、环境变量、挂载配置典型 Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80这个对象表达的是:
请始终让集群里有 3 个 app=web 的 Pod;
Pod 模板按这里定义;
如果 Pod 挂了,自动补;
如果模板变了,按策略滚动更新。Deployment 通常用于:
- 无状态 Web 服务。
- API 服务。
- Worker 服务。
- 可以水平扩展的后台任务。
- 需要滚动更新和回滚的应用。
Service 管什么#
Service 是网络访问抽象。
Pod 是动态的:
- Pod 会重建。
- Pod IP 会变。
- 副本数量会变。
- 滚动更新时新旧 Pod 会同时存在。
Service 给这些 Pod 提供一个稳定入口:
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80它表达的是:
把访问 web Service 80 端口的流量;
转发到 app=web 的后端 Pod 的 80 端口。客户端可以访问:
curl http://web.default.svc.cluster.local而不需要知道某个具体 Pod IP。
Service 通常用于:
- 给集群内其他服务提供稳定 DNS 名称。
- 给一组 Pod 做四层负载分发。
- 通过 NodePort 或 LoadBalancer 暴露到集群外。
- 为没有 selector 的外部服务提供 Kubernetes 内部抽象。
二者如何配合#
最常见链路是:
Deployment
↓ 创建和维护
ReplicaSet
↓ 创建和维护
Pod
↓ labels 被 selector 选中
Service
↓ EndpointSlice 记录后端
客户端访问稳定入口关键点是 labels。
Deployment 的 Pod template 里有:
metadata:
labels:
app: webService 里有:
selector:
app: web这两个要匹配。
如果 Service selector 写错,Deployment 可能运行正常,但 Service 没有任何后端。
排查:
kubectl get deploy web
kubectl get pod -l app=web -o wide
kubectl get svc web
kubectl get endpointslice -l kubernetes.io/service-name=web什么时候只需要 Deployment#
有些工作负载不需要被其他服务主动访问。
例如:
- 消费消息队列的 Worker。
- 定时拉取任务的后台进程。
- 只主动访问外部系统的同步程序。
- 不暴露网络端口的批处理服务。
这种情况下可能只需要 Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: worker
spec:
replicas: 3
selector:
matchLabels:
app: worker
template:
metadata:
labels:
app: worker
spec:
containers:
- name: worker
image: example/worker:v1
env:
- name: QUEUE_NAME
value: jobs如果没有别的东西要访问这些 Pod,就不必创建 Service。
什么时候只需要 Service#
Service 可以没有 selector。
这种情况常用于把集群外服务包装成集群内名称。
例如:
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
ports:
- port: 5432
targetPort: 5432然后手工创建 EndpointSlice 指向外部后端。
或者使用 ExternalName:
apiVersion: v1
kind: Service
metadata:
name: external-api
spec:
type: ExternalName
externalName: api.example.com这类 Service 不管理 Pod,也不需要 Deployment。
适用场景:
- 给外部数据库一个集群内固定名称。
- 迁移期间把旧系统纳入 Kubernetes DNS。
- 用统一名称屏蔽外部服务地址变化。
注意:ExternalName 主要是 DNS CNAME 语义,不是四层代理。
什么时候二者都需要#
典型 API 服务:
Deployment:运行 3 个 API Pod;
Service:提供 api.default.svc.cluster.local;
Ingress 或 Gateway:把外部 HTTP 流量引到 Service。完整示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: example/api:v1
ports:
- name: http
containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- name: http
port: 80
targetPort: http这里的职责分离很清楚:
Deployment 关心 image、replicas、Pod 模板;
Service 关心 selector、port、targetPort、访问入口。常见对象选择#
有时问题不是 Service 和 Deployment 二选一,而是 Deployment 也不一定是正确工作负载对象。
| 场景 | 更合适的对象 |
|---|---|
| 无状态长期运行服务 | Deployment |
| 每个节点运行一个副本 | DaemonSet |
| 有稳定网络标识和有状态存储 | StatefulSet |
| 一次性任务 | Job |
| 定时任务 | CronJob |
| 暴露一组 Pod 的网络入口 | Service |
| 七层 HTTP 路由入口 | Ingress 或 Gateway |
例如数据库通常不适合直接用 Deployment 草率部署,因为它往往需要稳定身份、稳定存储、顺序启动和更谨慎的故障处理。
常见误区#
误区一:Service 会创建 Pod#
不会。
Service 只是选择后端和提供入口,它不会创建或维护 Pod。
创建 Pod 的通常是 Deployment、StatefulSet、DaemonSet、Job 等工作负载控制器。
误区二:Deployment 会暴露网络#
不会。
Deployment 创建的 Pod 有 Pod IP,但这个 IP 不稳定,也不适合作为服务入口。
要给其他客户端稳定访问,通常需要 Service。
误区三:containerPort 等于 Service 端口#
不等于。
ports:
- port: 80
targetPort: 8080含义是:
Service 暴露 80;
后端 Pod 接收 8080。containerPort 更像容器声明自己监听的端口信息,Service 真正转发到哪里看 targetPort。
误区四:Service selector 可以随便写#
不能。
selector 必须匹配后端 Pod labels。
检查:
kubectl get pod --show-labels
kubectl describe svc api
kubectl get endpointslice -l kubernetes.io/service-name=api如果 EndpointSlice 为空,常见原因就是 selector 没匹配到 Ready Pod。
排查顺序#
如果访问某个服务失败,可以按这个顺序:
# 1. Deployment 是否正常
kubectl get deploy api
kubectl describe deploy api
# 2. Pod 是否存在并 Ready
kubectl get pod -l app=api -o wide
kubectl describe pod <pod-name>
# 3. Service 是否存在
kubectl get svc api
kubectl describe svc api
# 4. EndpointSlice 是否有后端
kubectl get endpointslice -l kubernetes.io/service-name=api -o wide
# 5. 集群内 DNS 和访问测试
kubectl run -it --rm debug --image=curlimages/curl -- sh
nslookup api.default.svc.cluster.local
curl -v http://api.default.svc.cluster.local现象和方向:
| 现象 | 优先检查 |
|---|---|
| Deployment 不 Ready | 镜像、探针、资源、调度、日志 |
| Pod Ready 但 Service 无后端 | selector、labels、namespace |
| Service 有后端但访问失败 | port、targetPort、NetworkPolicy、kube-proxy |
| DNS 不解析 | CoreDNS、Service 名称、namespace |
| 外部访问失败 | NodePort、LoadBalancer、Ingress、Gateway |
小结#
一句话区分:
Deployment 让应用跑起来;
Service 让别人稳定访问它。设计一个 Kubernetes 应用时,可以先问:
需要长期运行几个副本吗?需要 Deployment。
需要被别人访问吗?需要 Service。
需要从集群外 HTTP 访问吗?再加 Ingress 或 Gateway。
需要有状态身份和存储吗?考虑 StatefulSet。这样就不会把“部署应用”和“暴露应用”混成一个问题。