什么时候用 Service,什么时候用 Deployment

This article is extracted from the chat log with AI. Please identify it with caution.

本文边界:本文聚焦 Service 与 Deployment 的职责划分和对象选型,不展开 Deployment 的完整资源机制或每种发布操作的细节。

推荐深读Deployment 详解:Kubernetes 集群的"应用部署控制器"rollout 与 rolling update 的区别

说明:本文由 Codex 根据作者提供的主题、素材与公开资料辅助生成,属于 AI 生成/整理内容,非作者原创。请读者自行甄别并交叉验证。

先说结论#

DeploymentService 解决的是两个不同问题。

Deployment 管应用实例;
Service 管访问入口。

更具体一点:

对象解决的问题
Deployment要运行几个 Pod,Pod 用什么镜像,如何更新和回滚
Service客户端如何稳定访问一组动态变化的 Pod

所以它们不是二选一关系。

大多数无状态服务会同时需要:

Deployment + Service

Deployment 保证 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: web

Service 里有:

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。

这样就不会把“部署应用”和“暴露应用”混成一个问题。

参考资料#

本文共 2641 字,创建于 Jun 28, 2026

相关标签: Kubernetes, DevOps, ByAI