KPanel 运行时性能基线
July 28, 2026 · View on GitHub
测试日期:2026-07-28
测试提交:7ea8eaf
测试版本:0.24.0-perf
结论
当前版本适合单管理员日常使用,常规只读请求吞吐和稳定性通过;但在
256 MiB 容器限制下,并发密码校验会触及 cgroup 内存上限,发布前应先调整
登录哈希并发或容器内存策略。
| 项目 | 结论 |
|---|---|
| 常规页面和会话 API | 通过,10 并发约 4,400 req/s,无错误 |
| Agent 实时采集 | 单用户可用;并发时重复采集导致延迟和内存上升 |
| 登录 | 单次约 225 ms;双并发内存余量不足;第三路会被 429 限流 |
| 长连接 | 100 个空闲 Keep-Alive 连接未增加可见 RSS,关闭后 FD 正常回收 |
| 前端传输 | 资源已拆包并支持 304,但直连模式未启用 gzip/Brotli |
| 镜像和持久化 | 镜像约 7.92 MiB;只读压测未造成异常数据增长 |
本轮总体判定为有条件通过。阻断项是登录内存边界,不是常规 API 吞吐。
测试边界
测试使用当前源码构建独立 Panel 镜像和同版本 Agent,不复用线上 Panel 数据:
- 验收机:Debian 13、8 vCPU、约 7.76 GiB RAM、Docker 29.6.2;
- Panel:仅监听
127.0.0.1:18080,限制1 CPU / 256 MiB / 128 PIDs; - Agent:独立 socket、token 和状态目录,只执行 GET 观测;
- 压测从同一宿主机发起,结果不包含公网延迟;
- 未执行安装、更新、备份、还原、卸载等会改变宿主机状态的任务;
- 活跃交互终端依赖真实脚本任务,本轮只验证连接保持和代码中的长轮询边界。
因此,本报告是 Panel/Agent 运行时基线,不替代 LDNMP、应用安装和公网链路的 专项实机验收。
请求基准
共执行 22 组、114,050 次 GET 请求,累计响应约 67.97 MiB,
所有请求均完成,HTTP 错误和客户端错误均为 0。
| 接口 | 并发 | 请求数 | 吞吐 req/s | 平均 ms | P95 ms | P99 ms |
|---|---|---|---|---|---|---|
/api/v1/health | 1 | 20,000 | 2,290.27 | 0.429 | 0.641 | 1.121 |
/api/v1/health | 10 | 20,000 | 4,394.92 | 2.266 | 5.218 | 7.370 |
/api/v1/health | 50 | 20,000 | 4,309.13 | 11.567 | 28.008 | 39.832 |
/api/v1/auth/session | 10 | 10,000 | 4,510.28 | 2.208 | 4.973 | 6.952 |
/ | 20 | 10,000 | 3,715.86 | 5.364 | 13.349 | 20.117 |
/api/v1/sites | 10 | 2,000 | 3,110.91 | 3.191 | 6.258 | 15.771 |
/api/v1/capabilities | 1 | 100 | 10.01 | 99.901 | 117.884 | 128.505 |
/api/v1/capabilities | 10 | 100 | 51.29 | 189.674 | 240.498 | 247.327 |
/api/v1/system/summary | 1 | 100 | 6.14 | 162.927 | 165.618 | 170.002 |
/api/v1/system/summary | 10 | 100 | 57.69 | 170.529 | 186.452 | 188.301 |
/api/v1/docker/summary | 10 | 1,000 | 677.03 | 14.735 | 18.605 | 22.304 |
/api/v1/docker/containers | 1 | 100 | 11.91 | 83.926 | 96.975 | 102.882 |
/api/v1/docker/containers | 10 | 100 | 34.42 | 290.008 | 304.474 | 309.570 |
/api/v1/apps | 1 | 50 | 10.32 | 96.885 | 110.876 | 124.234 |
/api/v1/apps | 10 | 50 | 29.20 | 339.428 | 369.935 | 371.257 |
/api/v1/system/summary 包含固定 150 ms CPU 采样窗口,因此约 163 ms
的单请求耗时符合当前实现。Docker 容器列表会逐容器读取详情,应用列表还会
组合完整应用目录,所以二者在并发下存在明显的重复工作。
CPU、内存、线程与 FD
常规只读负载
- Panel 稳态 RSS 约
15–17 MiB,线程8–9,FD8–10; - Agent 空闲 RSS 约
20–21 MiB; - Agent 在并发实时采集期间峰值 RSS 约
91.7 MiB,线程由13增至17,FD 由7增至12; - 压测结束后 Agent RSS 先降至约
55 MiB,数分钟后约40.6 MiB, 未观察到 FD 泄漏; - 主要 GET 套件中,Panel 消耗约
23.8 CPU 秒,Agent 约25.3 CPU 秒; - 8 核宿主机 load 由
0.09升至3.19,测试期间无请求失败。
启动
5 次冷启动到健康检查成功:
- 平均
606.4 ms; - 最快
524 ms; - 最慢
661 ms; - 启动 RSS 约
76.2–79.2 MiB。
每次启动都会生成 Argon2 假密码哈希,以避免用户名枚举造成的时序差异。 这会产生约 64 MiB 的安全性相关瞬时内存,不是页面业务泄漏。
登录内存边界
默认 Argon2 参数为 64 MiB / 3 iterations / 4 threads,登录哈希并发上限为
2:
| 场景 | 结果 |
|---|---|
| 单并发,5 次 | 全部 200,平均约 224.74 ms |
| 双并发,6 次 | 全部 200,平均约 542.98 ms |
| 三并发,6 次 | 2 次 200、4 次 429,限流生效 |
风险事实:
- 混合顺序和并发登录后,cgroup
memory.peak到达256 MiB上限; memory.events.max增长,说明内核已经进行限额回收;- 本轮
oom=0、oom_kill=0,容器没有退出; - 高 RSS 在 20 秒空闲和短时并发健康请求后仍未立即回落;
- 两轮各 20,000 次单连接健康请求触发 Go GC/回收后,RSS 最终回到约
17.1 MiB。
这不是已发生 OOM,但在当前 256 MiB 限制下缺少可靠安全余量。
空闲连接
同时保持 100 个 HTTP Keep-Alive 连接 20 秒:
- Panel FD 从
8增至108; - RSS 保持约
17.3 MiB; - 线程保持
8; - 连接关闭后 FD 回到
8。
当前交互终端采用最长 1 秒的有界长轮询,不是永久 WebSocket。真实脚本输出和 输入延迟仍应随安装、建站、体检任务做专项验收。
前端和网络传输
生产镜像大小为 8,306,670 bytes,Web 静态目录为
2,105,348 bytes / 219 files。
| 资源 | 原始大小 | gzip 后参考值 |
|---|---|---|
| 主 JS | 144,073 B | 53,894 B |
| 主 CSS | 55,755 B | 10,598 B |
| Overview 路由 | 478,802 B | 90,893 B |
| 交互终端路由 | 343,900 B | 87,410 B |
直连 Panel 验证结果:
- JS、CSS 和
/api/v1/apps在客户端声明Accept-Encoding: gzip后仍返回 原始内容,没有Content-Encoding; /api/v1/apps单次响应约196,868 B;- 哈希静态资源缓存时间为 1 小时;
- 静态资源带
Last-Modified,条件请求正确返回304。
如果前置反向代理开启压缩,可补偿直连模式的传输开销;IP + 端口直连不会获得 该收益。
优化优先级
P0:收紧登录内存边界
在以下方案中选择并实测一种,不能只修改配置后推测:
256 MiB限额下将密码哈希并发降为 1;- 保留双并发,但提高 Panel 内存限额并设置合理的
GOMEMLIMIT; - Argon2 参数调整只能在完成安全评估后考虑,不能单纯为省内存降低强度。
验收应断言双登录、连续登录及登录后普通 API 负载均不产生
memory.events.max,并保留 oom_kill=0。
P1:合并 Agent 重复采集
- 对
capabilities、Docker 列表和应用清单增加 1–3 秒短缓存; - 对并发相同 GET 使用 singleflight,避免同一时刻重复扫描;
- 消除容器列表中的逐容器 inspect,或使用有界并发与短缓存;
system/summary可共享后台 CPU 采样结果,避免每个请求固定等待 150 ms。
缓存只能减少重复观测,写操作完成后必须失效,不能成为第二套业务状态。
P1:压缩与静态缓存
- 为文本静态资源和较大的 JSON 响应启用 gzip/Brotli;
- 对带内容哈希的静态文件使用长缓存和
immutable; - 保留
index.html的no-cache; - 同时验证域名反代和 IP + 端口两种访问方式。
P2:前端载荷
- 优先分析 Overview
478,802 B路由包; - 交互终端已经按路由懒加载,保持该边界;
- 应用清单内容较大,先压缩,再评估是否需要摘要/详情拆分,避免过早引入第二套 状态接口。
复跑
仓库提供无第三方依赖的只读基准工具:
python3 scripts/runtime-benchmark.py \
--base-url http://127.0.0.1:8080 \
--path /api/v1/health \
--requests 20000 \
--concurrency 10
认证接口使用 curl Netscape cookie jar:
python3 scripts/runtime-benchmark.py \
--base-url http://127.0.0.1:8080 \
--path /api/v1/auth/session \
--cookie-file ./cookies.txt \
--requests 10000 \
--concurrency 10
正式发布验收应在干净的 Debian、Ubuntu 和 Rocky/AlmaLinux 主机上复跑,并增加 实际安装、终端长轮询、备份/还原、断网和低内存场景。