1. 引言
开发应用或服务时,我们常常会问:它能承受多少并发请求?它依赖的服务又会有怎样的响应?即使验证了所有功能都正常运行,也不能保证随着需求增长,系统依然能保持原有的性能。
正因如此,软件测试不能只停留在验证功能需求上,还要评估性能、稳定性和响应速度等方面。尤其是负载测试,目的并非把系统压到崩溃为止,而是弄清系统在各种条件下的表现,找出它在什么时候开始无法满足预期要求。
在上一篇文章中,我们使用 Amazon Bedrock、S3 Vectors 和 DynamoDB 构建了一个识别重复商品的服务,其中包含一个执行实时搜索的 AWS Lambda 函数。初步测试时,服务能在几秒内响应,但还有一个问题悬而未决:如果大量用户同时访问,它会表现如何?
为了回答这个问题,在第二部分中,我们将通过 Amazon API Gateway 把服务暴露出去,并评估它在负载下的表现。不过在跑测试之前,得先明确我们要测量什么、用什么标准来评估结果。为此,我们会介绍一些站点可靠性工程(SRE)的基础概念。接下来,我们将在 AWS 上用 Grafana k6 自动化负载测试,最后生成一份报告,由生成式 AI 帮我们解读收集到的指标。
2. 认识站点可靠性工程(SRE)
2.1. SRE 是什么?
在评估 API 的负载表现之前,我们需要先明确:什么样的性能和可靠性才算合格。一个服务也许还在持续响应请求,但这并不一定意味着它达到了预期的性能要求。
这时,站点可靠性工程(SRE)就派上用场了。SRE最早由Google提出,其核心是把软件工程的方法运用到系统的运行和维护中,以保障系统可靠性。它强调定义可量化的目标、选取能够真实反映服务状态的指标,并通过自动化来减少重复性的运维工作。
在我们的场景中,这些理念让我们不再局限于“API是否能响应”这种简单判断,而是可以进一步评估响应时间、错误率以及不同负载条件下的性能表现等具体指标。
2.2. SRE的七大原则
SRE建立在一系列原则之上,指导我们如何设计、运维和维护可靠的服务。这些原则涵盖可靠性目标的设定、风险管理、自动化、监控,以及尽量保持系统简洁等方面。
下面的信息图基于Google的《Site Reliability Engineering》一书,总结了其中的七项原则。

Illustration by the author. Based on Google's Site Reliability Engineering book.
2.3. 容量规划与性能
服务的可靠性不仅取决于可用性,还取决于能否在保持可接受性能的前提下应对需求。为此,SRE包含多项实践,旨在提前发现问题、优化资源利用,并降低生产环境变更带来的影响。
其中值得关注的实践包括:
- 容量规划:确保系统拥有足够的资源和冗余,能够承受预期负载,同时不影响可用性目标。为此,Google建议定期进行负载测试,评估服务的实际容量。
- 效率与性能:了解性能随负载增长的变化趋势,识别潜在的性能退化,并寻找优化资源利用的机会。
- 变更管理:降低生产环境变更带来的风险。根据Google的经验数据,约70%的服务故障源于对线上系统的变更,这也凸显了尽早发现问题的重要性。 这些实践与我们的场景直接相关——我们正是要在不同负载条件下评估API的性能。
2.4. SLI、SLO 和 SLA
评估服务的容量和性能,不能只是跑跑测试、收集指标就够了。我们还需要明确要测量什么,以及哪些服务水平是可以接受的。
在这一点上,SRE 引入了三个相互关联的概念:SLI(服务水平指标)、SLO(服务水平目标)和 SLA(服务水平协议)。三者共同把技术指标与服务目标和承诺连接起来。
下面的信息图总结了每个概念的含义、它能回答的问题,以及一个实际应用示例。

Illustration by the author. Based on Google's SRE framework.
3. Grafana k6:性能测试工具
Grafana k6 是由 Grafana Labs 用 Go 语言开发并维护的开源工具,用于对应用、API 和微服务进行负载与性能测试。测试场景用 JavaScript 定义,因此更容易实现测试自动化,并将其集成到开发工作流中。
虽然我们常把这类工具和压力测试画上等号,但 k6 其实可以评估系统在不同负载条件下的表现,而不一定要把系统压到崩溃为止。

Illustration by the author. Based on Grafana k6 documentation.
3.1. 负载测试的类型
根据评估目标的不同,可以定义各种测试场景:
- Smoke(冒烟测试):验证系统在最小负载下能否正常响应。
- Average-load(平均负载):评估系统在正常运行条件下的表现。
- Stress(压力测试):考察系统在高负载下的响应情况。
- Soak(浸泡测试):发现负载长时间持续时可能出现的性能劣化。
- Spike(尖峰测试):评估系统应对流量突增的能力。
- 拐点测试:逐步增加负载,找出系统不再满足预期指标的那个临界点。 通过这些场景,我们可以从不同角度分析系统性能。此外还要注意,应用通常依赖多个组件和服务,性能瓶颈可能来自架构中的任何一环。
3.2. k6 的核心组件
要执行这些测试,k6 提供了若干组件,用于配置负载、校验响应、收集指标,并将结果与其他工具集成。

Illustration by the author. Based on Grafana k6 documentation.
这些组件可以归纳为四大类:
- ⚙️ 选项(Options):定义测试的执行方式,可配置场景、负载阶段、虚拟用户(VU)、持续时间、迭代次数和验收阈值。借助这些选项,我们可以复现前面提到的各类测试。
- ✅ 检查(Checks):对收到的响应进行条件校验,例如 HTTP 状态码或响应内容。检查失败会记录到指标中,但不会自动中断测试。
- 📊 指标(Metrics):收集系统行为的相关数据,包括响应时间、请求数量和错误率。k6 自带内置指标,也支持自定义指标。
- 🔗 集成(Integrations):支持将测试纳入 CI/CD 流水线,并把结果导出到存储、监控和可视化工具。 在本方案中,我们会用CSV 和 JSON存储结果,随后处理最终报告中包含的指标。
4. 架构
我们的负载测试方案建立在上一篇文章开发的game-catalog-resolve-product Lambda 函数之上,该函数借助 Amazon Bedrock、S3 Vectors 和 DynamoDB 来识别重复商品。
在本系列的第二部分,我们通过Amazon API Gateway(HTTP API)将函数暴露出去,并采用 IAM 授权,因此 k6 发起的请求需要用 AWS 凭证进行签名。
整个测试流程通过 AWS Step Functions 实现自动化:由它编排 Amazon ECS Fargate 上的 k6 执行,再调用 AWS Lambda 函数生成报告。测试脚本和结果存放在 Amazon S3 中,容器镜像则保存在 Amazon ECR 里。
全部基础设施均使用 Terraform 定义,代码托管在 GitHub 上,方案可随时复现。
5. 使用案例:自动化负载测试
在这个案例中,我们将对 API 运行多种负载测试场景。为此,我们使用 Grafana k6 在 Docker 容器中发送请求并收集性能指标。测试完成后,一个基于 Python 的流程会计算结果,并生成一份 PDF 报告,其中包含由 Amazon Bedrock 提供的解读说明。
5.1. 代码仓库
该项目已发布在 GitHub 上:game-catalog-k6。
⚡ 在 AWS 上实现自动化 API 负载测试:Grafana k6 + Amazon Bedrock 智能报告
这是一个无服务器流水线,通过 AWS Step Functions 编排,在 Amazon ECS Fargate 上运行 Grafana k6 对 API 进行负载测试,并生成 PDF 报告,其中指标由 Amazon Bedrock 解读。所有基础设施均使用 Terraform 定义。
📝 文章: 🔗 第一部分(被测 API): 相同还是相似?基于 Amazon Bedrock 和 S3 Vectors 的 AI 商品匹配 · 代码仓库
🎯 它能做什么
- 运行负载测试:在 ECS Fargate 容器中用 k6 执行可配置场景(冒烟、负载、压力、尖峰)。
- 存储每次运行:每次测试都保存在独立的 S3 文件夹中,包含结果以及所用场景的完整副本。
- 生成 PDF 报告:指标由代码计算,Amazon Bedrock 只负责解读,其引用的数字会与计算结果进行校验。
关于阈值设定……
项目结构包括 k6 脚本、测试场景、报告生成器,以及用 Terraform 定义的基础设施。
game-catalog-k6/
├── Dockerfile, entrypoint.sh
├── search.js, sigv4.js
├── scenarios/
├── queries.json
├── report/
│ └── config/
├── terraform/
└── build-and-push.sh
我们的目标是让这套方案可以直接复用于测试其他 API,无需改动底层基础设施或报告生成流程。为此,只需配置三个要素:
- 〰️ 要测试的 API
- 〰️ 要发送的请求
- 〰️ 压力测试场景
5.2. 为另一个 API 配置测试
1. 确定目标 API:api_id
本例中使用 Amazon API Gateway(HTTP API)并结合 IAM 授权,服务 URL 的结构如下:
https://<api-id>.execute-api.us-east-1.amazonaws.com
API标识符可以在AWS管理控制台中找到,路径是API Gateway→你的API→API details。
要配置项目,我们在terraform/terraform.tfvars中指定它:
api_id = "<api-id>"
Terraform 会用这个标识符来构造 API URL,并配置必要的权限,让 k6 容器能通过 IAM 签名请求调用该 API。
2. 定义请求:queries.json
确定了要测试的 API 之后,接下来就要明确要发送哪些请求。
我们的 API 通过 POST /search 接受文本搜索。为了定义测试期间执行的请求,我们使用 queries.json 文件,其中包含多个英文和西班牙文的视频游戏搜索词。
[
{"query": "fifa 14 ps3 digital code", "lang": "en"},
{"query": "fifa 14 ps3 codigo digital", "lang": "es"},
{"query": "gta vice city ps2", "lang": "en"},
{"query": "el mario kart de la wii", "lang": "es"}
]
如果 API 要求的请求结构不同,还需要相应调整 search.js 中的请求体。
3. 配置负载:scenarios
定义好要请求什么之后,接下来要确定负载如何施加。为此,我们在 scenarios/ 目录下使用 JSON 文件,每个文件都有各自的配置。
每个 JSON 文件对应一种不同的负载测试场景:
- smoke.json — 冒烟测试:1 个虚拟用户运行 1 分钟,验证基本功能是否正常。
- load.json — 负载测试:5 个虚拟用户运行 5 分钟,评估系统在稳定负载下的表现。
- stress.json — 压力测试:在 6 分钟内将负载从 5 个虚拟用户逐步增加到 40 个,找出性能下降的临界点。
- spike.json — 尖峰测试:30 秒内将负载从 0 骤增至 50 个虚拟用户,评估系统应对突发流量冲击的能力。 每个场景都可以自定义虚拟用户数量、测试时长、负载模式和性能阈值。
通过这些文件,我们可以配置虚拟用户数量、测试时长、负载模式以及性能阈值。
例如,smoke.json 的内容如下:
{
"name": "smoke",
"description": "1 user for 1 minute: checks that the API works before adding load.",
"queries_file": "queries.json",
"endpoint": "/search",
"think_time_seconds": 1,
"executor": {
"executor": "constant-vus",
"vus": 1,
"duration": "1m"
},
"thresholds": {
"http_req_failed": ["rate<0.01"],
"http_req_duration": ["p(95)<5000"]
}
}
主要参数如下:
- endpoint:请求发送到的 API 路径。
- think_time_seconds:每个虚拟用户两次请求之间的停顿时长。
- executor:定义负载模式、虚拟用户数量以及测试时长。
- thresholds:用于为各项指标设定验收标准。本例中要求错误率低于 1%,p95 响应时间低于 5 秒。 借助这些阈值,我们可以把性能目标转化为测试中可衡量的验收标准,并遵循前文介绍的 SLI 和 SLO 概念。 我们可以修改现有场景,或者新建 JSON 文件来评估不同的负载条件,同时复用同一份 k6 代码。
5.3. 用 Terraform 部署基础设施
配置文件就绪后,我们就可以用 AWS CloudShell 部署整套方案,CloudShell 已预装运行 Terraform 和构建 Docker 镜像所需的工具。
1. 安装 Terraform(仅首次需要)
AWS CloudShell 默认没有 Terraform。我们可以把它下载到主目录,这样即使跨会话也能保留:
curl -sLo /tmp/terraform.zip https://releases.hashicorp.com/terraform/1.9.8/terraform_1.9.8_linux_amd64.zip
mkdir -p ~/bin && unzip -o /tmp/terraform.zip -d ~/bin
echo 'export PATH="HOME/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc
terraform version
2. 搭建基础设施
git clone https://github.com/RominaElenaMendezEscobar/game-catalog-k6.git
cd game-catalog-k6/terraform
echo 'api_id = "<api-id>"' > terraform.tfvars
terraform init
terraform apply \
-target=aws_ecr_repository.k6 \
-target=aws_ecr_repository.report
../build-and-push.sh
terraform apply
部署分三步进行:先创建 Amazon ECR 仓库,再构建并推送容器镜像,最后部署其余资源。
Terraform 会创建所需的基础设施,包括 Amazon ECS Fargate、AWS Step Functions、AWS Lambda、Amazon S3,以及 IAM 角色和权限配置。同时,它还会把 k6 要用的测试场景和查询上传到 Amazon S3。
5.4. 运行负载测试
基础设施部署完成后,就可以通过 AWS Step Functions 启动测试执行,只需指定要运行的场景即可。
例如,要执行load.json:
aws stepfunctions start-execution \
--state-machine-arn "$(terraform output -raw state_machine_arn)" \
--input '{"scenario": "load", "label": "baseline"}'

Screenshot by the author. AWS Management Console.
状态机编排了三个步骤:
-
PrepareRun: 接收输入中的场景和标签,并基于日期时间生成唯一标识符(
run_id)。后续两个步骤都会用到这个标识符,确保结果和报告对应同一次测试执行。 - RunLoadTest: 在 Amazon ECS Fargate 上启动 k6 容器,执行所选场景,并将结果存入 Amazon S3。
-
GenerateReport: 通过 AWS Lambda 函数处理结果,计算各项指标,并生成 PDF 报告,其中包含 Amazon Bedrock 提供的解读分析。
每次执行都会存入独立的文件夹,目录结构类似
runs/<scenario>/<date>/,同时保存该次测试所用的配置。

Screenshot by the author. Amazon S3 bucket showing the files and results generated by the automated load testing pipeline.
k6 生成的文件(RunLoadTest):
- k6-summary.json: 测试摘要,包括汇总指标(平均值、百分位数、错误率)以及阈值评估结果。
- k6-results.csv: 每个请求的详细数据,包括发送时间、耗时以及是否失败。这些数据用于生成时序图表。
- run-info.json: 执行元数据,包括场景、标签、时间戳、k6 版本和容器镜像。
- scripts/: 所用脚本和场景的完整副本,即使原始文件日后被修改,也能复现当时的测试配置。 #### 由 Report Lambda 生成(GenerateReport):
- metrics.json: 代码基于测试过程中生成的文件计算出的指标。
- ai-analysis.json: Amazon Bedrock 生成的解读分析,以及数值校验结果。
- report.pdf:最终报告,汇总了上述所有信息。 如需运行其他测试场景,只需修改 Step Functions 执行输入中的 scenario 值即可。
5.5. 报告生成
测试完成后,流水线会处理 k6 的结果,并生成一份 PDF 报告,其中既有计算出的各项指标,也包含 Amazon Bedrock 给出的解读。
这一阶段我们使用以容器镜像形式部署的 AWS Lambda 函数,因为需要用到多个 Python 库:pandas 用于数据处理,matplotlib 用于生成图表,fpdf2 用于生成 PDF 文档。
传统的 ZIP 部署方式解压后最多只能 250 MB,而 AWS Lambda 的容器镜像可以做到 10 GB。这样一来,把代码及其依赖打包成可复现的环境就轻松多了,同时还能保留无服务器的执行模式。
下面这张信息图总结了这种做法的主要优势。
容器镜像通过 Dockerfile 构建并推送到 Amazon ECR,随后 Terraform 会配置 AWS Lambda 函数来使用这个镜像,整体流程如下图所示。
生成的 PDF 报告示例可以通过以下链接查看:
github.com 上的 report.pdf

作者使用 Python、Grafana k6 和 Amazon Bedrock 生成的报告。
报告共分多页,各页按以下章节组织:
- 测试上下文:实际执行的场景(负载、时长和阈值)以及测试环境(k6 版本、容器镜像和结果存放位置)。
- 结果与指标:请求数、吞吐量、错误率、p50/p95/p99 延迟、阈值达标情况,以及请求各阶段的耗时分解。
- 性能图表:响应时间随时间变化曲线、延迟分布、百分位数、请求耗时分解、吞吐量、错误数和虚拟用户数。
- AI 驱动的分析:由 Amazon Bedrock 对各项指标进行解读,并对分析中引用的数值加以验证。
- 指标术语表:每个指标的定义、计算方式、示例及其意义。
需要强调的是,所有指标都是用 Python 代码基于 k6 的测试结果计算出来的。Amazon Bedrock 只负责解读这些数值并生成文字分析,另外还有一道验证环节,用来核对生成文本中提到的数字是否准确。
6. 结论
本文从一个问题出发:并发量不断攀升时,API 的表现会如何变化?为了回答这个问题,我们搭建了一条自动化压测流水线,可以在不同负载场景下评估 API 性能并分析结果。
这个实践让我们把一些 站点可靠性工程(SRE)理念落到了实处:先明确要度量服务的哪些方面(SLI),再确定可接受的服务水平(SLO),最后用 Grafana k6 把这些目标转化为测试中可度量的阈值。
除了跑压测,还需要解读大量性能数据。为此我们引入了 生成式 AI 来辅助分析,生成一份总结关键发现的报告,读者无需深入掌握每个指标也能看懂。要强调的是,指标由代码计算,AI 只负责解读,采集到的数据与生成的结论之间保持了清晰的边界。
另一个值得一提的方面是方案的可复用性。只需更换 API、查询语句和压测场景,同一条流水线就能用来评估其他服务。此外,每次执行的配置和结果都会保留下来,既保证了可追溯性,也方便日后对比。
当然,这个 MVP 还有不少局限和改进空间:
- 地理覆盖:测试仅从单一 AWS 区域发起,未必能反映分布在不同地区用户的真实体验。
- 测试时长:短时间的测试场景样本量有限,可能影响 p95、p99 等百分位指标的稳定性。
- AI 解读:分析结果取决于所用的模型和提示词。值得探索的方向是评估不同模型的表现,并根据我们希望获得的洞察类型来优化提示词。
- CI/CD 集成与可观测性:目前这条流水线尚未接入 CI/CD 流程。后续改进方向包括:自动化地在不同应用版本间运行测试,对比结果以发现性能回退,并接入 AWS Lambda 和 Amazon Bedrock 的 Amazon CloudWatch 指标。
归根结底,目标不只是搞清楚 API 能承受多少请求,而是建立一套可复现的流程,让我们能够度量其行为表现、识别潜在的性能退化,并评估每次变更之后性能的演变趋势。
参考资料
- Beyer, B. 等(2016)。《Site Reliability Engineering》,第 4 章:Service Level Objectives。https://sre.google/sre-book/service-level-objectives/
- Grafana Labs(未注明日期)。Grafana k6。https://grafana.com/oss/k6/
- Grafana Labs(未注明日期)。Load test types,Grafana k6 文档。https://grafana.com/docs/k6/latest/testing-guides/test-types/
- Amazon Web Services(未注明日期)。Lambda quotas,AWS Lambda 文档。https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html
- Méndez Escobar, R. E.(2026)。相同还是相似?基于 Amazon Bedrock 和 S3 Vectors 的 AI 商品匹配。DEV Community。https://dev.to/aws-builders/identical-or-similar-ai-powered-product-matching-with-amazon-bedrock-and-s3-vectors-3pm





