返回

文章详情

Zeroserve:一个可以使用eBPF进行脚本编写的零配置Web服务器

Hacker News2026年6月6日 14:59

免责声明:本文与GPT-5.5和Claude Opus 4.8合作撰写。zeroserve是一个小巧、快速的零配置HTTPS服务器。你只需将网站的tar包交给它,它就会通过HTTP/2和TLS 1.3进行服务,支持热重载,并且占用极小的内存占用。特别之处在于,你可以将eBPF程序放入tar包中,它们会在每个请求上运行,在用户空间中作为沙盒中间件——重写、验证和限流请求,或者在需要时将它们反向代理到后端,以便其在你的应用前充当网关。简而言之:快速:在一个核心上,它在大多数工作负载中超过nginx——小文件和大静态文件、脚本中间件以及小响应代理,所有这一切都通过HTTPS进行。高效的eBPF脚本:脚本被即时编译为本地代码,并在用户空间中沙盒执行,足够便宜,可以在每个请求上运行。程序即配置:你的eBPF程序就是全部的配置,决定每个请求将发生什么。全面使用io_uring:所有网络和磁盘操作都通过io_uring提交。现代TLS内置:TLS 1.3、HTTP/2、加密客户端Hello、SNI证书选择和JA4指纹识别。简单操作:从一个tar包服务整个网站,并可以通过SIGHUP热重载它(以及TLS材料)。它旨在作为nginx和Caddy的替代品,其设计赌注在于配置。这些服务器提供声明性的配置语言——位置块、重写规则、映射指令、try_files——然后,一旦声明性语言达到其极限,选择性的脚本运行时与之并联(Lua或Caddy的插件)。行为最终分为两个层次:默默自我扩展的指令,加上在请求生命周期中的某个位置运行的脚本,你必须在脑海中保持。zeroserve将其合并为一件事。没有配置文件。eBPF程序就是配置——一个普通的沙盒程序,看到每个请求并决定发生什么:路由、头部、认证、限速、代理。我希望整个请求路径在一个我可以从头到尾阅读的程序中。一个tar包,地点服务整个网站。整个网站是一个单一的tar文件。zeroserve在加载时对其进行索引——构建一个路径->字节范围映射——然后通过对tar包本身发出字节范围读取来提供文件。没有任何东西被解压到磁盘。网站完全存在于那个文件中,因此没有多余地点规则暴露的文档根,而部署则是单个原子文件交换。要打包目录:zeroserve --pack ./public > site.tar zeroserve --addr 0.0.0.0:8080 site.tar 部署新版本是“替换tar包并发送SIGHUP”。重载原子性地交换网站、脚本和TLS材料,在同一进程中,且不会掉线:killall -SIGHUP zeroserve 所有网络和磁盘I/O都通过io_uring(通过monoio运行时)进行。每个实例是一个单线程事件循环。这听起来像是一个局限性,而且对于每个进程确实如此——但当你的扩展单位是“更多的进程”时,这是一种适当的形状,正因如此,它们可以在一台机器上快乐共存。使用eBPF进行用户空间的脚本,这是我觉得最有趣的部分。任何你放在.zeroserve/scripts/下的.c文件在打包时都会被编译为eBPF对象(通过clang和llc),并在每个请求上运行。eBPF完全在用户空间中运行:zeroserve将字节码加载到自己的普通、非特权进程中的一个运行时(async-ebpf),因此内核的BPF子系统和CAP_BPF都避开了它。async-ebpf将字节码即时编译为本地机器代码(它包含uBPF),因此你的“配置”作为本地x86-64运行。一个指针笼完成内核验证程序通常会做的工作,防止程序读取或写入它不该访问的内存:即时编译代码中的每个内存访问都被掩盖到了程序自己的区域,因此多余的访问保持在脚本自己的内存中。该脚本直接在zeroserve的单个事件循环上运行。为了防止一个缓慢的脚本卡住其他连接,运行时是完全可抢占的:定时器可以在执行中断即时编译的本地代码并将控制权交还给事件循环。编程模型是一个脚本链,按文件名排序运行,分享每个请求的元数据映射。如果脚本调用zs_respond或zs_reverse_proxy,链条将中断。以下是一个首先运行并丰富每个请求的脚本: #include <zeroserve.h> ZS_ENTRY zs_u64 entry(void) { char peer[64]; if (zs_req_peer(peer, sizeof(peer)) <= 0) zs_strcpy(peer, "unknown"); // 为HTML模板发布值 zs_meta_set(ZS_STR("visitor"), ZS_STR(peer)); // 将一个头部附加到*每个*响应:静态文件、zs_respond、代理 zs_meta_set(ZS_STR("zs.response.header.x-served-by"), ZS_STR("zeroserve-ebpf")); return 0; } 它设置的元数据有两个作用。zs.response.header.*下的键成为所有内容的响应头。而其他键则为一个小模板传递提供了数据:<zs-meta>visitor</zs-meta>

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡