Operations
1. Triển khai
| Thuộc tính | Giá trị |
|---|---|
| Base image | debian:12-slim (binary Bun standalone) |
| Container | vd dev-nx-signal |
| Internal port | 3000 (ngoài 31090) |
| Network | nx-network (external, dùng chung) |
| Replicas | scale ngang được (state ở Redis + DB, không phải pod) |
| Snowflake ID | 9 |
| Migration mode | seed run-on-boot (alwaysRun) — chỉ permissions + role-permissions |
| Health | GET /v1/api/signal/health (public, từ VerifierApplication) |
Volumes
| Host Path | Container Path | Mục đích |
|---|---|---|
packages/signal/dist/bin | /app/bin | Binary đã compile (bun run rebuild) |
packages/signal/resources | /app/resources | Banner, HTML test client |
infrastructure/deployments/develop/signal/start.sh | /app/start.sh | Script khởi động |
Traefik — dual routing
Signal expose hai router chia sẻ một backend (port 3000):
| Router | Rule | Middleware | Vì sao |
|---|---|---|---|
signal-rest | PathPrefix(/v1/api/signal) | rate-limit, circuit-breaker, security-headers | Bảo vệ chuẩn cho request HTTP ngắn ngủi |
signal-ws | PathPrefix(/stream) | none | Kết nối WS bền vỡ dưới rate-limiting / circuit-breaking theo request |
yaml
labels:
- "traefik.enable=true"
- "traefik.http.routers.signal-rest.rule=PathPrefix(`/v1/api/signal`)"
- "traefik.http.routers.signal-rest.middlewares=rate-limit@file,circuit-breaker@file,security-headers@file"
- "traefik.http.routers.signal-ws.rule=PathPrefix(`/stream`)"
- "traefik.http.services.signal.loadbalancer.server.port=3000"Router WS riêng (không middleware) là bất biến triển khai then chốt. Xem ADR-0003 và ADR-0001.
Bus Redis
WebSocketServerHelper của Signal và WebSocketEmitter của mỗi publisher phải trỏ tới cùng instance/cluster Redis (APP_ENV_WEBSOCKET_REDIS_*):
| Service | Component | Vai trò |
|---|---|---|
| Signal | WebSocketServerHelper | subscribe + publish (server đầy đủ) |
| Sale / Payment | WebSocketEmitter | chỉ publish |
Fan-out đa instance
| Kịch bản | Hành vi |
|---|---|
| Deliver cục bộ | Client đích trên instance này → deliver trực tiếp (không round-trip Redis) |
| Deliver từ xa | Đích trên instance khác → publish tới Redis, instance sở hữu deliver |
| Broadcast / room | Publish tới Redis; mỗi instance deliver tới client khớp cục bộ của nó |
2. Observability
| Tín hiệu | Nguồn | Xem ở đâu |
|---|---|---|
| Logs | stdout (key-value có cấu trúc) | kubectl logs / Loki |
| Health | GET /v1/api/signal/health | Portal gateway / Traefik check (30s) |
| OpenAPI | GET /v1/api/signal/doc, /explorer | Scalar UI |
| Traces | none (no-op) | — |
Dòng log chính
| Khu vực | Cho bạn biết gì |
|---|---|
| Kafka consumer | topic / partition / offset mỗi message; broker connect/disconnect |
| Push notification | topic / room / recipientId mỗi WS emit; warn khi emitter chưa sẵn |
| Handshake | warn khi thiếu/sai public key hoặc loại auth không hỗ trợ |
3. Bảo mật
| Mối quan tâm | Giảm thiểu |
|---|---|
| AuthN (REST) | JWT + Basic, xác minh qua JWKS từ xa |
| AuthN (WS) | JWT type=Bearer xác minh bởi JWKSVerifierTokenService lúc handshake |
| AuthZ | Permission WebSocketClient.* trên route quản lý client; /notifications scope theo subject JWT |
| Mã hoá E2E | Bắt buộc ECDH P-256 + AES-256-GCM trên /stream (requireEncryption: true); khoá ephemeral theo từng client (forward secrecy), xoá khi disconnect |
| Ngoại lệ plaintext | chỉ sự kiện connected và error |
| TLS | wss:// kết thúc tại Traefik trong production |
| Endpoint public | /health, /doc, /openapi.json, /explorer, và GET /socket/websocket/clients/status |
| Room ACL | hiện passthrough (chấp nhận mọi room) — ACL phạm vi merchant là TODO đã biết |
| Network | internal port 3000 không expose host; truy cập chỉ qua Traefik |
4. Runbook
4.1 Lớp cảnh báo
| Cảnh báo | Kích hoạt | Kiểm tra | Khắc phục | Leo thang |
|---|---|---|---|---|
signalConsumerStalled | offset không tiến trên signal.activity-notification | logs consumer / lag | restart pod; xác minh brokers + SASL | backend on-call |
signalWsEmitterNotReady | warn "WebSocket emitter not ready" lặp lại | kết nối Redis | kiểm APP_ENV_WEBSOCKET_REDIS_*, sức khoẻ Redis | SRE on-call |
signalHandshakeRejects | tăng vọt handshake bị từ chối | khả năng tới JWKS identity, lệch clock | xác minh URL identity + parity APP_ENV_WEBSOCKET_ECDH_INFO | backend on-call |
signalDuplicateNotifications | hàng trùng sau redelivery | logs offset/commit consumer | query de-dup; giảm thiểu (không dedup built-in — xem ADR-0002) | backend on-call |
4.2 Thao tác thường gặp
| Thao tác | Lệnh |
|---|---|
| Tail logs | kubectl logs -n <ns> -f deploy/signal |
| Kiểm sẵn sàng WS | curl /v1/api/signal/socket/websocket/clients/status |
| Xem client đã kết nối | GET /socket/websocket/clients (JWT + permission) |
| Replay notification | re-publish tới signal.activity-notification (lưu ý: tạo hàng trùng) |
| Reseed permission | chạy migration signal (idempotent) — chỉ operator chạy |
5. Trang liên quan
- Configuration
- Encryption
/runbook/— runbook trung tâm cho sự cố xuyên service- Decisions