189 lines
6.6 KiB
Markdown
189 lines
6.6 KiB
Markdown
# Arsitektur asepharyana-hub
|
|
|
|
## Topologi Fisik
|
|
|
|
Dua node terhubung via **Tailscale** overlay network:
|
|
|
|
```
|
|
┌──────────────────────────────┐ ┌──────────────────────────────┐
|
|
│ orangevps (VPS) │ │ imrnes (Bare-metal) │
|
|
│ IP: 45.127.35.244 │ │ Tailscale: 100.121.180.82 │
|
|
│ Tailscale: 100.x.x.x │◄──────┤ │
|
|
│ │ │ Layanan: │
|
|
│ Layanan: │ │ ├─ PostgreSQL (port 6432) │
|
|
│ ├─ Traefik (port 80/443) │ │ └─ Redis (port 6379) │
|
|
│ ├─ NATS + JetStream │ │ │
|
|
│ ├─ Dapr Placement │ └──────────────────────────────┘
|
|
│ ├─ Redis (cache, Dapr) │
|
|
│ ├─ Scraper API + Dapr │
|
|
│ └─ Hub (Next.js SPA) │
|
|
└──────────────────────────────┘
|
|
```
|
|
|
|
### Konektivitas Container ke Tailscale
|
|
|
|
Container di `orangevps` tidak bisa langsung mencapai IP Tailscale (`100.x.x.x`). Route Tailscale harus ditambahkan ke tabel routing utama (`main`) via `tailscale-routes.service` agar traffic dari container bisa melewati host ke Tailscale.
|
|
|
|
## Alur Request HTTP (External)
|
|
|
|
```
|
|
Internet
|
|
│
|
|
▼ Port 443
|
|
Traefik (v3.6)
|
|
├─ TLS termination (sertifikat dari volume mount)
|
|
├─ Middleware chain: secure-headers → compress → retry → rate-limit → buffer
|
|
├─ Plugin: real-ip (Cloudflare), block-sensitive-paths
|
|
│
|
|
▼ Router matching
|
|
Host(`asepharyana.my.id`) || Host(`www.asepharyana.my.id`) || Host(`hub.asepharyana.my.id`) → hub
|
|
Host(`scraper.asepharyana.my.id`) || Host(`api.asepharyana.my.id`) → scraper-api
|
|
│
|
|
▼ Service load balancer
|
|
http://scraper-api:4091
|
|
│
|
|
▼
|
|
Scraper API (Rust / Axum)
|
|
├─ Health check: GET /, respon 200
|
|
├─ REST endpoints
|
|
├─ Database via `DATABASE_URL` (Tailscale → PostgreSQL di imrnes)
|
|
├─ Cache via `REDIS_URL` (Redis lokal di container)
|
|
└─ Pub/sub via Dapr sidecar (localhost:3500)
|
|
```
|
|
|
|
## Infrastruktur Internal
|
|
|
|
### Docker Compose Project
|
|
|
|
Semua service berjalan dalam satu Docker Compose project bernama `compose` dan bergabung di network `app-shared-net`:
|
|
|
|
| File | Service | Peran |
|
|
|------|---------|-------|
|
|
| `traefik.yml` | `traefik` | Reverse proxy, TLS termination, middleware |
|
|
| `shared.yml` | `redis` | Cache, session store, backend Dapr pub/sub & state |
|
|
| `nats.yml` | `nats` | Message broker + JetStream persistent streaming |
|
|
| `dapr.yml` | `dapr-placement` | Koordinasi actor placement untuk sidecar Dapr |
|
|
| `scraper.yml` | `scraper-api` + `scraper-api-dapr` | Aplikasi Rust + sidecar Dapr |
|
|
| `hub.yml` | `hub` | Next.js SPA portfolio |
|
|
|
|
### Dapr Sidecar Pattern
|
|
|
|
Setiap aplikasi yang menggunakan Dapr mendapat sidecar container `daprd`:
|
|
|
|
```
|
|
┌─────────────────────┐
|
|
│ scraper-api │
|
|
│ (app port 4091) │
|
|
└────────┬────────────┘
|
|
│ localhost:3500 (HTTP)
|
|
│ localhost:50001 (gRPC)
|
|
┌────────▼────────────┐
|
|
│ scraper-api-dapr │
|
|
│ (daprd sidecar) │
|
|
│ │
|
|
│ Dapr components: │
|
|
│ ├─ pubsub.redis │
|
|
│ └─ state.redis │
|
|
└─────────────────────┘
|
|
```
|
|
|
|
Komponen Dapr:
|
|
|
|
| Komponen | Tipe | Backend |
|
|
|----------|------|---------|
|
|
| `pubsub` | `pubsub.redis` | `redis:6379` |
|
|
| `statestore` | `state.redis` | `redis:6379` (prefix `dapr`) |
|
|
|
|
### NATS + JetStream
|
|
|
|
NATS berjalan dengan flag `-js` untuk mengaktifkan JetStream. Persistent stream disimpan di volume `nats_data`. Dapr pub/sub routing:
|
|
|
|
```
|
|
Service → Dapr sidecar (pubsub.redis) → Redis streams
|
|
```
|
|
|
|
> **Catatan:** Saat ini Dapr pub/sub menggunakan Redis, bukan NATS. Jika ingin migrasi ke NATS untuk pub/sub, komponen Dapr perlu diganti dengan `pubsub.nats`.
|
|
|
|
## Arsitektur CI/CD
|
|
|
|
```
|
|
Push ke main (apps/**, infra/**)
|
|
│
|
|
▼
|
|
docker-build-push.yml
|
|
├─ Phase 1: Detect changed services
|
|
├─ Phase 2: Build & Push image ke GHCR
|
|
└─ Phase 3: Update compose manifest + submodule pointer
|
|
│
|
|
▼ (workflow_run trigger)
|
|
deploy-docker.yml
|
|
├─ SSH ke orangevps
|
|
├─ Git sync, pull images
|
|
├─ Remove stale containers
|
|
└─ Selective restart service
|
|
```
|
|
|
|
Submodule update dari remote repo via `repository_dispatch`:
|
|
|
|
```
|
|
Push ke asepharyana-hub-scraper
|
|
│
|
|
▼ (repository_dispatch)
|
|
update-submodule.yml
|
|
├─ Update submodule pointer
|
|
└─ Commit & push ke hub repo
|
|
│
|
|
▼ (repository_dispatch trigger)
|
|
docker-build-push.yml
|
|
└─ Build, push, deploy
|
|
```
|
|
|
|
## Image Tagging Strategy
|
|
|
|
| Tag | Contoh | Penggunaan |
|
|
|-----|--------|------------|
|
|
| `sha-<short>` | `sha-a3c5d74` | Immutable, deterministic rollback |
|
|
| `latest` | `latest` | Mutable, convenience |
|
|
| `buildcache` | `sha-a3c5d74-buildcache` | Registry-based build cache (internal) |
|
|
|
|
## Networking
|
|
|
|
### Port Map
|
|
|
|
| Port | Service | Deskripsi |
|
|
|------|---------|-----------|
|
|
| 443 | Traefik | HTTPS eksternal |
|
|
| 80 | Traefik | Redirect ke HTTPS |
|
|
| 4222 | NATS | Client connections |
|
|
| 8222 | NATS | HTTP monitor / health |
|
|
| 6379 | Redis | Internal container network |
|
|
| 3500 | Dapr sidecar | Dapr HTTP API (per service) |
|
|
| 50001 | Dapr sidecar | Dapr gRPC API (per service) |
|
|
| 50005 | Dapr placement | Actor placement |
|
|
| 4091 | Scraper API | Aplikasi HTTP |
|
|
|
|
## Event Topics Convention
|
|
|
|
Semua event menggunakan prefix `hub.`:
|
|
|
|
| Topic | Payload | Deskripsi |
|
|
|-------|---------|-----------|
|
|
| `hub.image.cached` | `{original_url, cdn_url, source}` | Image selesai di-cache |
|
|
| `hub.image.repaired` | `{old_url, new_url}` | CNAME image diperbaiki |
|
|
| `hub.scrape.anime.done` | `{source, slug, duration}` | Scrape anime selesai |
|
|
| `hub.system.alert` | `{service, level, message}` | Error/alert dari service |
|
|
|
|
## Service Registry (Traefik)
|
|
|
|
Domain routing:
|
|
|
|
| Subdomain | Service | URL Backend |
|
|
|-----------|---------|-------------|
|
|
| `scraper.*` | Scraper API | `http://scraper-api:4091` |
|
|
| `api.*` | Scraper API (alias) | `http://scraper-api:4091` |
|
|
| `traefik.*` | Traefik Dashboard | `api@internal` |
|
|
|
|
Semua domain tersedia di:
|
|
- `<service>.asepharyana.my.id`
|
|
- `<service>.asepharyana.web.id`
|