
pingap v0.13.8
nginx와 같은 리버스 프록시로, pingora를 기반으로 구축되었으며, 간단하고 효율적입니다.
pingap
pingap 버전이 안정화되기 전까지는 pull request를 받지 않습니다. 질문이 있으시면 먼저 새 issue를 생성해 주세요.

개요
Pingap은 Cloudflare Pingora 기반의 고성능 리버스 프록시입니다. 간결한 TOML 파일과 직관적인 웹 관리 인터페이스를 통해 동적이고 무중단(zero-downtime) 설정 핫 리로딩을 지원하여 운영 관리를 단순화합니다.
핵심 강점은 강력한 플러그인 시스템으로, 인증(JWT, Key Auth), 보안(CSRF, IP/Referer/UA 제한), 트래픽 제어(속도 제한, 캐싱), 콘텐츠 수정(리다이렉트, 콘텐츠 치환), 관측성(Request ID)을 위한 20가지 이상의 즉시 사용 가능한 기능을 제공합니다. 이를 통해 Pingap은 단순한 프록시가 아닌 유연하고 확장 가능한 애플리케이션 게이트웨이로, API 보호부터 현대적인 웹 애플리케이션 배포까지 복잡한 시나리오를 손쉽게 처리하도록 설계되었습니다.
中文说明 | Documentation · 中文文档 | Examples | Plugins | Crates
flowchart LR
internet("Internet") -- request --> pingap["Pingap"]
pingap -- proxy:pingap.io/api/* --> apiUpstream["10.1.1.1,10.1.1.2"]
pingap -- proxy:cdn.pingap.io --> cdnUpstream["10.1.2.1,10.1.2.2"]
pingap -- proxy:/* --> upstream["10.1.3.1,10.1.3.2"]
주요 기능
-
🚀 고성능 및 안정성
- 메모리 안전성과 최고 수준의 성능을 위한 Rust 기반.
- 전투 검증된 비동기 네트워킹 라이브러리인 Cloudflare Pingora 기반.
- HTTP/1.1, HTTP/2 및 gRPC-web 프록시 지원.
-
🔧 동적이며 사용하기 쉬움
- 핫 리로딩을 통한 무중단 설정 변경.
- 간단하고 사람이 읽기 쉬운 TOML 설정 파일.
- 직관적인 실시간 관리를 위한 완전한 기능의 Web UI.
- 파일 및 etcd를 설정 백엔드로 지원.
- 설정 기록 저장 지원, 한 번의 클릭으로 이전 버전 복원 가능.
-
🧩 강력한 확장성
- 일반적인 게이트웨이 작업을 처리하는 풍부한 플러그인 시스템.
- 호스트, 경로 및 정규식 매칭을 통한 고급 라우팅.
- 정적 목록, DNS 또는 Docker 라벨을 통한 내장 서비스 디스커버리.
- Let's Encrypt를 통한 자동 HTTPS(HTTP-01 및 DNS-01 챌린지 모두 지원).
-
📊 현대적인 관측성
- 모니터링을 위한 네이티브 Prometheus 메트릭(pull 및 push 모드).
- 분산 추적을 위한 OpenTelemetry 통합 지원.
- 30개 이상의 변수를 지원하는 고도로 사용자 정의 가능한 액세스 로그.
- 업스트림 연결 시간, 처리 시간 등을 포함한 상세 성능 메트릭.
🚀 시작하기
Pingap을 시작하는 가장 쉬운 방법은 Docker Compose를 사용하는 것입니다.
docker-compose.yml파일을 생성합니다:
# docker-compose.yml
version: '3.8'
services:
pingap:
image: vicanso/pingap:latest # 프로덕션에서는 vicanso/pingap:0.12.1-full과 같은 특정 버전을 사용하세요
container_name: pingap-instance
restart: always
ports:
- "80:80"
- "443:443"
volumes:
# 모든 설정과 데이터를 유지하기 위해 로컬 디렉토리를 마운트
- ./pingap_data:/opt/pingap
environment:
# 환경 변수를 사용하여 설정
- PINGAP_CONF=/opt/pingap/conf
- PINGAP_ADMIN_ADDR=0.0.0.0:80/pingap
- PINGAP_ADMIN_USER=pingap
- PINGAP_ADMIN_PASSWORD=<YourSecurePassword> # 변경하세요!
command:
# pingap 시작 및 핫 리로딩 활성화
- pingap
- --autoreload
- 데이터 디렉토리를 생성하고 실행합니다:
mkdir pingap_data
docker-compose up -d
- 관리 UI에 접속합니다:
Pingap 인스턴스가 실행 중입니다! 설정한 자격 증명으로 http://localhost/pingap 에서 웹 관리 인터페이스에 접속할 수 있습니다.
curl을 통한 바이너리 설치
Linux 및 macOS의 경우 다음 한 줄 명령으로 최신 사전 빌드 바이너리를 /usr/local/bin/pingap에 설치할 수 있습니다:
curl -sSL https://raw.githubusercontent.com/vicanso/pingap/main/install.sh | sh
선택적 환경 변수:
PINGAP_FULL=1—-full빌드 설치(모든 선택 기능 활성화)PINGAP_LIBC=gnu— Linux에서 기본 musl 정적 빌드 대신 glibc 빌드 사용PINGAP_TLS=rustls— Linux에서-rustls-full빌드 설치(rustls TLS 백엔드, 모든 선택 기능, OpenSSL 없음); TLS 백엔드 참조
# 전체 기능 빌드
curl -sSL https://raw.githubusercontent.com/vicanso/pingap/main/install.sh | PINGAP_FULL=1 sh
지원 대상: Linux x86_64/arm64, Darwin x86_64/arm64. 사용 가능한 모든 자산은 릴리스 페이지에서 확인하세요.
바이너리 실행을 포함한 더 자세한 지침은 Documentation을 참조하세요.
설정 파일 없이 프록시 시작
단일 명령으로 도메인을 https로 서비스하고 백엔드로 전달할 수 있습니다:
# let's encrypt에서 인증서 요청
pingap --domain=pingap.io --upstream=192.168.1.1:3000
# 또는 자체 인증서 사용
pingap --domain=pingap.io --upstream=192.168.1.1:3000 --cert=/etc/ssl/pingap.io
--cert 없이 실행하면 Pingap은 HTTP-01 챌린지를 통해 Let's Encrypt에 인증서를 요청하므로 pingap.io가 이 호스트로 해석되어야 하며 포트 80이 인터넷에서 접근 가능해야 합니다. 발급된 인증서는 ~/.pingap/acme/<domains>.toml에 저장되며 재시작 시 재사용됩니다 — 발급에는 속도 제한이 있으므로 삭제하지 마세요. 다른 모든 것은 여전히 명령줄에서 가져옵니다: --upstream 변경은 인증서에 영향을 주지 않고 다음 시작 시 적용됩니다.
--cert는 인증서 자체 또는 인증서가 있는 디렉토리를 허용합니다 — 일반적인 fullchain.pem / privkey.pem, cert.pem / key.pem 및 tls.crt / tls.key 레이아웃이 자동으로 감지되며, 다른 경우에는 --key를 사용하세요. 리스너는 인증서가 있는 경우 기본적으로 0.0.0.0:443, 인증서도 도메인도 없는 경우 0.0.0.0:80으로 설정되며 --addr로 재정의할 수 있습니다. --upstream은 쉼표로 구분된 백엔드 목록을, --domain은 쉼표로 구분된 호스트 목록을 받습니다(생략하면 모든 호스트를 일반 http로 서비스). 목록에 없는 호스트에 대한 요청은 404로 응답합니다.
설정은 매 시작 시 생성되므로 관리 UI를 통해 편집할 수 없습니다: 단일 서버 이상이 필요한 경우 --conf를 사용하세요. 이 플래그들은 함께 사용할 수 없습니다.
동적 설정
Pingap은 다운타임 없이 설정 변경에 적응하도록 설계되었습니다.
핫 리로드(--autoreload): 업스트림, location 또는 플러그인 업데이트와 같은 대부분의 변경 사항에 대해 Pingap은 재시작 없이 10초 이내에 새 설정을 적용합니다. 이는 컨테이너 환경에 권장되는 모드입니다.
그레이스풀 재시작(-a 또는 --autorestart): 서버 수신 포트 수정과 같은 근본적인 변경의 경우 이 모드는 요청 손실 없이 완전한 무중단 재시작을 수행합니다.
인계는 타이밍 기반이 아닌 준비 상태 기반입니다: 교체 프로세스는 -d -u로 시작되어 업그레이드 소켓 옆의 유닉스 소켓을 통해 리스너를 인계받을 준비가 된 순간 보고하며, 그 후에만 실행 중인 프로세스가 자신에게 SIGQUIT를 보냅니다. 교체 프로세스가 종료되거나, 데몬이 죽거나, basic.restart_ready_timeout(기본 1분)이 먼저 경과하면 재시작이 중단되고 실행 중인 프로세스가 계속 서비스를 제공합니다.
🔧 개발
make dev
웹 관리자가 필요하다면 nodejs를 설치하고 웹 자산을 빌드해야 합니다.
# 관리 웹 자산 생성
cd web
npm i
cd ..
make build-web
TLS 백엔드
기본 빌드는 openssl 크레이트에 의해 소스에서 컴파일된 OpenSSL로 TLS를 종료합니다. 대신 rustls로 빌드하려면 OpenSSL 소스 빌드를 제거합니다(C 컴파일러는 여전히 필요합니다: rustls의 암호화 제공자인 ring과 aws-lc-rs에는 C 및 어셈블리가 포함되어 있습니다):
cargo build --release --no-default-features --features tls-rustls
# 선택 기능도 함께 사용하려면
cargo build --release --no-default-features --features tls-rustls,full
rustls 빌드는 서버별 tls_min_version, tls_max_version, tls_cipher_list 및 tls_ciphersuites 설정을 무시하고 설정 시 경고를 기록합니다: 항상 rustls 기본 암호화 스위트로 TLS 1.2 및 1.3을 제공합니다. 동적 SNI 인증서, 자체 서명 CA 발급, ACME 및 업스트림 ca 옵션을 포함한 다른 모든 것은 동일하게 동작합니다. 업스트림 검증 시 알아야 할 한 가지 차이점: rustls(webpki)는 OpenSSL이 허용하는 CA:TRUE가 포함된 서버 인증서를 거부하므로, 빠른 openssl req -x509 자체 서명 인증서를 사용하는 백엔드는 업스트림 ca 옵션이 신뢰할 수 있으려면 CA가 서명한 적절한 리프 인증서(또는 CA 플래그가 없는 자체 서명 리프)가 필요합니다. 시작 로그는 바이너리가 어떤 백엔드로 빌드되었는지 보고합니다.
📝 설정
server "test" {
addr = "127.0.0.1:6118"
location "github-api" {
path = "/api"
proxy_set_headers = ["Host:api.github.com"]
rewrite = "^/api/(?<path>.+)$ /$1"
upstream "api" {
addrs = ["api.github.com:443"]
discovery = "dns"
sni = "api.github.com"
}
}
location "static" {
plugin "staticServe" {
category = "directory"
path = "~/Downloads"
step = "request"
}
}
}
[upstreams.api]
addrs = ["api.github.com:443"]
discovery = "dns"
sni = "api.github.com"
[plugins.staticServe]
category = "directory"
path = "~/Downloads"
step = "request"
[locations.github-api]
upstream = "api"
path = "/api"
proxy_set_headers = ["Host:api.github.com"]
rewrite = "^/api/(?<path>.+)$ /$1"
[locations.static]
plugins = ["staticServe"]
[servers.test]
addr = "127.0.0.1:6118"
locations = ["github-api", "static"]
관련 지침은 여기에서 확인할 수 있습니다: https://pingap.io/crates/config.
🔄 프록시 단계
graph TD;
server["HTTP Server"];
locationA["Location A"];
locationB["Location B"];
locationPluginListA["Proxy Plugin List A"];
locationPluginListB["Proxy Plugin List B"];
upstreamA1["Upstream A1"];
upstreamA2["Upstream A2"];
upstreamB1["Upstream B1"];
upstreamB2["Upstream B2"];
locationResponsePluginListA["Response Plugin List A"];
locationResponsePluginListB["Response Plugin List B"];
start("New Request") --> server
server -- "host:HostA, Path:/api/*" --> locationA
server -- "Path:/rest/*"--> locationB
locationA -- "Exec Proxy Plugins" --> locationPluginListA
locationB -- "Exec Proxy Plugins" --> locationPluginListB
locationPluginListA -- "proxy pass: 10.0.0.1:8001" --> upstreamA1
locationPluginListA -- "proxy pass: 10.0.0.2:8001" --> upstreamA2
locationPluginListA -- "done" --> response
locationPluginListB -- "proxy pass: 10.0.0.1:8002" --> upstreamB1
locationPluginListB -- "proxy pass: 10.0.0.2:8002" --> upstreamB2
locationPluginListB -- "done" --> response
upstreamA1 -- "Exec Response Plugins" --> locationResponsePluginListA
upstreamA2 -- "Exec Response Plugins" --> locationResponsePluginListA
upstreamB1 -- "Exec Response Plugins" --> locationResponsePluginListB
upstreamB2 -- "Exec Response Plugins" --> locationResponsePluginListB
locationResponsePluginListA --> response
locationResponsePluginListB --> response
response["HTTP Response"] --> stop("Logging");
📊 성능
CPU: M4 Pro, Thread: 1
액세스 로그 없는 Ping
wrk 'http://127.0.0.1:6118/ping' --latency
Running 10s test @ http://127.0.0.1:6118/ping
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 66.41us 23.67us 1.11ms 76.54%
Req/Sec 73.99k 2.88k 79.77k 68.81%
Latency Distribution
50% 67.00us
75% 80.00us
90% 91.00us
99% 116.00us
1487330 requests in 10.10s, 194.32MB read
Requests/sec: 147260.15
Transfer/sec: 19.24MB
📦 Rust 버전
현재 MSRV는 1.96입니다.
📄 라이선스
이 프로젝트는 Apache License, Version 2.0에 따라 라이선스가 부여됩니다.