본문으로 건너뛰기

© 2026 Molayo

GeekNews헤드라인2026. 06. 07. 09:17

Zeroserve: eBPF로 스크립팅할 수 있는 무설정 웹 서버

요약

Zeroserve는 eBPF 프로그램을 사용하여 설정 파일 없이 요청을 처리하는 무설정 HTTPS 서버입니다. 단일 tarball 내에 정적 파일과 스크립트를 포함하며, io_uring 기반의 고성능 네트워크 처리를 지원합니다.

핵심 포인트

  • eBPF를 활용한 사용자 공간 샌드박스 미들웨어 구현
  • 설정 파일 대신 프로그램 코드로 라우팅 및 인증 처리
  • 단일 tarball 배포 및 SIGHUP을 통한 원자적 핫 리로드
  • io_uring 및 monoio 런타임을 통한 고성능 네트워크 I/O
  • 작고 빠른
    HTTPS 서버인 zeroserve는 웹사이트 tarball을 받아 HTTP/2와 TLS 1.3으로 제공하고, tarball 안의 eBPF 프로그램을 사용자 공간 샌드박스 미들웨어로 요청마다 실행함
  • 구성 파일 없이 eBPF 프로그램이 요청별 라우팅, 헤더, 인증, 속도 제한, 프록시를 결정해 nginx·Caddy의 선언형 설정과 별도 스크립팅 계층을 하나로 합침
  • 사이트는 단일
    tar 파일로 인덱싱되고 디스크에 풀리지 않으며, tarball 교체와 SIGHUP

으로 사이트·스크립트·TLS 자료를 연결 손실 없이 원자적으로 교체함

  • 단일 코어 HTTPS 벤치마크에서 zeroserve는 소형 정적 파일 36,681 req/s, 10ms eBPF 동적 JSON 46,945 req/s, 소형 프록시 26,486 req/s를 기록했지만,
    100KB 프록시는 nginx가 5,882 req/s로 우위임
  • zeroserve는 nginx와 Caddy의 대안을 목표로 단일 tarball 배포, 프로그램형 설정, 사용자 공간 eBPF, 현대적 TLS를 결합하지만, 큰 프록시 응답에는 nginx가 더 적합함

개요

  • zeroserve는 웹사이트 tarball 하나를 HTTP/2와 TLS 1.3으로 제공하는 작고 빠른 무설정 HTTPS 서버임
  • tarball 안에 넣은 eBPF 프로그램은 모든 요청에서 사용자 공간 샌드박스 미들웨어로 실행되며, 요청 재작성, 인증, 속도 제한, 백엔드 리버스 프록시 처리 가능
  • 단일 코어 기준으로 소형·대형 정적 파일, 스크립트 미들웨어, 소형 응답 프록시 대부분의 워크로드에서 nginx보다 높은 성능을 보이는 목표의 서버임
  • eBPF 스크립트는 네이티브 코드로 JIT 컴파일되고 사용자 공간에서 샌드박싱되며, 요청마다 실행할 만큼 낮은 비용을 목표로 함
  • 네트워크와 디스크 작업은 monoio 런타임을 통해
    io_uring

으로 제출됨

  • TLS 1.3, HTTP/2, Encrypted Client Hello, SNI 인증서 선택, JA4 핑거프린팅 지원
  • 전체 사이트와 TLS 자료는 tarball 하나에서 제공되며,
    SIGHUP

으로 핫 리로드 가능

구성 모델: 프로그램이 곧 설정

  • zeroserve는 nginx와 Caddy의 대안을 목표로 하며, 핵심 설계 선택은 구성 방식임
  • nginx와 Caddy는
    location

블록, rewrite

규칙, map

지시어, try_files

같은 선언형 설정 언어를 제공하고, 한계에 도달하면 Lua나 Caddy 플러그인 같은 선택적 스크립팅 런타임을 옆에 붙이는 구조임

  • 그 구조에서는 동작이 자체 제어 흐름을 가진 지시어 계층과 요청 생명주기의 특정 지점에서 실행되는 스크립트 계층으로 나뉨
  • zeroserve에는 구성 파일이 없으며, eBPF 프로그램 하나가 모든 요청을 보고 라우팅, 헤더, 인증, 속도 제한, 프록시를 결정함

단일 tarball을 그대로 제공

  • 전체 사이트는 하나의
    tar

파일이며, zeroserve는 로드 시 path -> byte-range

맵을 만들고 tarball 자체에 바이트 범위 읽기를 수행해 파일 제공

  • 어떤 파일도 디스크에 풀리지 않기 때문에 사이트는 단일 파일 안에만 존재하며, 잘못된
    location

규칙이 노출할 문서 루트가 없음

  • 배포는 단일 파일의 원자적 교체 방식이며, 새 버전 배포는 tarball 교체 뒤
    SIGHUP

전송

  • 디렉터리 패키징과 실행 명령은 다음 형식임
zeroserve --pack ./public > site.tar
zeroserve --addr 0.0.0.0:8080 site.tar
killall -SIGHUP zeroserve
  • 리로드는 같은 프로세스 안에서 사이트, 스크립트, TLS 자료를 원자적으로 교체하고 연결 손실 없이 동작함
  • 각 인스턴스는 단일 스레드 이벤트 루프이며, 프로세스 하나 기준으로는 제한이지만 확장 단위가 “더 많은 프로세스”일 때 맞는 형태로 제시됨

사용자 공간 eBPF 스크립팅

.zeroserve/scripts/

아래에 둔 모든 .c

파일은 패키징 시점에 clang

llc

로 eBPF 오브젝트로 컴파일되고 모든 요청에서 실행됨

  • eBPF는 커널 BPF 서브시스템이나
    CAP_BPF

없이 일반 비권한 프로세스 안의 async-ebpf 런타임에서 사용자 공간으로 실행됨

  • async-ebpf는 uBPF를 내장해 바이트코드를 네이티브 x86-64 머신 코드로 JIT 컴파일함
  • 포인터 케이지(pointer cage)는 JIT 컴파일된 코드의 모든 메모리 접근을 프로그램 전용 아레나로 마스킹해 잘못된 접근을 스크립트 메모리 안에 가둠
  • 스크립트는 zeroserve의 단일 이벤트 루프에서 직접 실행되며, 느린 스크립트가 다른 연결을 멈추지 않도록 타이머가 JIT 컴파일된 네이티브 코드를 실행 중간에 인터럽트하고 제어를 이벤트 루프로 돌려줄 수 있음
  • 프로그래밍 모델은 파일명 정렬 순서로 실행되는 스크립트 체인이며, 스크립트들은 요청별 메타데이터 맵을 공유함
  • 스크립트가
    zs_respond

zs_reverse_proxy

를 호출하면 체인은 단락 종료됨
zs.response.header.*

아래 키는 모든 응답의 헤더가 되며, 다른 키는 HTML 파일의 <zs-meta>visitor</zs-meta>

같은 플레이스홀더를 출력 시점에 치환하는 작은 템플릿 패스에 사용됨

  • 헬퍼 표면은 요청 메서드·경로·쿼리·헤더·피어 주소 읽기, URI 재작성, 헤더 설정·삭제를 지원함
  • 암호화와 인코딩 헬퍼는 SHA-256, HMAC-SHA256, base64, hex,
    getrandom

제공

  • JSON 헬퍼는 요청 본문 파싱, 문서 트리 생성·수정,
    zs_json_respond

응답 지원

  • 속도 제한은 피어 IP나 API 키 같은 임의 키 기반의 토큰 버킷을 지원하며, 상태는 핫 리로드 뒤에도 유지됨
  • AWS SigV4 헬퍼는 S3와 기타 AWS 서비스 통신용 서명
    Authorization

헤더와 presigned URL 지원

  • OIDC 로그인은 Authorization Code + PKCE 기반 relying-party 흐름을 제공하며, 전체 로그인 세션을 sealed XChaCha20-Poly1305 쿠키에 담아 서버를 상태 비저장으로 유지한 채 정적 사이트를 “Google로 로그인” 뒤에 둘 수 있음
  • 동적 엔드포인트는 특정 경로에서 스크립트가 직접 응답하는 방식이며, 예시에서는
    /health

요청에 application/json

헤더와 {"status":"ok"}

본문을 반환함

  • 각 스크립트는 기본 256KB 메모리 상한 아래 실행되며, 런타임은 오래 실행되는 스크립트를 실행기에서 시간 분할하고 폭주 스크립트를 스로틀링함
  • 스크립트는
    zs_call

로 서로 호출할 수 있으며, 호출 깊이는 제한됨

  • 무한 루프에 빠진 스크립트는 자기 요청만 지연시키며, 선점 타이머가 이를 인터럽트해 서버가 다른 요청을 계속 처리함
  • TLS 계층은 TLS 1.3 전용이며 BoringSSL로 종료됨
  • Encrypted Client Hello는 실제 SNI가 평문으로 나타나지 않게 하며, 디렉터리 기반 SNI 인증서 선택과 스크립트에 노출되는 JA4 클라이언트 핑거프린팅 제공
  • 투명 ECH 릴레이 모드는 복호화할 수 없는 핸드셰이크를 실제 업스트림으로 바이트 단위 그대로 전달해 보호된 이름이 공개 이름 뒤에 섞일 수 있게 함

성능

벤치마크 조건

  • zeroserve, nginx 1.26, Caddy 2.11을 8코어 Ryzen 7 3700X에서 같은 콘텐츠와 같은 자체 서명 인증서로 HTTPS 제공 비교
  • zeroserve 인스턴스가 설계상 단일 스레드이므로 비교 기준은 코어당 성능임
  • 모든 서버는
    taskset

으로 CPU 하나에 고정됐고, nginx는 worker_processes 1

, Caddy는 GOMAXPROCS=1

, zeroserve는 기존 단일 스레드 구조 사용

  • 부하는 다른 코어에서
    wrk -t4 -c100

으로 생성했고, 10초 실행 3회의 중앙값 사용
wrk

는 HTTP/1.1을 사용하므로 수치는 TLS 1.3 위의 HTTP/1.1이며, 긴 keep-alive 연결로 핸드셰이크 비용을 분산한 이미 열린 HTTPS 연결의 정상 상태 비용임

소형 정적 파일 174B

  • | 서버 | req/s | p99 |

  • | --- | ---: | ---: |

  • | zeroserve |
    36,681 | 5.4 ms |

  • | nginx | 31,226 | 7.8 ms |

  • | Caddy | 12,830 | 22 ms |

  • zeroserve는 단일 코어에서 nginx보다 약 17% 빠르게 소형 파일을 제공했고, 꼬리 지연도 더 낮음

  • HTML 페이지, 소형 JSON, CSS 같은 정적 사이트 기본 사례가 zeroserve 튜닝 대상임

대형 정적 파일 100KB

  • | 서버 | req/s | 처리량 | p99 |
  • | --- | ---: | ---: | ---: |
  • | zeroserve |
    8,000 | 782 MB/s | 22 ms |
  • | nginx | 7,600 | 773 MB/s | 28 ms |
  • | Caddy | 6,084 | 590 MB/s | 44 ms |
  • 세 서버의 결과는 가까웠고, zeroserve가 단일 코어에서 약 780 MB/s로 약간 앞섬
  • nginx의 대형 파일 강점인
    sendfile()

은 TLS 아래에서 사용되지 않으며, 바이트를 사용자 공간에서 암호화해야 하므로 세 서버 모두 암호화와 쓰기 루프에 묶임

  • kernel TLS를 세 서버 모두 끈 상태에서 zeroserve의
    io_uring

읽기·쓰기 경로가 약간 더 빠른 결과임

eBPF vs Lua

  • 스크립팅 비교 대상은 웹 서버 안에서 빠른 코드를 실행하는 일반적 방식인 nginx + LuaJIT
    ngx_http_lua_module

  • zeroserve는 기본값으로 스크립트 선점 타이머를 2ms마다 설정하며, 세밀한 간격은 문제 스크립트를 빠르게 스로틀링하지만 정상 스크립트에도 비용을 줌
  • 기본 2ms에서는 완전 동적 응답 기준 eBPF가 약 32k req/s로 nginx Lua의 41k req/s보다 낮음
    --preempt-timer-interval-ms

를 10으로 올리면 스크립팅 처리량이 약 40% 회복되고 결과가 뒤집힘

요청별 헤더 주입 미들웨어

  • | 엔진 | req/s | p99 |
  • | --- | ---: | ---: |
  • | zeroserve eBPF 10ms |
    43,709 | 5.1 ms |
  • | zeroserve eBPF 2ms 기본값 | 31,334 | 6.7 ms |
  • | nginx Lua
    header_filter

| 28,653 | 8.4 ms |

  • 스크립트가 실행되지만 정적 파일은 계속 제공되는 미들웨어 사례에서 10ms eBPF가 nginx Lua보다 약 50% 높고 꼬리 지연도 더 낮음

완전 동적 JSON 응답

  • | 엔진 | req/s | p99 |
  • | --- | ---: | ---: |
  • | zeroserve eBPF 10ms |
    46,945 | 4.5 ms |
  • | nginx Lua
    content_by_lua

| 41,231 | 6.4 ms |

  • | zeroserve eBPF 2ms 기본값 | 32,393 | 6.7 ms |
  • 10ms 간격의 조정된 eBPF는 완전 합성 응답에서도 nginx의
    content_by_lua

보다 높은 처리량을 기록함

  • 두 엔진 모두 네이티브 코드로 컴파일되며, LuaJIT는 트레이싱 JIT이고 async-ebpf는 uBPF를 통해 eBPF를 JIT 컴파일함
  • TLS 암호화가 공통 요청 비용인 조건에서 조정된 eBPF 경로가 처리량에서 앞섬
  • 2ms 기본값에서는 eBPF가 미들웨어 우위는 유지하지만 합성 응답 선두는 내주므로, 운영 스크립트에는 10ms 사용 권장

리버스 프록시로 사용

  • zeroserve는
    zs_reverse_proxy("http://127.0.0.1:9000";)

를 스크립트에서 호출해 백엔드로 프록시함

  • 업스트림 연결 풀은 백엔드당 최대 128개 연결과 30초 유휴 재사용을 지원함
  • 공정 비교를 위해 nginx는 기본적으로 요청마다 업스트림 연결을 닫는 특성을 고려해
    keepalive 128

, proxy_http_version 1.1

, 비운 Connection

헤더를 명시 사용함

  • Caddy는 기본 동작대로 연결 재사용
  • 각 프록시는 단일 코어에서 TLS를 종료하고 공유 평문 백엔드로 전달했으며, 백엔드는 별도 2코어 서버로 자체 100k req/s를 유지해 프록시 오버헤드만 측정함

소형 174B 응답 프록시

  • | 프록시 | req/s | p50 | p99 |
  • | --- | ---: | ---: | ---: |
  • | zeroserve |
    26,486 | 3.3 ms | 8 ms |
  • | nginx | 21,761 | 4.2 ms | 10.5 ms |
  • | Caddy | 7,683 | 10.3 ms | 33 ms |
  • zeroserve의 풀링된
    io_uring

프록시는 nginx보다 약 22% 앞섰고 Caddy 대비 약 3.4배 처리량을 기록함

  • API 호출, 소형 JSON, 앱 서버 HTML 같은 일반적 프록시 워크로드에서 zeroserve가 TLS 종료와 백엔드 전달을 더 빠르게 수행함

100KB 응답 프록시

  • | 프록시 | req/s | 처리량 |
  • | --- | ---: | ---: |
  • | nginx |
    5,882 | 585 MB/s |
  • | Caddy | 4,285 | 406 MB/s |
  • | zeroserve | 3,631 | 359 MB/s |
  • 프록시 본문이 커지면 nginx의 버퍼링이 바이트를 더 효율적으로 이동해 앞서고, Caddy가 중간, zeroserve가 뒤처짐
  • 프록시 응답이 크면 nginx가 더 나은 도구이며, 작고 많은 응답이면 zeroserve가 더 빠름

메모리

  • 유휴 상태의 단일 zeroserve 인스턴스는 약 15MB PSS를 사용하며, nginx의 약 6MB보다 많고 Caddy의 약 60MB보다 적음
  • 실행 단위가 전체 프로세스라는 점이 중요하며, 코어마다 복사본을 실행할 때 같은 바이너리를 매핑해 코드 페이지를 공유함
  • 추가 프로세스는 자체 워킹셋 외에는 적은 메모리를 더함

공개

  • zeroserve는 GitHub에서 오픈소스로 공개된 프로젝트임

AI 자동 생성 콘텐츠

본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0