zeroserve v0.2.11: Caddy 호환성 및 eBPF-to-Native JIT 컴파일

zeroserve v0.2.11은 사용자가 Caddyfile을 제공하면 서버가 이를 eBPF로 JIT 컴파일한 다음 x86_64 또는 ARM64 네이티브 머신 코드로 컴파일하는 Caddy 호환 모드를 도입합니다. 이 프로세스를 통해 서버는 io_uring 이벤트 루프 내에서 구성 로직을 실행할 수 있으며, 이는 기존의 인터프리터 방식이나 고수준 서버 구성과 비교하여 성능을 크게 향상시킵니다.

성능 벤치마크

zeroserve는 HTTPS 리버스 프록시 시나리오에서 Caddy보다 상당한 성능 향상을 보여줍니다. 두 개의 스레드를 사용하는 AMD Ryzen 7 3700X에서 수행된 테스트에서, zeroserve(clang 컴파일러 사용)는 약 38,948 req/s를 달성한 반면, Caddy는 12,529 req/s를 기록했습니다.

주요 성능 지표는 다음과 같습니다:

Protocol Server Throughput p50 Latency p99 Latency Peak RSS
HTTPS zeroserve-clang 38,948 req/s 1.45ms 3.91ms 30.9 MiB
HTTPS zeroserve-tcc 36,653 req/s 1.67ms 4.00ms 34.2 MiB
HTTPS Caddy 12,529 req/s 4.74ms 13.11ms 67.4 MiB
HTTPS Nginx 37,424 req/s 1.57ms 4.24ms 25.7 MiB

이 결과는 zeroserve-clang이 Caddy보다 약 3배의 처리량을 제공하며 p99 지연 시간을 70% 감소(3.91ms vs 13.11ms)시킨다는 것을 나타냅니다.

eBPF 미들웨어를 통한 확장성

zeroserve는 사용자 공간에서 튜링 완전한 eBPF를 실행하므로, Caddyfile을 통해 요청 파이프라인에 커스텀 네이티브 코드를 직접 통합할 수 있습니다. 사용자는 eBPF 플러그인을 로드하고 zeroserve_call 지시어를 사용하여 해당 플러그인 내의 특정 메서드를 호출할 수 있습니다.

예를 들어, S3 호환 버킷에 대한 AWS SigV4 인증을 구현하려면 플러그인(io.su3.aws-sigv4.c)을 로드하고, 요청이 리버스 프록시되기 전에 서명하도록 route 블록 내에서 zeroserve_call 지시어를 활용해야 합니다.

기술적 구현 및 커뮤니티 피드백

아키텍처

zeroserve의 핵심 혁신은 고수준 구성(Caddyfile) $\rightarrow$ eBPF $\rightarrow$ 네이티브 머신 코드 변환입니다. 이를 통해 서버는 유연한 구성 형식을 유지하면서 비동기 I/O를 위해 io_uring의 효율성을 활용할 수 있습니다.

커뮤니티 비판

기술 사용자들 사이의 논의에서는 zeroserve 도입과 관련된 몇 가지 중요한 트레이드오프와 우려 사항이 강조되었습니다:

  • 기능적 동등성: 비판론자들은 "Caddy 호환성"이 구성 구문에 국한되며, 자동 ACME(SSL/TLS 인증서 관리) 및 광범위한 플러그인 생태계와 같은 필수적인 Caddy 기능을 포함하지 않는다고 지적합니다.
  • 보안 우려: io_uring과 JIT 컴파일의 사용은 보안 경보를 불러일으켰습니다. 일부 사용자는 최근의 보안 권고 사항으로 인해 io_uring을 사용하여 서비스를 노출하는 것이 위험하다고 주장하는 반면, 다른 이들은 JIT 컴파일에 의해 생성되는 공격 표면이 "매우 방대하다"고 제안합니다.
  • 실용적 유용성: 여러 논평가들은 Caddy의 성능이 대다수 사용자의 병목 현상인지 의문문을 던지며, 전문화된 JIT 컴파일 서버로 전환하는 것보다 수평적 확장이 더 실용적인 해결책이라고 주장합니다.
  • Nginx와의 비교: 벤치마크 결과 Nginx가 처리량(37,424 req/s) 면에서 zeroserve-clang과 거의 대등하며, 안정성과 지원 측면에서 장기적인 기록을 유지하고 있다는 점을 보여줍니다.

Sources