解读 gVisor 性能数据:从 website/performance 基准 CSV 到 runc/runsc 实测对比
2026/9/13 23:58:24 网站建设 项目流程

解读 gVisor 性能数据:从 website/performance 基准 CSV 到 runc/runsc 实测对比

【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor

导读

本文以仓库中 website/performance 目录为核心,完整解读 gVisor 官方保留的 14 份基准测试数据(CSV),带你逐项看懂 runc 与 runsc 在启动时间、内存密度、CPU、系统调用、网络、文件系统等维度上的真实差距。同时结合 性能指南 中的"结构性成本(structural cost)与实现成本(implementation cost)"分析框架,以及仓库内 test/benchmarks 的现代基准工具链,说明这些数字从何而来、如何解读,以及如何在你自己的机器上复现同类测试。读完本文,你将具备独立评估 gVisor 沙箱性能模型并设计可复现基准实验的能力。

一、性能数据目录:历史基准的官方存档

website/performance/README.md 是对整个website/performance/目录的说明。其核心信息有三点:

  1. 该目录存放的是由 benchmark-tools 仓库生成的 CSV 文件
  2. 原 Python 版 benchmark-tools 已被移除,功能等价的替代实现在仓库内的 test/benchmarks(Go 语言重写,基于标准库testing.B);
  3. 未来这些数据会被自动发布到云存储桶并由网站动态加载,届时该静态目录将被删除。

也就是说,这批 CSV 是 gVisor 在特定历史版本、特定测试环境下的基准快照,具有"官方参考数据"的地位,但也是静态存档。目录中共有 14 个 CSV 文件:

CSV 文件对应基准维度
startup.csv容器启动时间启动
density.csv容器内存占用密度内存
sysbench-cpu.csvCPU 计算吞吐CPU
sysbench-memory.csv内存访问带宽内存
syscall.csv裸系统调用延迟系统调用
redis.csvRedis 典型操作吞吐系统调用/应用
iperf.csv原始网络吞吐网络
applications.csvnode/ruby HTTP 服务网络/应用
httpd100k.csvApache 服务 100k 静态文件文件系统/网络
httpd10240k.csvApache 服务 10MB 静态文件文件系统/网络
fio.csv磁盘顺序/随机 I/O文件系统
fio-tmpfs.csvtmpfs 内存盘 I/O文件系统
ffmpeg.csv视频转码耗时综合
tensorflow.csvCNN 训练耗时综合/CPU

二、CSV 数据格式与指标约定

所有 CSV 均采用扁平的长表结构,前三列语义如下:

  • runtime:被测容器运行时。仓库数据中仅出现runc(原生容器)与runsc(gVisor),syscall.csv额外包含runsc-kvm(KVM 平台);
  • method:基准方法/场景名,例如startup.emptydensity.nodehttp.nodefio.randreadPING_INLINE等。httpd*.csv的首列是connections(并发连接数);
  • metric:指标名,例如startup_time_ms(毫秒)、memory_usage(字节)、transfer_ratelatency(毫秒)、requests_per_secondbandwidth(字节/秒)、syscall_time_ns(纳秒)、run_time(秒)等;
  • result:实测数值。

以 startup.csv 为例:

runtime,method,metric,result runc,startup.empty,startup_time_ms,1193.10768 runc,startup.node,startup_time_ms,2557.95336 runsc,startup.empty,startup_time_ms,1144.1775

可以理解为"runc 启动一个空容器(仅执行true)耗时约 1193ms"这样的句子。注意memory_usage单位为字节、bandwidth单位为字节/秒,阅读时需自行换算。

三、测试方法论:数据是怎么测出来的

解读这些数字前,必须了解 性能指南 中记录的测试前提,否则极易误读:

  • 机器环境:Google Compute Enginen1-standard-4(Broadwell)虚拟机,镜像 Debian GNU/Linux 9(stretch),内核 4.19.0-0,2048GB SSD 持久化启动盘;
  • 默认平台:除特别说明外,所有 runsc 测试均在ptrace 平台上进行。ptrace 无需硬件虚拟化、兼容性最好,但结构性成本最高,不代表理想场景;指南明确指出大多数场景应使用 Systrap 以获得最佳性能;
  • 对照组runc代表原生容器基线,runsc是 gVisor 的 OCI 运行时入口;
  • 数据生成:由 benchmark-tools 仓库产出,即现在仓库内的 test/benchmarks。

指南提出了一个贯穿全文的两分类成本模型,这是理解所有数据的关键:

  • 结构性成本(structural cost):由 gVisor 架构决定、无法通过优化轻易消除的成本。例如 Sentry 本身需要额外内存、应用系统调用必须穿越额外的软件层;以及出于安全设计选用 Go 语言实现 Sentry 而带来的取舍;
  • 实现成本(implementation cost):gVisor 作为系统调用面的独立实现,某些子系统尚未优化到成熟实现(如内核、runc)的同等水平。典型例子是网络栈仍在演进中、CPU 效率相对较低。这类成本是持续改进的对象。

四、逐项解读基准数据

4.1 启动时间(startup.csv)

runtime场景启动时间(ms)
runcstartup.empty1193.11
runscstartup.empty1144.18
runcstartup.node2557.95
runscstartup.node2441.90
runcstartup.ruby2530.13
runscstartup.ruby2455.70

三个场景分别是:执行true的 Alpine 空容器、加载多个模块并绑定 HTTP 端口的 node 应用(以端口收到首个成功请求计时)、行为类似的 ruby 应用。有意思的是,这份历史数据中runsc 的启动时间与 runc 基本持平甚至略快,这是因为绝大部分开销来自 Docker 本身——空容器的 runc 基线就已高达约 1193ms。指南特别提示:若要规避 Docker 开销,可考虑使用runsc do模式或直接调用 OCI runtime。

4.2 内存占用与密度(density.csv)

runtime场景内存占用(字节)
runcdensity.empty4,092,149.76(≈3.9MB)
runscdensity.empty23,695,032.32(≈22.6MB)
runcdensity.node76,709,888(≈73.2MB)
runscdensity.node124,076,605.44(≈118.3MB)
runcdensity.ruby45,737,000.96(≈43.6MB)
runscdensity.ruby106,141,777.92(≈101.2MB)
runcdensity.redis1,055,323,750.4(≈1GB 数据)
runscdensity.redis1,076,686,028.8(≈1GB 数据)

测量方法:运行大量容器实例(一般 50 个,redis 为 5 个),统计主机内存前后差值再除以容器数,而不是直接读取 cgroup 的usage_in_bytes——因为某些运行时(非 runc/runsc)不创建独立容器 cgroup。结论清晰:Sentry 带来一个"小且基本固定"的内存增量(空容器约 18.7MB 的差额),但随应用规模增长,相对占比快速下降(redis 场景仅差约 2%)。对于追求高密度的场景(大量低流量容器),这部分固定开销是评估时的首要考量。

4.3 CPU 与内存访问(sysbench-cpu / sysbench-memory / tensorflow)

基准runcrunsc解读
sysbench CPU(events/s)103.62103.21几乎无差异
sysbench 内存(ops/s)13098.7313107.44几乎无差异
TensorFlow CNN 训练(s)207.11244.47慢约 18%

指南指出:gVisor不模拟、不干扰应用原生执行 CPU 指令,因此 CPU 密集型负载没有运行时开销——sysbench 的 CPU 事件率数据印证了这一点。内存访问上,页错误等 OS 机制虽然经由 Sentry 翻译,但映射一旦安装,后续访问没有额外开销。TensorFlow 示例(卷积神经网络训练)的耗时差异主要来自容器整体启动与运行时间,且该测试同样基于 ptrace 平台。对于数据处理、机器学习等 CPU 密集型负载,runsc 通常只引入极小开销。

4.4 系统调用延迟(syscall.csv)

runtimesyscall_time_ns
runc1939
runsc(ptrace)38219
runsc-kvm763

这是唯一包含 KVM 平台数据的 CSV:测试由自定义二进制执行大量裸系统调用后取平均。ptrace 平台单次系统调用约 38.2μs,是 runc 的约 20 倍;而 KVM 平台仅约 763ns,比 runc 原生还低。这直观展示了平台选择对结构性成本的巨大影响。系统调用开销主要冲击系统调用密集型应用(如高性能数据存储、静态网络服务);应用在用户态做的工作越多,被摊薄的相对影响越小。

4.5 Redis 基准(redis.csv)

操作runc(req/s)runsc(req/s)相对吞吐
PING_INLINE30525.0314528.55≈47.6%
PING_BULK30293.8515627.44≈51.6%
SET30257.1915403.57≈50.9%
GET30312.2115325.67≈50.6%
INCR30525.0315269.51≈50.0%
LPUSH30712.5315172.20≈49.4%
RPUSH30459.9515117.16≈49.6%
LPOP30367.4515257.86≈50.2%
RPOP30665.4415188.33≈49.5%
SADD30030.0315432.10≈51.4%
HSET30656.0415163.00≈49.5%
SPOP29940.1215561.78≈52.0%
LRANGE_10024224.8113365.41≈55.2%
LRANGE_30014302.069520.18≈66.6%
LRANGE_50011728.838248.78≈70.3%
LRANGE_6009900.996544.07≈66.1%
MSET30120.4814367.82≈47.7%

规律非常典型:单次用户态工作量越小的操作(PING/SET/GET)相对损耗越大,而LRANGE这类在应用内部做更多工作的操作相对损耗更小。Redis 在用户态几乎不做事(读 socket→改数据→写回 socket),是系统调用结构性成本最"吃亏"的场景,指南坦言 redis 很可能是长期具有挑战性的性能场景,但优化平台选择也会有显著改观。

4.6 网络(iperf.csv / applications.csv / httpd*.csv)

iperf 原始吞吐:

方向runc(MB/s)runsc(MB/s)
download≈711.9≈610.6
upload≈677.0≈459.9

测试中指定的运行时作为 iperf 客户端(upload)或服务端(download),另一端始终用原生运行时。

HTTP 应用(applications.csv,25 并发,模板渲染):

应用指标runcrunsc
nodetransfer_rate3814.851615.54
nodelatency(ms)1127
noderequests_per_second885.81375.13
rubytransfer_rate2874.381382.71
rubylatency(ms)1838
rubyrequests_per_second539.97259.75

Apache 静态文件服务(httpd100k.csv:单文件 100k;httpd10240k.csv:单文件 10MB,ApacheBench 驱动,指标为 transfer_rate 与 latency(ms)):

并发连接runtime100k 吞吐(KB/s)100k 延迟10MB 吞吐(KB/s)10MB 延迟
1runc565.351ms674.051ms
1runsc282.842ms243.352ms
5runc3260.571ms3089.831ms
5runsc832.693ms981.912ms
10runc4672.011ms4701.201ms
10runsc1095.474ms1135.084ms
25runc4964.142ms5021.362ms
25runsc961.0312ms963.2612ms

指南明确说明:网络性能绝大部分受实现成本约束,且 gVisor 的网络栈正在快速改进;httpd这种在热路径上执行大量文件操作(且所有请求读同一文件、存在内部串行点)的基准,同时叠加了 VFS 实现成本与网络栈问题,结果"predictably poor"(可预期的差),属于最能体现当前实现瓶颈的极端场景。

4.7 文件系统(fio.csv / fio-tmpfs.csv / ffmpeg.csv)

fio(sync 引擎,磁盘):

场景runc(MB/s)runsc(MB/s)
read≈240.6≈240.7
write≈436.6≈411.8
randread≈5.0≈4.2
randwrite≈102.8≈65.9

fio(sync 引擎,tmpfs):

场景runc(MB/s)runsc(MB/s)
read≈4044.1≈2416.3
write≈2889.2≈1151.5
randread≈1164.8≈65.7
randwrite≈997.6≈64.2

ffmpeg 转码 27MB 视频总耗时:runc 82.00s vs runsc 88.24s。

解读要点:

  • 原始磁盘 I/O 上 gVisor 无显著结构性开销:fio.csv 中顺序读写与 runc 几乎持平,因为磁盘本身成为瓶颈;
  • tmpfs 场景放大了 VFS 实现成本:sandbox 内部的 tmpfs 不受磁盘瓶颈约束,操作成本主要由内存拷贝与 VFS 层决定,顺序读写约慢 40%~60%,随机读写差距更大(约 6%~16% 的吞吐保留);
  • 指南指出文件系统方面同样实现成本占主导(内部 VFS 实现需要改进),属于快速改进中的领域;
  • ffmpeg 这类"磁盘 I/O 为主 + 混合计算"的负载中,文件系统开销被摊薄,整体仅慢约 7.6%。

五、在源码中印证基准的实现

数据与结论在仓库源码中有对应实现可查证:

  • 基准工具本身:test/benchmarks/README.md 说明了 Go 版基准的编写范式——使用testing.B、通过 dockerutil 启动容器、用harness.GetMachine()声明所需机器数、以b.ReportMetric()上报自定义指标;
  • fio 基准:test/benchmarks/fs/fio_test.go 中可见BenchmarkFioWrite/Read/RandWrite/RandRead覆盖多种块大小(4KB/64KB/1024KB)、I/O 引擎(sync/libaio)、深度与多 job,并在 bind/tmpfs/rootfs 三类挂载上重复测试;测试要求 root 权限用于harness.DropCaches()清理页缓存;
  • ffmpeg 基准:test/benchmarks/media/ffmpeg_test.go 展示了"尊重b.N"的典型写法——每次迭代新建容器执行ffmpeg -i video.mp4 -c:v libx264 -preset veryslow output.mp4,计时只覆盖容器运行段;
  • 其他基准目录:network(httpd/iperf/nginx/node/ruby)、database(redis)、tcp(含 nsjoin/tcp_proxy/xdp 等);
  • 基准镜像:Dockerfile 统一存放在 images/benchmarks 下,约定使用显式版本化的包,且不在镜像内写 ENV/CMD(由 API 层dockerutil.RunOpts传入)。

六、在本机复现基准测试

结合 Makefile 与 test/benchmarks/README.md,可按下述步骤复现(前提:Docker 17.09.0+、仓库构建环境、root 权限):

  1. 安装 runsc 作为 Docker runtimemake dev(会将多个 runsc 配置写入/etc/docker/daemon.json,请选择不带 debug 的配置);
  2. 运行单个基准
make run-benchmark RUNTIME=[RUNTIME_FROM_DAEMON.JSON/runc] BENCHMARKS_TARGETS=path/to/target
  1. 一键多平台对比(systrap、kvm 与原生 runc 同跑):
make benchmark-platforms BENCHMARKS_TARGET=path/to/target
  1. 列出全部可用基准
bazel query 'attr("tags", ".*gvisor_benchmark.*", //test/benchmarks/...)'

Makefile 中可调的关键变量(Makefile):

变量默认值作用
BENCHMARKS_TARGETS//test/benchmarks/media:ffmpeg_test基准目标
BENCHMARKS_FILTER.测试套件过滤正则
BENCHMARKS_OPTIONS-test.benchtime=30s传给go test的基准参数(如-test.benchtime=1m-test.benchtime=10x
BENCHMARKS_RUNCtrue是否同时跑 runc 对照
BENCHMARKS_PLATFORMS只针对指定平台列表运行
BENCHMARKS_UPLOADfalse是否将结果解析并上传 BigQuery(配合BENCHMARKS_PROJECT/DATASET/TABLE/SUITE/OFFICIAL
BENCHMARKS_PROFILE性能剖析选项,如-pprof-dir=/tmp/profile -pprof-cpu -pprof-heap

关于性能剖析,注意两点:运行时需以--profile标志启用(该标志会放宽 seccomp 过滤器以便写盘,不建议用于生产);剖析输出位于/tmp/profile,且 runc 不支持剖析。

Go 基准的基本骨架(摘自 test/benchmarks/README.md):

func BenchmarkMyCoolOne(b *testing.B) { machine, err := harness.GetMachine() // check err defer machine.CleanUp() ctx := context.Background() container := machine.GetContainer(ctx, b) defer container.CleanUp(ctx) b.ResetTimer() // Respect b.N. for i := 0; i < b.N; i++ { out, err := container.Run(ctx, dockerutil.RunOpts{ Image: "benchmarks/my-cool-image", Env: []string{"MY_VAR=awesome"}, }, "sh", "-c", "echo MY_VAR") // check err... b.StopTimer() // Do parsing and reporting outside of the timer. number := parseMyMetric(out) b.ReportMetric(number, "my-cool-custom-metric") b.StartTimer() } } func TestMain(m *testing.M) { harness.Init() os.Exit(m.Run()) }

写作基准时需遵守三条约定:尊重并随b.N线性扩展(用户可用--benchtime=10x--benchtime=1m控制迭代);用b.ReportMetric()上报自定义指标;客户端-服务端型基准(如 httpd)把b.N作为客户端容器发起请求数的参数。

七、阅读这些数据的注意事项

  • 平台前提:绝大多数 runsc 数据基于 ptrace 平台,这是结构性成本最高的平台。从 syscall.csv 可见,KVM 平台系统调用延迟比 runc 还低;指南建议大多数场景使用 Systrap 以获得最佳性能。因此这批数据不能代表 runsc 在最优平台上的表现
  • 版本与时效:CSV 是历史快照(对应已移除的 Python benchmark-tools 时代,机器为 n1-standard-4/Debian 9 内核 4.19),不代表当前版本性能;网络栈与 VFS 等实现成本正在持续改进;
  • 负载匹配:沙箱并非对所有负载都适用——例如可信数据库本就把用户数据置于沙箱内,攻破沙箱并无额外收益,这类场景沙箱价值有限;
  • 成本归属:系统调用、内存、密度数据主要体现结构性成本(长期存在但可因平台优化而改善);网络、VFS、tmpfs 随机 I/O 主要体现实现成本(正在被持续优化)。区分二者有助于判断"该不该投入优化、优化空间在哪"。

总结

website/performance 目录的 14 份 CSV 是理解 gVisor 性能模型的一手素材:CPU 与内存访问几乎无开销、密度成本小且固定、系统调用与网络/文件系统是实现改进的主战场、平台选择(ptrace/KVM/Systrap)对结构性成本影响巨大。结合 性能指南 的成本分析框架与 test/benchmarks 的 Go 基准工具链,你既可以读懂官方历史数据,也可以按本文给出的 Makefile 命令在目标硬件上复现并评估适合自身负载的性能结论。

【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询