Pusk: Go로 구축된 16MB 자체 호스팅 알림 서버
Pusk는 모니터링을 위한 외부 메시징 플랫폼 의존성을 제거하는 자체 호스팅 솔루션입니다. 단일 16MB 실행 파일로 13개의 Telegram Bot API 메서드, WebSocket, Web Push 및 PWA 클라이언트를 지원합니다. PostgreSQL이나 다른 데이터베이스 없이 순수 Go의 SQLite만으로 실행됩니다. 제한, 차단 또는 격리로 인해 외부 API를 사용할 수 없는 시나리오를 위해 설계되었습니다.
아키텍처는 외부 서비스를 배제합니다. Go는 빠른 빌드(7초)와 내장 net/http를 위해 선택되었습니다. 데이터베이스는 CGO 없이 modernc.org/sqlite를 사용하며, 시작 시 RSS는 약 4MB입니다. 로깅은 slog를 통해 JSON 또는 텍스트 형식으로 처리됩니다.
표준 메신저를 사용하지 않는 이유
대안들은 마이그레이션, 데이터베이스 및 상당한 리소스를 요구합니다:
| 플랫폼 | 의존성 | 봇 호환성 | ACK | RAM |
|-----------|-------------|---------------------|-----|-----|
| Mattermost | PostgreSQL, SMTP | 재작성 필요 | 플러그인 | 500+ MB |
| Matrix | Synapse, PostgreSQL | 재작성 필요 | 없음 | 500+ MB |
| Telegram | 클라우드 | — | 없음 | — |
Gotify/ntfy는 채팅이나 ACK 없이 푸시 알림만 제공합니다. Telegram에 VPN을 사용하면 장애 지점이 추가됩니다. Pusk는 Bot API를 에뮬레이션합니다: 봇의 base_url을 변경하면 인프라와 함께 작동합니다.
바이너리 구조
pusk (16 MB, Go 코드 약 6600줄, 테스트 110개)
├── Bot API — /bot/<token>/<method> (Telegram Bot API 80+ 메서드 중 13개 지원)
├── Client API — /api/* (PWA 백엔드)
├── WebSocket — /api/ws (실시간 상태, 입력 중 표시)
├── Web Push — FCM / Mozilla (폴링 없이 알림)
├── Files — /file/<id> (미디어)
├── PWA — / (웹 클라이언트)
└── SQLite — data/orgs/*/pusk.db (조직별 별도 DB)
지원 메서드: sendMessage, editMessageText, deleteMessage, answerCallbackQuery, sendPhoto, sendDocument, sendVoice, sendVideo, setWebhook, deleteWebhook, getWebhookInfo, getMe, getUpdates.
호환성을 위한 미들웨어:
func TelegramCompat(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
p := r.URL.Path
if strings.HasPrefix(p, "/bot") && !strings.HasPrefix(p, "/bot/") {
r.URL.Path = "/bot/" + p[4:]
}
next.ServeHTTP(w, r)
})
}
python-telegram-bot에서 마이그레이션:
# 이전:
app = ApplicationBuilder().token(TOKEN).build()
# 이후:
app = ApplicationBuilder().token(TOKEN).base_url("https://pusk.internal/bot").build()
롱 폴링 및 업데이트 전달
/getUpdates는 timeout 초 동안 연결을 유지하며, 오프셋을 고려하고 인메모리 큐(봇당 100개 업데이트)를 사용합니다. 재시작 시, 봇은 지속성 없이 후속 알림을 받습니다.
NAT 뒤의 봇을 위한 WebSocket 릴레이:
import websockets, json, asyncio
async def relay():
async with websockets.connect("wss://pusk.internal/bot/TOKEN/relay") as ws:
async for msg in ws:
update = json.loads(msg)
print(update["message"]["text"])
asyncio.run(relay())
자동 선택: 웹훅(SSRF 보호 포함), 릴레이(분당 30개 메시지로 제한), 또는 getUpdates.
조직별 SQLite 및 핫 백업
각 조직은 WAL 모드의 별도 pusk.db를 가집니다. 쓰기 중에도 리더를 차단하지 않습니다. 백업: sqlite3 pusk.db ".backup backup.db". PostgreSQL JOIN 없이 분당 수십 개 메시지에 적합합니다.
워크플로
- 관리자가 서버를 시작(docker run 또는 바이너리), 조직 생성 — #general 및 시스템 봇 생성.
- 링크를 통한 초대(7일, 최대 50명), 채팅에서 자동 알림.
- #alerts 생성, Zabbix/Grafana/Alertmanager에서 웹훅(?format=alertmanager).
- 온콜: 푸시/WebSocket, ACK 버튼, 전체 팀이 상태 확인.
역할:
- 관리자: 전체 권한 + 사용자/채널 관리.
- 멤버: 채팅, ACK, 파일, @멘션.
알림을 위한 개선 사항
- ACK → 무음: PUSK_ALERTMANAGER_URL를 통해 Alertmanager에 POST /api/v2/silences.
- 색상: 발화/해결에 대한 빨간색/녹색 줄.
- 푸시 최적화: WebSocket이 활성화되고 채널이 열려 있으면 푸시 건너뜀.
- 격리: 프론트엔드 내장, CDN 없음. Web Push는 FCM/Mozilla 필요.
주요 기능
- 외부 의존성 없는 단일 16MB 바이너리, Go의 SQLite.
- 재작성 없이 마이그레이션을 위한 Telegram Bot API 부분 에뮬레이션.
- Alertmanager 자동 무음을 위한 ACK, 색상 코딩 상태 표시기.
- NAT, 속도 제한, SSRF 보호를 위한 WebSocket 릴레이.
- 푸시, 관리자/멤버 역할, 조직별 DB 격리를 갖춘 PWA.
— Editorial Team
아직 댓글이 없습니다.