콘텐츠로 이동

모니터링 & 알림

PureCVisor는 외부 에이전트 없이 자체 메트릭 수집, 알림 엔진, Prometheus 호환 엔드포인트를 내장한다.

ebpf_telemetry.c가 10개 콜렉터로 ~178개 Prometheus 메트릭을 직접 수집한다 (node_* 126 + purecvisor_* 52).
별도의 node_exporter 설치가 불필요하다.

콜렉터메트릭 수수집 내용
CPU~18per-core 9모드 (user/system/idle/iowait/irq/softirq/steal/nice/guest)
Memory~60+meminfo 전 필드 (Slab/SReclaimable/Swap/PageFault)
Diskstats~12IOPS, read/write bytes, IO time
Filesystem~6마운트별 total/free/used
Netdev~8rx/tx bytes/packets, Error, Drop
VMstat~4pgfault, pgmajfault, pswpin, pswpout
Sockstat~4TCP/UDP 소켓 수
Pressure (PSI)~6CPU/memory/io some/full
Hwmon가변온도, 팬 속도
Misc~8boot_time, entropy, filefd, conntrack, ARP, NIC meta

추가 purecvisor_* 메트릭 (~44개):

  • purecvisor_cb_state / purecvisor_cb_failures (서킷 브레이커)
  • purecvisor_vm_locks_held (VM 상태 잠금)
  • purecvisor_tls_cert_expiry_days (인증서 만료)
  • purecvisor_zpool_* (6개: size/alloc/free/frag/cap/health)
  • purecvisor_worker_pool_pending (GTask 큐 깊이)
  • purecvisor_audit_queue_depth (감사 큐)
  • purecvisor_connpool_idle/active/max (커넥션 풀)
Terminal window
# Prometheus 메트릭 엔드포인트 (인증 불필요)
curl -s http://127.0.0.1:8080/api/v1/metrics
# 특정 메트릭 필터
curl -s http://127.0.0.1:8080/api/v1/metrics | grep purecvisor_cb_state

process_monitor.c/proc/[pid]/stat+io를 20초 주기로 스캔하여 프로세스별 CPU%, 메모리, I/O를 추적한다.

항목
스캔 주기20초
최대 추적 수512 프로세스
CPU% 계산delta(utime+stime) / delta(total)
정렬CPU% 내림차순 Top N
Terminal window
# CLI
pcvctl monitor processes --top 10
# REST
curl -s -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/processes | python3 -m json.tool
# RPC
echo '{"jsonrpc":"2.0","method":"monitor.processes","params":{"top":10},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock

WhaTap 지속 조건(sustained condition) 모델을 기반으로 한 알림 시스템.

파라미터기본값daemon.conf 키
평가 주기5초하드코딩
eval_period30초[alert] eval_period
dedup_window300초[alert] dedup_window
CPU 경고/위험80% / 95%[alert] cpu_warn / cpu_crit
Memory 경고/위험85% / 95%[alert] mem_warn / mem_crit
Disk 경고/위험80% / 90%[alert] disk_warn / disk_crit
히스토리1000건 링버퍼하드코딩

알림 평가 흐름:

5초 주기 → 메트릭 샘플링 → 임계값 비교
→ eval_period(30초) 연속 초과 확인
→ dedup_window(300초) 내 중복 제거
→ 웹훅 발송 + 히스토리 기록

알림 설정은 process-local config_revision으로 낙관적 동시성을 제어한다.
alert.config.set 요청은 alert.config.get에서 읽은 양의 정수 expected_revision을 반드시 포함해야 한다.
서버는 revision 비교, 부분 patch 적용, 최종 설정 전체 검증, commit, revision 증가를 하나의 원자 구간에서 수행한다.

Terminal window
# CLI는 내부에서 GET→SET을 한 번씩 수행한다.
pcvctl alert set --cpu_warn 82
# raw JSON-RPC를 사용할 때는 조회한 revision을 직접 전달한다.
echo '{"jsonrpc":"2.0","method":"alert.config.set","params":{"expected_revision":7,"cpu_warn":82},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock
  • 성공한 SET과 유효한 reload만 revision을 정확히 1 증가시킨다.
  • 오래된 revision은 -32002로 거절되며 CLI는 자동 재시도하지 않는다.
  • CPU/Memory/Disk/DataPool 임계값은 각각 0..100 범위이며 Warning < Critical이어야 한다.
    eval_period5..600초다.
  • 잘못된 타입, 알 수 없는 키, 임계값 쌍/범위 위반, 잘못된 URL은 -32602로 요청 전체가 거절된다.
    실패 시 설정과 revision은 변하지 않는다.
  • alert.config.reload의 source가 잘못되면 현재 런타임 설정을 보존하고 daemon_config_valid=false, daemon_config_error=invalid_alert_config로 경고한다.
  • webhook_secret 원문은 GET/SET 성공 응답, 로그, 오류에 반환하지 않는다.
    응답의 webhook_secret_configured boolean으로 설정 여부만 확인한다.
  • 시작 source가 잘못되면 자동 metric 평가가 꺼진 안전 기본값으로 시작한다.
  • enabled=false는 CPU/메모리/디스크/DataPool 자동 평가만 멈춘다.
    보안 이벤트, 직접 운영 이벤트, 히스토리, ACK, 에스컬레이션 처리는 계속 동작한다.
  • alert.config.setpcvctl alert set 변경은 현재 프로세스에만 적용된다.
    daemon.conf를 영속 수정하지 않으며, 프로세스 재시작 시 daemon.conf 값으로 복원된다.

라이브 JSON-RPC 계약은 응답 가능한 실제 daemon에서 strict 모드로 검증한다.
daemon socket이 없거나 응답하지 않으면 SKIP이 아니라 실패한다.

Terminal window
sudo env PCV_R7_ALERT_LIVE=1 \
bash tests/integration/test_alert_config_live.sh

이 전용 gate는 daemon.conf를 root 전용 백업으로 보존하고 모든 종료 경로에서 원본 파일과 서비스를 복구한다.
VM·스토리지·호스트 네트워크는 변경하지 않는다.

ZFS pool 사용량을 purecvisor_zpool_* 메트릭으로 수집하고, disk_warn/disk_crit 임계값 초과 시 알림을 발생시킨다.

GTask 스레드에서 비동기로 웹훅을 발송하여 메인 루프를 블로킹하지 않는다.

형식설정값페이로드
Slackwebhook_format=slack{"text": "..."}
Telegramwebhook_format=telegram{"chat_id": "...", "text": "..."}
Genericwebhook_format=genericPureCVisor JSON 전문

webhook_secret이 설정된 웹훅에는 HMAC-SHA256 서명 헤더 X-PureCVisor-Signature를 포함한다.

# daemon.conf
[alert]
enabled=true
cpu_warn=80
cpu_crit=95
mem_warn=85
mem_crit=95
disk_warn=80
disk_crit=90
data_pool_warn=80
data_pool_crit=90
eval_period=30
dedup_window=300
webhook_url=https://hooks.slack.com/services/T.../B.../xxx
webhook_crit_url=https://events.example.com/purecvisor/critical
webhook_secret=ENC:...
webhook_format=slack
telegram_chat_id=

8.5 알림 ACK & 에스컬레이션 (v1.0)

섹션 제목: “8.5 알림 ACK & 에스컬레이션 (v1.0)”

미확인(unacknowledged) 알림은 10분 후 자동 재전송된다.

Terminal window
# 알림 히스토리 조회
pcvctl alert list
# 알림 확인(ACK)
pcvctl alert ack --id <alert-id>
# REST
curl -s -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/alerts | python3 -m json.tool
# REST — per-alert ACK (OPERATOR 이상)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/alerts/<alert-id>/ack
# RPC
echo '{"jsonrpc":"2.0","method":"alert.history","params":{},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock
# RPC — per-alert ACK
echo '{"jsonrpc":"2.0","method":"alert.ack","params":{"alert_id":501},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock

웹 UI에서도 같은 동작을 한다.
관제 → 알림의 알림 이력 표 마지막 열이 확인 열이며, 미확인 행에는 확인 버튼이, 확인된 행에는 ACK 배지가 놓인다.
버튼을 누르면 해당 행만 즉시 배지로 바뀌고(필터·페이지 상태 유지) 포커스는 같은 표의 다음 미확인 버튼으로 옮겨간다.
표 머리의 전체 확인은 확인 대화상자를 거친 뒤 미확인 항목을 20건씩 나눠 처리하고 실패 건수를 토스트로 알린다.
ACK 권한은 OPERATOR 이상이라 VIEWER 세션에서는 두 버튼 모두 보이지 않는다.
글로벌 statusbar의 Critical 세그먼트는 +N warn · M unack으로 미확인 잔량을 같이 보여주고, 모바일 알림 탭의 각 행에도 같은 확인 버튼이 있다.

VM별 가동 시간을 추적하여 uptime_percent를 계산한다.

Terminal window
# RPC
echo '{"jsonrpc":"2.0","method":"vm.sla","params":{"name":"web-prod"},"id":"1"}' \
| nc -U /var/run/purecvisor/daemon.sock

VM별로 별도의 웹훅 대상을 지정할 수 있다.

Terminal window
# RPC
echo '{"jsonrpc":"2.0","method":"alert.set_config","params":{
"vm_name":"web-prod",
"webhook_url":"https://hooks.slack.com/services/...",
"webhook_format":"slack"
},"id":"1"}' | nc -U /var/run/purecvisor/daemon.sock

AND/OR 조건으로 최대 8개 조건을 조합한 복합 알림 규칙을 생성할 수 있다.

{
"jsonrpc": "2.0",
"method": "alert.set_config",
"params": {
"compound_rules": [
{
"operator": "AND",
"conditions": [
{"metric": "cpu_percent", "op": ">", "value": 90},
{"metric": "mem_percent", "op": ">", "value": 85}
]
}
]
},
"id": "1"
}

웹훅 발송 실패 시 DLQ에 최대 1000건까지 보관하며, 재시도 가능하다.

prometheus.yml
scrape_configs:
- job_name: 'purecvisor'
scrape_interval: 15s
metrics_path: '/api/v1/metrics'
static_configs:
- targets:
- '192.0.2.19:80'
- '192.0.2.20:80'
- '192.0.2.21:80'

Grafana 대시보드: 192.0.2.61:3000 (Example Operations 통합 대시보드)

ws://127.0.0.1:8080/api/v1/ws/events로 10초 주기 실시간 메트릭을 push한다.

const ws = new WebSocket('ws://127.0.0.1:8080/api/v1/ws/events');
ws.onmessage = (e) => {
const data = JSON.parse(e.data);
// { type: "metrics", cpu: 45.2, mem: 62.1, ... }
};

8.12 관측성 트레이싱 (retis, 2.0, D10)

섹션 제목: “8.12 관측성 트레이싱 (retis, 2.0, D10)”

2.0은 실 네트워크 경로를 커널 레벨에서 추적·프로파일링하는 debug.trace.* 제어면을 추가했습니다.
백엔드는 retis이며, 5-tuple 필터로 특정 VM/테넌트/프로토콜/IP의 패킷 경로와 drop 지점을 잡습니다.
진단(디버그) 용도이므로 강한 안전 가드가 걸립니다.

  • 타임박스 필수: timebox_sec(1..3600초, 무기한 캡처 금지)을 반드시 지정해야 하며, 만료 시 수집이 자동 종료됩니다.
  • 동시성 1: 동시에 하나의 추적만 실행되도록 가드합니다.
  • 오버레이 netns: 대상이 테넌트 오버레이 VM이면 해당 엔드포인트 netns에 진입해 추적합니다.
  • 순환 보존 + 부팅 fail-safe purge: 산출물은 순환 보존되며, 부팅 시 잔여 추적 상태를 정리합니다.
  • report: debug.trace.report는 수집 결과에서 병목·drop 지점을 분석해 돌려줍니다.

retis가 설치되어 있지 않으면 추적은 조용히 degraded 상태로 빠집니다(가용성은 배포 환경에 따름).

RPC 전용(2.0): debug.trace.* 네임스페이스는 전용 REST 경로·CLI 서브커맨드·UI 페이지가 없습니다.
POST /api/v1/rpc JSON-RPC 패스스루(또는 UDS 직접 nc -U)로만 호출하며, 5개 메서드 모두 ADMIN 권한이 필요합니다(전 테넌트 트래픽 관찰 표면).

메서드파라미터용도
debug.trace.start{timebox_sec, [vm], [tenant], [proto], [src_ip], [dst_ip]}추적 시작(추적 ID·산출 폴더 반환)
debug.trace.stop추적 ID 지정추적 중지
debug.trace.status추적 ID 지정추적 상태
debug.trace.list{}추적 목록
debug.trace.report추적 ID 지정추적 리포트(병목·drop 분석)
Terminal window
# 특정 VM 트래픽 30초 추적 (POST /api/v1/rpc 패스스루)
curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
http://127.0.0.1:8080/api/v1/rpc \
-d '{"jsonrpc":"2.0","method":"debug.trace.start","params":{
"timebox_sec":30,"vm":"web-prod"},"id":"1"}'

8.13 브라우저 Web Push 알림 (2.0.0, SP2b)

섹션 제목: “8.13 브라우저 Web Push 알림 (2.0.0, SP2b)”

2.0.0 현행 정본에서 데몬이 WARN/CRIT 알림을 브라우저 구독으로 직접 발송합니다.
중계 서비스를 두지 않고 RFC 8030(Web Push 프로토콜)·RFC 8291(aes128gcm 메시지 암호화)·RFC 8292(VAPID)를 데몬이 직접 구현하므로 신규 런타임 의존이 없습니다(이미 링크된 libcrypto·libsoup3·sqlite3만 사용).
브라우저 사업자의 push 서비스는 암호문만 중계하며 알림 본문을 읽지 못합니다.

구독 켜기

화면위치
데스크톱상단 우측 톱니(⚙) → 설정(Preferences) 모달의 알림 섹션 → 알림 구독 버튼
모바일알림 탭 상단 푸시 알림 카드의 같은 버튼

두 곳은 같은 토글 컴포넌트라 버튼 라벨과 상태 문구가 동일합니다.
구독된 상태에서는 라벨이 구독 해제로 바뀝니다.
소유자를 서버가 호출자 신원으로 고정하므로 VIEWER도 자기 구독은 등록·해지·조회할 수 있습니다.

구독의 소유자와 상한

구독은 계정별로 귀속됩니다.
클라이언트가 소유자를 지정할 수 없고, 조회(push.mine)도 인자 없이 본인 것만 돌아옵니다(endpoint 요약 + SHA-256 지문이라 원문 왕복 없이 대조합니다).
한 사용자가 가질 수 있는 구독 행은 최대 10개이며(브라우저·기기마다 1행), 초과하면 400으로 거부합니다.
이미 본인이 가진 endpoint를 다시 등록하는 것(자가치유 재등록)은 행이 늘지 않으므로 상한과 무관합니다.

메서드REST권한용도
push.vapid.getGET /api/v1/push/vapidVIEWER구독에 필요한 VAPID 공개키 조회
push.subscribePOST /api/v1/push/subscribeVIEWER브라우저 구독 등록(endpoint 기준 upsert)
push.unsubscribePOST /api/v1/push/unsubscribeVIEWER구독 해지(비-ADMIN은 본인 소유 행만)
push.mineGET /api/v1/push/mineVIEWER본인 구독만 조회(인자 없음)
push.listGET /api/v1/push/subscriptionsADMIN전체 구독 목록(브라우저 키 p256dh/auth는 미포함)
push.vapid.rotatePOST /api/v1/push/vapid/rotateADMINVAPID 키 재생성 + 전 구독 폐기
push.testPOST /api/v1/push/testADMIN테스트 알림 큐잉(운영 진단)

설정 (daemon.conf)

[webpush]
# 기본 true. false면 발송과 구독 등록이 모두 멈춘다
enabled = true
# "warn" | "crit" — 이 심각도 이상만 발송 (기본 warn)
min_severity = warn
# RFC 8292 VAPID sub. mailto: 또는 https: 스킴 필수, 미설정 시 생략
contact = mailto:ops@purecvisor.example.com

min_severitycontact는 허용 목록·스킴 검증을 거치며, 위반하면 기동을 막지 않고 기본값으로 되돌린 뒤 경고를 남깁니다.

secure context 요건 (자주 걸리는 함정)

브라우저의 Service Worker와 Push API는 secure context에서만 동작합니다 — https://이거나 http://127.0.0.1 계열이어야 합니다.
자체서명 인증서로 HTTPS를 올린 노드에서는 브라우저가 Service Worker 등록 자체를 거부하고, 그러면 구독은 어떤 방법으로도 되지 않습니다.
이 경우 토글은 누르기 전에 잠기고 사유가 버튼 옆에 그대로 표시됩니다.

사유표시되는 안내
인증서 미신뢰로 SW 등록 차단인증서를 신뢰 목록에 추가한 뒤 재시도 안내(로컬 노드면 http://127.0.0.1 계열 주소는 인증서 없이 동작)
그 외 SW 등록 실패브라우저가 준 실패 사유 원문
브라우저 알림 권한이 denied사이트 권한을 허용으로 바꾸라는 안내
Push API 미지원 브라우저지원하지 않는다는 안내

“페이지를 새로고침하세요”는 인증서 문제에서는 영원히 틀린 안내이므로, 등록 실패가 인증서 계열로 판정되면 그 문구를 쓰지 않습니다.

iOS(Safari): 이 화면을 홈 화면에 PWA로 설치한 경우에만 푸시가 도착합니다.
일반 브라우저 탭 상태에서는 구독을 켜도 알림이 오지 않습니다.

구독 인수인계와 자가치유

  • 브라우저가 endpoint를 교체하면(pushsubscriptionchange) Service Worker가 자동으로 재구독하고, 열려 있는 페이지가 그 결과를 서버에 반영합니다 — 운영자가 다시 켤 필요가 없습니다.
  • ADMIN이 push.vapid.rotate를 돌리면 기존 구독은 전부 무효가 됩니다.
    열려 있는 탭은 최대 1분 안에 스스로 재구독해 수렴합니다(옛 키로 만든 구독은 폐기하고 새로 만듭니다).
  • 구독 등록 시 endpoint는 서버의 SSRF 가드를 통과해야 합니다.
    거부되면 사람이 읽는 사유가 토글 옆에 그대로 노출됩니다.