压测用Job——聊聊K8s的服务与工作负载
潘忠显 / 2025-12-05
前段时间在做功能和压力测试时,使用的是 K8s 的 Job,但也有人使用 服务+部署 的方式实现。
今天来聊聊,为什么这种场景 Job 最适合。
进而介绍 K8s 的服务到底是什么,服务和工作负载的关系,不同工作负载的异同。
什么是 Job 和 Deployment
首先 Job 和 Deployment 都是K8s中的工作负载。Job 表示一次性任务,运行完成后就会停止。
Job 会创建一个或者多个 Pod,然后开始运行,直到指定数量的 Pod 成功终止。当数量达到指定的成功个数阈值时,任务(即 Job)结束。
一次压测或者功能测试也是这样,是一次性任务,压测可能使用多个Pod,功能测试则通常一个Pod就足够了。每次压测的结果独立,也没有任何的状态信息。
如果你一个Job的配置或者镜像有什么问题,直接删掉 Job 重新启动一个即可。
在一个比较完整的K8s操作平台,我们往往能看到可以使用各种各样的工作负载。

Deployment 用于管理运行一个应用负载的一组 Pod,通常适用于不保持状态的负载(任何 Pod 都是可互换的,可以在需要时进行替换)并会持续运行。
比如,一个 Web 服务,或者一个后台处理服务。如果你是个后台开发,可能接触的绝大部分都会是 Deployment。
Deployment 有版本的概念,可以通过滚动更新的方式进行升级,或者回滚。
什么是Service
Service 是 Kubernetes 中将运行在一个或一组 Pod 上的网络应用程序公开为网络服务的方法。
可以将其理解为 K8s 的 “网络抽象层”:
- 稳定的网络入口: Service 为一组动态变化的 Pod 提供一个固定不变的 IP 地址 (ClusterIP) 和 DNS 名称。无论后端的 Pod 如何创建、销毁或重启,这个入口地址始终不变。
- 服务发现机制: 它自动在集群 DNS (CoreDNS) 中注册记录。集群内的其他应用可以通过这个 Service 的名称(而不是 Pod 的 IP)来访问它。
- 负载均衡: Service 充当请求的负载均衡器,将流量平均分配给所有健康且匹配标签的后端 Pod。
为什么服务和部署关联密切
在日常的 Kubernetes 使用中,人们将 Service 和 Deployment 视为一个整体的频率,远高于将 Service 和其他工作负载联系起来。
一个 Deployment 定义了如何运行和管理一组应用程序的副本(Pod)。你可以把它看作是应用程序的生命周期管理器。他会给这组 Pods 打上标签。
一个 Service 定义了访问一组 Pods 的策略。你可以把它看作是负载均衡器和稳定访问点。它会通过标签选择器来选择一组Pods。
这也有点类似生产者(Deployment)和消费者(Service)的关系。他们往往会关联到同一组 Pods:Deployment 负责运行——让应用跑起来,Service 负责暴露——让应用被访问到。
但 Kubernetes 的核心设计原则:松耦合和标签选择器 (Label Selectors)。
因此,一个服务也可以关联多组 Pods,典型的如金丝雀发布,其 Selector 同时匹配 Deployment A 和 B 的 Pods。通过调整不同部署的副本数(例如只运行 5% 的 Pods)来控制新版本获得的流量比例。
反过来,也有可能一个 Deployment 被多个 Service 关联的场景。
另外一个「服务」
在很多人的认知中,K8s 的服务(Service)和我们平时说的「服务」可能是两个概念。
一些平台会将多个 Deployment 和多个 Service 封装在一起,形成的“更大”的概念,而这个也通常被称为「服务」。为了区分,其实也可以称之为「应用」。
在一个「服务」内,Deployment 和 Service 之间可以做相互关联,但是在「服务」之间,虽然都在同一个K8s集群的同一个命名空间内,这些对象仍然是相互独立的。
这种隔离通常会使用标签来实现,比如某个服务内的所有资源,都会被打上 tenc-app: press-client-148-online 的标签。
测试不适合起新服务
尽管前面介绍了 Job 更适合做测试,但很多封装度高的K8s平台往往只提供「服务」的创建入口,只能创建 Deployment。
在这种约束下,是否能把服务当 Job 来使用(每启动一次测试就创建一个服务)呢?显然,这是不合适的,主要原因是创建「服务」是有成本的。
你可能觉着,我服务中部署中缩减到0副本就行了。但实际上,此时仍然有开销,长期积累会影响集群性能。
一次性任务的遗留消耗
以前边压测场景为例,这些任务往往不需要网关资源。
但是作为K8s的一些基础管理的信息,在创建一次性任务的服务并销毁Pod之后,也会有一些遗留:
- DNS 记录: KubeDNS/CoreDNS 中的服务发现记录仍然存在。
- Kube-proxy 规则: 集群内每个节点上的
iptables或IPVS规则仍然存在,尽管它们现在指向一个空的 Endpoints 列表。

- 监控和告警规则:通过模板创建服务,往往也顺便会创建一些监控和告警规则。
- 配置文件(ConfigMap)和密钥(Secret):如果服务使用了配置,这些资源也会继续存在。
要注意,这里的DNS记录和Kube-proxy 规则并不会消耗IP,当Pods缩减为0,通常就会释放对应的子网IP,所以下边这种错误一般不是因为服务创建太多引起的:
普通服务的遗留消耗
除了一次性任务的遗留消耗,普通服务在删除 Deployment 之后,也会有额外的一些资源存在,产生持续的资源消耗和成本:
- LoadBalancer:如果是
type: LoadBalancer的 Service,它在云服务商处创建的外部负载均衡器会继续运行和计费。 - 网关资源:普通部署通常会通过 Ingress 资源暴露服务,这些 Ingress 资源也会继续存在,占用 Ingress Controller 的资源,会消耗其的 CPU 和内存。
- 流量黑洞: 外部用户仍然可能通过网关访问到这个被删除的 Ingress 规则所配置的域名,但请求会503失败,这通常也会产生网关流量计费。
- 遗留的PVC:一些有状态的服务通常需要使用到持久化存储 PersistentVolumeClaim,当服务停掉之后,
PVC资源本身不会自动消失,绑定的 PV(PersistentVolume) 也会存在,会持续产生成本。需要手动删除。
约束下的最佳实践
对于这些只提供部署的平台,如果要避免每次都创建「服务」产生资源消耗,也是可以通过在一个「服务」中模拟“运行至完成”(Run-to-Completion)的任务来实现的。
- 创建单一服务来维护各个活动的压力测试
- 使用不同部署版本来区分不同的测试任务,比如以活动名称作为版本部署名称
- 压测完成发送信号至管理中心,调用接口将部署缩减为0,并清理掉部署
- 清理集群内域名、集群外域名、集群外北极星等其他服务发现信息
是不是只要做到清理干净,创建每次测试创建单个服务也是可以的?
答案依然是否定的。
在一个「服务」中,不同的资源有同一个如 tenc_app 的标签,这样如果要监控相关服务,可以很方便的配置,即使部署被缩减,历史指标也会被保留。而如果创建了单个服务然后清理掉,很难再查到历史的指标信息,比如负载或者压测程序自身暴露的指标等等。
小结
本文介绍了 K8s 中 Job 和 Deployment 的区别,为什么 Job 更适合做测试任务。还介绍了 Service 的作用,以及为什么服务和部署关联密切,以及部署缩0的服务有哪些消耗。
希望通过这个压测场景的讨论,能帮助你更好地理解 K8s 的工作负载和服务模型,从而在实际应用中做出更合适的选择。
