zeroserve: Userspace eBPF로 스크립트 작성이 가능한 Zero-Config 웹 서버

zeroserve는 전통적인 선언적 설정을 샌드박스화된 userspace eBPF 프로그램으로 대체하는 고성능, zero-config HTTPS 서버입니다. 라우팅, 인증, 속도 제한(rate-limiting)을 하나의 스크립트 가능한 경로로 통합함으로써, Nginx 및 Caddy에서 볼 수 있는 설정 계층보다 더 투명하고 유연한 대안을 제공하는 것을 목표로 합니다.

Userspace eBPF를 통한 Program-as-Configuration

zeroserve는 전통적인 설정 파일을 제거합니다. 대신, 서버는 eBPF 프로그램을 주요 설정 수단으로 사용합니다. .zeroserve/scripts/ 디렉토리에 배치된 모든 .c 파일은 eBPF 객체로 컴파일되어 모든 요청에 대해 실행됩니다.

커널 권한 없이 보안과 안정성을 보장하기 위해, zeroserve는 다음과 같은 아키텍처를 구현합니다:

  • Userspace Execution: 스크립트는 async-ebpf 런타임(uBPF를 제공함)을 통해 실행되므로, 권한이 없는 프로세스 내에서 작동하며 CAP_BPF나 커널 BPF 서브시스템 접근이 필요하지 않습니다.
  • Memory Sandboxing: "pointer cage"가 모든 메모리 접근을 프로그램 자체의 아레나(arena)로 마스킹하여, 스크립트가 허가되지 않은 메모리를 읽거나 쓰는 것을 방지합니다.
  • Preemptible Runtime: 단일 느린 스크립트가 이벤트 루프를 중단시키는 것을 방지하기 위해, 런타임은 완전한 선점형(preemptible) 방식입니다. 타이머(기본값 2ms)가 JIT 컴파일된 네이티브 코드를 중단시키고 제어권을 서버로 반환할 수 있습니다.
  • Execution Flow: 스크립트는 파일 이름 정렬 순서로 실행됩니다. 요청별 메타데이터 맵을 공유하며, 스크립트가 zs_respond 또는 zs_reverse_proxy를 호출하면 체인이 단축(short-circuited)될 수 있습니다.

고성능 I/O 및 배포

zeroserve는 monoio 런타임을 기반으로 구축되었으며, 모든 네트워크 및 디스크 작업에 io_uring을 활용합니다. CPU 코어 전체에 걸쳐 여러 프로세스를 실행하여 확장할 수 있도록 설계된 단일 스레드 이벤트 루프 방식으로 작동합니다.

원자적 Tarball 배포 (Atomic Tarball Deployment)

서버는 단일 tar 파일에서 웹사이트를 직접 서비스합니다. 로드 시, zeroserve는 path -> byte-range 맵을 구축하고 파일을 디스크에 압축 해제하지 않고 tarball에 대해 byte-range 읽기를 수행합니다. 이 방식은 다음과 같은 장점을 보장합니다:

  • Atomic Updates: 새 버전을 배포하는 것은 간단한 파일 교체 후 SIGHUP 신호를 보내는 것입니다.
  • Security: 파일 시스템에 문서 루트(document root)가 없으므로, 잘못된 설정 규칙이 민감한 파일을 노출할 위험이 없습니다.
  • Hot Reloading: SIGHUP은 연결을 끊지 않고 사이트 콘텐츠, eBPF 스크립트 및 TLS 자료를 원자적으로 교체합니다.

전송 보안 (Transport Security)

서버는 BoringSSL을 통해 포괄적인 TLS 1.3 구현을 포함하며, 다음과 같은 기능을 제공합니다:

  • Encrypted Client Hello (ECH): 실제 SNI가 평문으로 나타나는 것을 방지합니다.
  • JA4 Fingerprinting: 클라이언트 지문(fingerprint)이 분석을 위해 eBPF 스크립트에 노출됩니다.
  • SNI Certificate Selection: 디렉토리에서 인증서를 동적으로 선택합니다.

성능 벤치마크

8-core Ryzen 7 3700X (단일 코어에 고정) 환경에서 Nginx 1.26 및 Caddy 2.11과 비교한 벤치마크 결과, zeroserve는 특정 워크로드에서 상당한 이점을 보여줍니다.

정적 파일 서비스 (Static File Serving)

작은 정적 파일(174 B)의 경우, zeroserve는 36,681 req/s를 달성하여 Nginx(31,226 req/s) 및 Caddy(12,830 req/s)보다 뛰어난 성능을 보여주었습니다. 큰 파일(100 KB)의 경우, zeroserve와 Nginx는 유사한 성능을 보냈으며, 둘 다 약 780 MB/s의 처리량을 보였습니다.

스크립팅 처리량 (eBPF vs. Lua)

Nginx와 LuaJIT를 비교했을 때, zeroserve의 eBPF 스크립트(10ms 선점 간격으로 조정됨)는 미들웨어 및 동적 응답 시나리오 모두에서 Lua보다 뛰어난 성능을 보였습니다:

  • Header Injection: zeroserve (43,709 req/s) vs. Nginx Lua (28,653 req/s).
  • Dynamic JSON Response: zeroserve (43,709 req/s) vs. Nginx Lua (41,231 req/s).

리버스 프록시 성능 (Reverse Proxy Performance)

작은 프록시 응답(174 B)의 경우, zeroserve는 26,486 req/s를 달성하여 Nginx(21,761 req/s)보다 약 22% 더 빨랐습니다. 그러나 큰 프록시 본문(100 KB)의 경우, Nginx가 585 MB/s를 달성하여 zeroserve의 359 MB/s보다 우수한 성능을 유지했습니다.

커뮤니티 인사이트 및 비판

기술 사용자들 사이의 논의는 프로젝트의 설계 및 생존 가능성에 관한 몇 가지 쟁점을 강조합니다:

"I think the bet is- misplaced - people prefer configuration over code and long have. A built-ins meet enough peoples needs entirely and they don't need to write C code."

다른 기여자들은 C 대신 eBPF 스크립트를 위한 Rust (.rs) 파일 지원, 그리고 userspace에서 SSL을 처리하는 것을 피하기 위해 kTLS를 통합하는 등의 잠재적인 개선 사항과 기술적 추가 사항을 제안했습니다. 일부 사용자들은 프로젝트의 "vibe coded" 성격과 발표 시 AI 생성 콘텐츠 사용에 대해 회의적인 시각을 보였이며, 소프트웨어 경화화(hardening) 및 품질 보증(QA) 수준에 대해 의문을 제기했습니다.

Sources