Jason Pan

压测用Job——聊聊K8s的服务与工作负载

潘忠显 / 2025-12-05


前段时间在做功能和压力测试时,使用的是 K8s 的 Job,但也有人使用 服务+部署 的方式实现。

今天来聊聊,为什么这种场景 Job 最适合。

进而介绍 K8s 的服务到底是什么,服务和工作负载的关系,不同工作负载的异同

什么是 Job 和 Deployment

首先 Job 和 Deployment 都是K8s中的工作负载。Job 表示一次性任务,运行完成后就会停止。

Job 会创建一个或者多个 Pod,然后开始运行,直到指定数量的 Pod 成功终止。当数量达到指定的成功个数阈值时,任务(即 Job)结束。

一次压测或者功能测试也是这样,是一次性任务,压测可能使用多个Pod,功能测试则通常一个Pod就足够了。每次压测的结果独立,也没有任何的状态信息

如果你一个Job的配置或者镜像有什么问题,直接删掉 Job 重新启动一个即可。

在一个比较完整的K8s操作平台,我们往往能看到可以使用各种各样的工作负载。

tke-ui

Deployment 用于管理运行一个应用负载的一组 Pod,通常适用于不保持状态的负载(任何 Pod 都是可互换的,可以在需要时进行替换)并会持续运行

比如,一个 Web 服务,或者一个后台处理服务。如果你是个后台开发,可能接触的绝大部分都会是 Deployment。

Deployment 有版本的概念,可以通过滚动更新的方式进行升级,或者回滚。

什么是Service

Service 是 Kubernetes 中将运行在一个或一组 Pod 上的网络应用程序公开为网络服务的方法。

可以将其理解为 K8s 的 “网络抽象层”:

为什么服务和部署关联密切

在日常的 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 来使用(每启动一次测试就创建一个服务)呢?显然,这是不合适的,主要原因是创建「服务」是有成本的。

multi-service-of-one-test

你可能觉着,我服务中部署中缩减到0副本就行了。但实际上,此时仍然有开销,长期积累会影响集群性能

一次性任务的遗留消耗

以前边压测场景为例,这些任务往往不需要网关资源。

但是作为K8s的一些基础管理的信息,在创建一次性任务的服务并销毁Pod之后,也会有一些遗留:

empty-dns-record

要注意,这里的DNS记录和Kube-proxy 规则并不会消耗IP,当Pods缩减为0,通常就会释放对应的子网IP,所以下边这种错误一般不是因为服务创建太多引起的:

no-more-ip-warning

普通服务的遗留消耗

除了一次性任务的遗留消耗,普通服务在删除 Deployment 之后,也会有额外的一些资源存在,产生持续的资源消耗和成本:

约束下的最佳实践

对于这些只提供部署的平台,如果要避免每次都创建「服务」产生资源消耗,也是可以通过在一个「服务」中模拟“运行至完成”(Run-to-Completion)的任务来实现的。

  1. 创建单一服务来维护各个活动的压力测试
  2. 使用不同部署版本来区分不同的测试任务,比如以活动名称作为版本部署名称
  3. 压测完成发送信号至管理中心,调用接口将部署缩减为0,并清理掉部署
  4. 清理集群内域名、集群外域名、集群外北极星等其他服务发现信息

是不是只要做到清理干净,创建每次测试创建单个服务也是可以的?

答案依然是否定的

在一个「服务」中,不同的资源有同一个如 tenc_app 的标签,这样如果要监控相关服务,可以很方便的配置,即使部署被缩减,历史指标也会被保留。而如果创建了单个服务然后清理掉,很难再查到历史的指标信息,比如负载或者压测程序自身暴露的指标等等。

小结

本文介绍了 K8s 中 Job 和 Deployment 的区别,为什么 Job 更适合做测试任务。还介绍了 Service 的作用,以及为什么服务和部署关联密切,以及部署缩0的服务有哪些消耗。

希望通过这个压测场景的讨论,能帮助你更好地理解 K8s 的工作负载和服务模型,从而在实际应用中做出更合适的选择。