Cloudflare Tunnel이 해외를 돌아 300ms씩 느려진다면? 국내에 직접 띄우는 오픈소스 Go 터널 'Meerkat'

한눈에 보기
국내 서버에 서비스를 올렸고 접속자도 국내에 있는데, Cloudflare Tunnel을 거치면 LA나 홍콩을 한 바퀴 돌아오느라 지연이 300ms 넘게 늘어나는 경우가 있습니다. Meerkat은 이 문제를 겪은 개발자가 직접 만든 오픈소스 터널입니다. 바깥에서 들어오는 공개 진입점(Outlet)을 국내 서버에 직접 둘 수 있습니다.
- 🔗 GitHub: lawnect/meerkat
- ⚙️ 구성: Go 백엔드 + Svelte 관리 화면
- 📜 라이선스: MIT (포함된 rathole, Caddy는 Apache-2.0)
- 🔐 주요 기능: SSL 인증서 자동 발급, OIDC SSO, 접근 제어, 감사 기록
왜 만들어졌을까?
Cloudflare Tunnel은 내부 서버의 인바운드 포트를 열지 않고도 서비스를 외부에 공개할 수 있어 많이 쓰입니다. 하지만 일부 국내 통신망에서는 Cloudflare 접속이 해외 PoP(LA·홍콩 등)를 경유합니다. 그래서 '국내 서버 → 국내 사용자' 구간인데도 체감 속도가 크게 떨어질 수 있습니다.
Meerkat은 이 우회 경로를 피하려고 공개 진입점을 내가 고른 국내 서버에 두는 방식을 택했습니다.
어떻게 동작하나요?
구조는 Cloudflare Tunnel과 거의 같습니다.
- Outlet: 공개 IP를 가진 서버(VM 1대 필요)에서 외부 요청을 받습니다.
- Connector: 사설 서버에서 실행되며, rathole을 통해 Outlet으로 아웃바운드 연결을 맺습니다.
- 그래서 사설 앱 쪽에서는 인바운드 포트를 하나도 열 필요가 없습니다.
성능은 어떨까?
작성자가 Oracle Cloud Ampere(1 OCPU, RAM 4GB) 환경에서 한 달 정도(내부 테스트 포함) 운영했을 때 측정한 값입니다.
| 지표 | 값 | |---|---| | Outlet 평균 처리시간 | 0.28ms | | Outlet p95 처리시간 | 0.82ms |
⚠️ 네트워크와 앱 응답시간이 모두 포함된 전체 지연이 아니라 Outlet 내부 처리시간만 잰 값입니다. 그래도 터널 자체가 병목이 될 일은 거의 없다고 볼 수 있습니다.
사내 서비스 운영에 필요한 기능들
개인용 장난감 수준이 아니라 기업 환경의 접근 통제와 감사까지 신경 쓴 점이 눈에 띕니다.
- TLS 인증서 자동 발급·갱신: Caddy 기반이라 인증서 관리 부담이 적습니다.
- Access 정책: 특정 도메인이나 경로에 OIDC SSO 또는 Bearer 토큰을 요구할 수 있습니다.
- 관리자 페이지 보안: 접속 IP 제한 등
- 감사 및 알림: 관리 작업 감사 기록, 접속·실패 로그, Telegram 알림
비슷한 프로젝트와 비교하면?
| 항목 | Cloudflare Tunnel | Pangolin | Meerkat | |---|---|---|---| | 진입점 위치 | Cloudflare 엣지 (해외 경유 가능) | 직접 운영 | 직접 운영 (국내 배치 가능) | | 구성 | 매니지드 서비스 | 비교적 기능이 많음 | Go + Svelte, 가벼운 구성 | | 초점 | 범용 | 범용 | HTTP 서비스 공개 | | 라이선스 | 상용 서비스 | AGPL-3.0 (CE) | MIT |
사내 도입을 검토한다면 AGPL이 아닌 MIT 라이선스라는 점도 장점입니다.
설치 방법 요약
1. 저장소 받기 & 설정 파일 복사 (작업용 PC에서)
git clone https://github.com/lawnect/meerkat.git
cd meerkat
ssh -t user@SERVER \
'sudo install -d -o "$(id -u)" -g "$(id -g)" -m 700 /opt/meerkat-outlet'
scp deploy/compose.outlet.yaml \
user@SERVER:/opt/meerkat-outlet/
user@SERVER는 실제 SSH 접속 주소로 바꿔 주세요.
2. Outlet 초기화 & 실행 (공개 서버에서)
docker run --rm --user "$(id -u):$(id -g)" \
--entrypoint /usr/local/bin/outlet-init \
-v /opt/meerkat-outlet:/out \
ghcr.io/lawnect/meerkat:latest -dir /out
cat > /opt/meerkat-outlet/compose.env <<EOF
MEERKAT_DEPLOY_DIR=/opt/meerkat-outlet
MEERKAT_UID=$(id -u)
MEERKAT_GID=$(id -g)
MEERKAT_BIND_ADDRESS=0.0.0.0
MEERKAT_IMAGE=ghcr.io/lawnect/meerkat:latest
EOF
docker compose --env-file /opt/meerkat-outlet/compose.env \
-f /opt/meerkat-outlet/compose.outlet.yaml up -d
3. 관리 화면 접속 (SSH 포트 포워딩)
보안을 위해 관리 화면은 설정을 마치기 전까지 공개 포트로 열리지 않습니다.
ssh -N -L 18443:127.0.0.1:18443 user@SERVER
이제 브라우저에서 https://localhost:18443에 접속합니다.
- 서버의
/opt/meerkat-outlet/configs/admin.token에 있는 설정 코드로 관리자 계정을 만듭니다. - 사용할 도메인을 등록합니다.
- 초기 화면은 사설 CA 인증서를 쓰므로 인증서 지문을 확인하거나 해당 CA를 신뢰해야 합니다.
- 보안을 위해 SSO 구성을 권장합니다.
사용 예시: app:80을 app.example.com으로 공개하기
사설 서버의 Docker 네트워크에서 돌고 있는 웹 앱을 외부에 공개하는 흐름입니다.
app.example.com의 DNS A 레코드를 Outlet 서버 IP로 지정합니다.- 관리 화면에서 터널을 만들고, 표시되는 Connector 설치 명령을 사설 서버에서 실행합니다.
- Connector와 앱을 같은 Docker 네트워크에 연결합니다.
- 관리 화면에서 서비스 대상으로
app:80, 공개 주소로app.example.com을 등록합니다. - 필요하면 SSO나 토큰 인증을 요구하는 Access 정책을 추가합니다.
이제 외부에서 https://app.example.com으로 접속할 수 있습니다. 🎉
🔑 Connector 설치 명령에는 비밀 토큰이 들어 있습니다. 채팅방이나 공개 저장소에 그대로 붙여넣지 않도록 주의하세요.
자세한 설치 및 운영 방법은 저장소의 한국어 가이드에서 볼 수 있습니다.
도입 전에 알아둘 한계
- 서버 운영은 직접 해야 합니다: 공개 서버의 관리, 업데이트, 백업을 모두 직접 챙겨야 합니다.
- 단일 인스턴스: 현재 Outlet은 이중화를 지원하지 않습니다.
- DDoS 방어는 기대하지 마세요: Cloudflare처럼 대규모 네트워크 공격을 대신 막아 주는 도구가 아닙니다.
에디터 한마디
국내 사용자 대상 서비스에서 Cloudflare Tunnel의 해외 경유 지연 때문에 고민해 본 적이 있다면 한 번 써볼 만한 프로젝트입니다. 사내 도구나 스테이징 서버처럼 트래픽 규모는 크지 않지만 응답성과 접근 통제가 중요한 서비스에 특히 잘 맞습니다. 작성자도 비슷한 문제를 겪은 사람들의 피드백을 기다리고 있으니, 써보고 개선점을 GitHub에 남겨 보세요.
원문: https://news.hada.io/topic?id=36696&utm_source=geeknews&utm_medium=rss&utm_campaign=news