feat: add NATS + Dapr infrastructure for event-driven architecture
- infra/compose/nats.yml: NATS server with JetStream, persistence volume, healthcheck - infra/compose/dapr.yml: Dapr placement service for sidecar coordination - infra/dapr/: Dapr global config, pubsub component (NATS), statestore (Redis) - infra/compose/scraper.yml: add Dapr sidecar container + depends_on nats/dapr - docs/add-dapr-service.md: guide for integrating Dapr into new services - docs/add-new-app.md: add Dapr sidecar step - ARCHITECTURE.md: add NATS/Dapr to infra table + service mesh diagram - infra/README.md: update layout and deployment order - deploy-docker.yml: add nats.yml + dapr.yml to ALL_COMPOSE_FILES Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
d546a1acf9
commit
ea168b4b20
@@ -0,0 +1,152 @@
|
||||
# Menambahkan Dapr ke Service Baru
|
||||
|
||||
Panduan integrasi Dapr runtime sidecar untuk service di `asepharyana-hub`.
|
||||
|
||||
## Prasyarat
|
||||
|
||||
- NATS server berjalan (`infra/compose/nats.yml`)
|
||||
- Dapr placement service berjalan (`infra/compose/dapr.yml`)
|
||||
|
||||
## 1. Compose File
|
||||
|
||||
Setiap service butuh sidecar container Dapr. Contoh:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
container_name: app
|
||||
image: ghcr.io/asepharyana/asepharyana-hub/app:latest
|
||||
restart: always
|
||||
depends_on:
|
||||
dapr-placement:
|
||||
condition: service_healthy
|
||||
nats:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
app-shared-net:
|
||||
aliases:
|
||||
- app
|
||||
env_file:
|
||||
- ../../.env
|
||||
|
||||
app-dapr:
|
||||
container_name: app-dapr
|
||||
image: daprio/daprd:latest
|
||||
restart: always
|
||||
depends_on:
|
||||
dapr-placement:
|
||||
condition: service_healthy
|
||||
nats:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- app-shared-net
|
||||
command:
|
||||
- './daprd'
|
||||
- '--app-id=app'
|
||||
- '--app-port=3000'
|
||||
- '--dapr-http-port=3500'
|
||||
- '--dapr-grpc-port=50001'
|
||||
- '--placement-host-address=dapr-placement:50005'
|
||||
- '--components-path=/components'
|
||||
volumes:
|
||||
- ../../infra/dapr/components:/components
|
||||
|
||||
networks:
|
||||
app-shared-net:
|
||||
name: app-shared-net
|
||||
external: true
|
||||
```
|
||||
|
||||
## 2. Mengakses Dapr dari Service
|
||||
|
||||
### Via HTTP API (semua bahasa)
|
||||
|
||||
Sidecar listen di `localhost:3500`:
|
||||
|
||||
```bash
|
||||
# Publish event
|
||||
curl -X POST http://localhost:3500/v1.0/publish/pubsub/hub.event.type \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"key": "value"}'
|
||||
|
||||
# Service invocation
|
||||
curl http://localhost:3500/v1.0/invoke/app/method/endpoint
|
||||
|
||||
# State store
|
||||
curl -X POST http://localhost:3500/v1.0/state/statestore \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '[{"key": "mykey", "value": "myvalue"}]'
|
||||
```
|
||||
|
||||
### Via Dapr SDK (Rust)
|
||||
|
||||
Tambah ke `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
dapr-sdk = { version = "0.15", features = ["pubsub", "http"] }
|
||||
tokio-stream = "0.1"
|
||||
```
|
||||
|
||||
Contoh publish event:
|
||||
|
||||
```rust
|
||||
use dapr_sdk::client::{Client, Event};
|
||||
use dapr_sdk::DaprClient;
|
||||
|
||||
let client = DaprClient::new("127.0.0.1", 3500).await?;
|
||||
client.publish_event("pubsub", "hub.image.cached", serde_json::json!({
|
||||
"original_url": url,
|
||||
"cdn_url": cdn_url,
|
||||
})).await?;
|
||||
```
|
||||
|
||||
Contoh subscribe event:
|
||||
|
||||
```rust
|
||||
let mut stream = client.subscribe_events("pubsub", "hub.image.cached").await?;
|
||||
while let Some(event) = stream.next().await {
|
||||
let data: MyEvent = serde_json::from_slice(&event.data)?;
|
||||
// handle event
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Event Topics Convention
|
||||
|
||||
Gunakan prefix `hub.` untuk semua event:
|
||||
|
||||
| Topic | Payload | Description |
|
||||
|-------|---------|-------------|
|
||||
| `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 |
|
||||
|
||||
## 4. Local Development
|
||||
|
||||
Untuk development tanpa Docker:
|
||||
|
||||
```bash
|
||||
# 1. Install Dapr CLI
|
||||
# 2. Init Dapr local
|
||||
dapr init
|
||||
|
||||
# 3. Run service dengan sidecar
|
||||
dapr run --app-id app --app-port 3000 --dapr-http-port 3500 \
|
||||
--resources-path ./infra/dapr/components \
|
||||
-- cargo run
|
||||
```
|
||||
|
||||
## 5. Verifikasi
|
||||
|
||||
```bash
|
||||
# Sidecar health
|
||||
curl http://localhost:3500/v1.0/healthz
|
||||
|
||||
# Publish test event
|
||||
curl -X POST http://localhost:3500/v1.0/publish/pubsub/hub.test \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"test": true}'
|
||||
|
||||
# NATS stream stats
|
||||
curl http://localhost:8222/jszetstream
|
||||
```
|
||||
@@ -52,6 +52,44 @@ networks:
|
||||
|
||||
Gunakan `app-shared-net` agar service dapat diakses oleh Traefik dan service lain.
|
||||
|
||||
## 3.5. Tambahkan Dapr sidecar (wajib untuk pub/sub)
|
||||
|
||||
Setiap service yang ingin menggunakan Dapr pub/sub atau service invocation harus punya sidecar.
|
||||
Tambah di `infra/compose/<nama-app>.yml`:
|
||||
|
||||
```yaml
|
||||
<nama-app>-dapr:
|
||||
container_name: <nama-app>-dapr
|
||||
image: daprio/daprd:latest
|
||||
restart: always
|
||||
depends_on:
|
||||
dapr-placement:
|
||||
condition: service_healthy
|
||||
nats:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- app-shared-net
|
||||
command:
|
||||
- './daprd'
|
||||
- '--app-id=<nama-app>'
|
||||
- '--app-port=<port>'
|
||||
- '--dapr-http-port=3500'
|
||||
- '--dapr-grpc-port=50001'
|
||||
- '--placement-host-address=dapr-placement:50005'
|
||||
- '--components-path=/components'
|
||||
volumes:
|
||||
- ../../infra/dapr/components:/components
|
||||
```
|
||||
|
||||
Pastikan juga app container punya `depends_on` ke dapr-placement dan nats:
|
||||
```yaml
|
||||
depends_on:
|
||||
dapr-placement:
|
||||
condition: service_healthy
|
||||
nats:
|
||||
condition: service_healthy
|
||||
```
|
||||
|
||||
## 4. Tambahkan route Traefik
|
||||
|
||||
Update `infra/traefik/dynamic/apps.yaml`:
|
||||
|
||||
Reference in New Issue
Block a user