ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

OpenMed 服务负载测试与延迟 SLO 门禁实战指南

OpenMed 服务负载测试与延迟 SLO 门禁实战指南 OpenMed 服务负载测试与延迟 SLO 门禁实战指南【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed导读本文以 OpenMed 仓库中 docs/serving/load-testing.md 为骨架完整讲解如何使用仓库内置的 k6 负载测试工具链对本地 OpenMed 服务容器进行压测并通过 p95/p99 延迟、错误率、吞吐量四类 SLO 门禁判断服务是否达标。读完本文你将掌握一键运行混合流量压测/analyze、/pii/deidentify、/pii/extract/stream三路按 4:4:2 加权、按设备分级调整门禁阈值、解读 JSON 报告与失败归因以及如何在 CI 定时任务中复用同一套门禁。一、压测工具链概览k6 场景 Docker 包装器OpenMed 的负载测试不是临时脚本而是一套“合成流量 本地回环 SLO 门禁”的完整工具链代码全部收录在仓库中场景脚本deploy/loadtest/scenario.js —— 基于 Grafana k6 编写负责生成确定性混合流量、采集指标、判定阈值并产出 JSON 报告包装脚本deploy/loadtest/run.sh —— 负责构建镜像、拉起临时容器、等待就绪、预热路由、调用 k6、退出时清理容器服务镜像deploy/docker/Dockerfile —— 基于python:3.11-slim安装 CPU 版 PyTorch 与openmed[hf,service]依赖以uvicorn openmed.service.app:app启动夜间 CI 工作流.github/workflows/loadtest.yml —— 每天 UTC 02:17 定时运行也支持workflow_dispatch手动触发调参。设计红线务必遵守压测夹具只使用合成文本绝不读取生产流量也不接受远程目标。包装脚本会把临时容器绑定到回环地址并且一旦发现LOADTEST_BASE_URL不是回环地址就立即拒绝执行见 deploy/loadtest/run.sh。不要用患者真实文本、凭据或生产 URL 替换合成夹具。合成负载内容与三个被测端点场景脚本内置一段固定合成病历文本SYNTHETIC_NOTE见 deploy/loadtest/scenario.js保证每次运行负载内容完全一致、可复现Taylor Reed, born 1981-02-03, visited Example Clinic for a routine follow-up. Call the fictional records desk at 555-0100.流量按固定权重分发到三个端点确定性混比见 deploy/loadtest/scenario.js权重声明见 deploy/loadtest/scenario.js端点权重请求体要点POST /analyze40%model_namedisease_detection_superclinicalconfidence_threshold0POST /pii/deidentify40%methodmaskmodel_nameOpenMed/OpenMed-PII-SuperClinical-Small-44M-v1confidence_threshold0POST /pii/extract/stream20%流式参数chunk_size128、window_chars256、tokenizer_context_chars64、max_entity_chars128、include_textfalse流量分配采用(__VU __ITER) % 10的取模方式因此负载在保持并发混合的同时是可复现的。三个端点的请求模式Pydantic 校验、批处理、流式分块分别定义在 openmed/service/schemas.py 与路由实现 openmed/service/app.py 中其中流式端点以application/x-ndjson返回逐事件 JSONinclude_textfalse可显著降低响应体大小从而把压测重心放在模型推理耗时本身。服务端前置条件模型预加载与 keep-alive为了让压测结果稳定run.sh在启动容器时通过环境变量预加载两个模型见 deploy/loadtest/run.shOPENMED_SERVICE_PRELOAD_MODELSdisease_detection_superclinical,OpenMed/OpenMed-PII-SuperClinical-Small-44M-v1 OPENMED_SERVICE_KEEP_ALIVE10m对应地服务端 openmed/service/runtime.py 的parse_preload_models()会把逗号分隔的模型名解析、去重、校验后交给ServiceRuntime.preload()在启动阶段就加载进热池warm pool见 openmed/service/warm_pool.py避免压测期间出现“首次加载模型”的冷启动毛刺。/readyz只有在预加载完成后才返回 200否则返回 503见 openmed/service/app.py这是压测包装器等待就绪的判断依据。模型下载只在镜像本地缓存缺失时由服务执行若镜像已内置或预加载过模型压测不会触发网络下载。二、本地一键运行压测前置依赖Dockerdocker命令可用curlk6k6命令可用默认运行在仓库根目录直接执行deploy/loadtest/run.sh包装脚本的完整流程对应 deploy/loadtest/run.sh用deploy/docker/Dockerfile构建镜像标签默认openmed:loadtest以--detach --rm方式启动临时容器把宿主机的127.0.0.1:18080映射到容器内8080轮询http://127.0.0.1:18080/readyz最长等待 900 秒LOADTEST_STARTUP_TIMEOUT_SECONDS期间每 2 秒探测一次预热三个路由/analyze、/pii/deidentify、/pii/extract/stream各发一次合成请求消除冷启动影响运行 k6 场景并强制执行 SLO 阈值退出时含出错强制删除临时容器。由于容器使用--rm且脚本注册了trap cleanup EXIT本地运行结束后不会残留容器报告默认写入系统临时目录因此本地压测不会在仓库里产生任何文件。常用变体跳过构建与预热使用预构建镜像LOADTEST_SERVICE_IMAGEopenmed:local \ LOADTEST_SKIP_BUILD1 \ LOADTEST_WARMUP0 \ deploy/loadtest/run.shLOADTEST_SKIP_BUILD1不重新构建镜像直接用LOADTEST_SERVICE_IMAGE指定的镜像LOADTEST_WARMUP0跳过三路预热请求适合已处于稳定状态的常驻服务。直接压测一个已在回环地址运行的服务绕过包装器自行调用 k6BASE_URLhttp://127.0.0.1:8080 \ LOADTEST_RESULT_FILE/tmp/openmed-loadtest-summary.json \ k6 run deploy/loadtest/scenario.js注意场景脚本同时接受LOADTEST_BASE_URL与短别名BASE_URL见 deploy/loadtest/scenario.js目标地址必须以http://开头且为回环地址。直接运行 k6 时报告路径完全由LOADTEST_RESULT_FILE决定。其他可用包装参数在 deploy/loadtest/run.sh 中还可调整变量默认值说明DOCKER_BINdockerDocker 可执行文件K6_BINk6k6 可执行文件LOADTEST_SERVICE_PORT18080宿主机映射端口须为 1024–65535 的非特权端口LOADTEST_CONTAINER_NAME随机临时容器名LOADTEST_STARTUP_TIMEOUT_SECONDS900就绪等待上限须为正整数OPENMED_PROFILEprod服务 profile透传给OPENMED_PROFILEOPENMED_OFFLINE未设置若已设置则透传给容器开启离线模式三、SLO 门禁阈值如何配置、如何判定k6 通过thresholds强制执行门禁见 deploy/loadtest/scenario.js任何一条阈值被击穿k6 进程即以非零状态退出同时场景脚本的handleSummary()会把“是否通过”显式写入控制台与 JSON 报告deploy/loadtest/scenario.js。环境变量与默认值变量默认值含义LOADTEST_DURATION_SECONDS30测试时长秒LOADTEST_RATE2目标每秒请求数到达率LOADTEST_CONCURRENCY4预分配虚拟用户数LOADTEST_MAX_VUS2 × concurrency虚拟用户上限LOADTEST_SLO_P95_MS30000p95 延迟严格上限毫秒LOADTEST_SLO_P99_MS60000p99 延迟严格上限毫秒LOADTEST_SLO_ERROR_RATE0.05失败请求占比严格上限5%LOADTEST_SLO_MIN_THROUGHPUT_RPS0.5最低达成吞吐量req/s场景脚本还接受短别名SLO_P95_MS、SLO_P99_MS、SLO_ERROR_RATE、SLO_MIN_THROUGHPUT_RPS优先读取长名见 deploy/loadtest/scenario.js 的envValue机制。阈值判定逻辑deploy/loadtest/scenario.jsconst passed p95 SLO_P95_MS p99 SLO_P99_MS errorRate SLO_ERROR_RATE throughput SLO_MIN_THROUGHPUT_RPS;注意差异延迟与错误率是严格小于上限吞吐量是大于等于下限。场景同时保留了 k6 内置指标http_req_duration、http_req_failed、http_reqs与自定义指标loadtest_latency_ms、loadtest_errors、loadtest_requests两套门禁即使日后修改工作负载门禁依然可用。按设备分级调参示例LOADTEST_SLO_P95_MS10000 \ LOADTEST_SLO_P99_MS20000 \ LOADTEST_SLO_ERROR_RATE0.01 \ LOADTEST_SLO_MIN_THROUGHPUT_RPS1 \ LOADTEST_RESULT_FILE/tmp/openmed-laptop-slo.json \ deploy/loadtest/run.sh建议的起步阈值按设备档位文档给出的以下起步值用于帮助按设备档位调参不是性能承诺调门禁前应使用稳定且已预热warmed-up的服务并重复运行同一档位配置取稳定结果设备档位p95p99错误率最低吞吐Nano / 受限 CPU20,000 ms30,000 ms5%0.1 req/s手机 / 小型笔记本10,000 ms15,000 ms2%0.25 req/s笔记本5,000 ms10,000 ms1%0.5 req/s服务器3,000 ms6,000 ms1%1 req/s之所以默认值30s / 30s p95明显宽于各档位是因为仓库默认门禁面向通用 CI 环境而 OpenMed 本身主打“本地优先、端侧运行”的医疗 AI临床 NER 与 PII 脱敏因此按手机、笔记本、服务器等不同算力档位收窄阈值才更有意义。四、报告产物与 JSON 结构k6 运行结束时的handleSummary()会在控制台输出一行摘要同时把结构化报告写入LOADTEST_RESULT_FILE指定的文件。报告为 JSON核心字段如下deploy/loadtest/scenario.js{ schema_version: 1, workload: { name: synthetic-mixed-service-traffic, endpoints: [/analyze, /pii/deidentify, /pii/extract/stream], weights: { analyze: 0.4, deidentify: 0.4, stream: 0.2 } }, requests: 60, throughput_rps: 2.0, p95_ms: 1234, p99_ms: 2345, error_rate: 0.0, slo: { p95_ms: 30000, p99_ms: 60000, max_error_rate: 0.05, min_throughput_rps: 0.5 }, passed: true }控制台摘要同时输出请求数、吞吐量、p95/p99、错误率与SLO status: PASS / FAIL; k6 will exit non-zerodeploy/loadtest/scenario.js。指标以 k6 tagendpoint区分三个端点deploy/loadtest/scenario.js因此如需按端点细看延迟分布可直接在 k6 汇总输出或结合k6的 JSON 扩展查看。五、失败归因与常见排查门禁度量的是服务行为而不是模型质量。一次 FAIL 意味着至少发生以下一种情况见 docs/serving/load-testing.mdp95 或 p99 请求延迟超过配置上限存在非 2xx 响应达成吞吐量低于配置下限服务未就绪/readyz未通过或某个预热路由失败。排查路径打开归档的 JSON 报告对比p95_ms/p99_ms/error_rate/throughput_rps与slo块中的上限/下限确定是哪一项击穿查看容器启动日志run.sh在等待就绪超时时会自动打印容器最近 80 行日志deploy/loadtest/run.sh本地手动排查也可用docker logs 容器名检查模型是否真正预热若阈值收紧后吞吐骤降多半是模型冷加载或 keep-alive 过期后反复重载可增大OPENMED_SERVICE_KEEP_ALIVE或确认OPENMED_SERVICE_PRELOAD_MODELS覆盖了场景中用到的模型保持单容器、单节点前提该压测工作负载按设计是单容器单节点它不是生产容量测试也不能替代单元并发测试工具unit concurrency harness。要在真实集群容量前请使用 deploy/k8s/hpa.yaml、docs/deploy/autoscaling.md 与 docs/serving/backpressure.md 等面向生产规模的资料另行设计。六、夜间 CI 门禁与 PR CI 隔离的独立工作流夜间工作流有意与 PR 级 CI 分离.github/workflows/loadtest.yml定时触发每天17 2 * * *UTC 02:17手动触发workflow_dispatch可在 GitHub 界面用Run workflow显式调参duration、rate、concurrency、四个 SLO 输入调参后立即跑一次验证定时运行的调度触发则使用默认参数与本地默认一致。工作流内容与本地包装器完全一致检出代码 →grafana/setup-k6-action安装 k6 → 构建镜像并以openmed:loadtest-sha为标签 → 运行deploy/loadtest/run.sh→无论 SLO 是否通过都上传loadtest-results/目录产物名为loadtest-slo-run-id保留 30 天。即使门禁失败报告也会被归档便于事后归因。提示用Run workflow调参时只有显式输入的值会被使用未输入的字段回落为默认值工作流内用inputs.xxx || 30形式处理。七、从压测到调参一份可落地的操作清单在目标机器本机或 CI Runner准备好 Docker、curl、k6首次运行直接deploy/loadtest/run.sh用默认门禁验证工具链闭环依据设备档位表选择一组更严格的阈值通过LOADTEST_SLO_*或短别名传入用LOADTEST_RESULT_FILE把报告落到固定路径重复运行同一档位至少两轮取稳定值只有拿到稳定、已预热的数据后再决定是否调整门禁阈值或 keep-alive/预加载配置将最终档位固化到 .github/workflows/loadtest.yml 的workflow_dispatch输入或默认值中纳入夜间门禁持续回归。通过这套工具链你可以把“服务在合成混合流量下是否满足延迟与可用性承诺”变成一个可复现、可归档、可进 CI 的硬性门禁并为手机、笔记本、服务器等不同算力档位分别沉淀各自的 SLO 基线。相关资源压测场景与指标定义deploy/loadtest/scenario.js容器包装与参数解析deploy/loadtest/run.sh服务镜像构建deploy/docker/Dockerfile夜间 SLO 工作流.github/workflows/loadtest.yml被测端点实现openmed/service/app.py请求体 Schema 与校验openmed/service/schemas.py预加载与 keep-alive 解析openmed/service/runtime.py服务端压测配套指南docs/serving/backpressure.md、docs/deploy/autoscaling.md【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表