diff --git a/content/de/developer/integration/ai/images/rustfs-sglang-models.png b/content/de/developer/integration/ai/images/rustfs-sglang-models.png new file mode 100644 index 000000000..2ba97d48b Binary files /dev/null and b/content/de/developer/integration/ai/images/rustfs-sglang-models.png differ diff --git a/content/de/developer/integration/ai/meta.json b/content/de/developer/integration/ai/meta.json index 9f8472c17..f03ec4897 100644 --- a/content/de/developer/integration/ai/meta.json +++ b/content/de/developer/integration/ai/meta.json @@ -3,6 +3,7 @@ "pages": [ "mlflow", "ray", + "sglang", "vllm" ] } diff --git a/content/de/developer/integration/ai/sglang.md b/content/de/developer/integration/ai/sglang.md new file mode 100644 index 000000000..fb9bb9b48 --- /dev/null +++ b/content/de/developer/integration/ai/sglang.md @@ -0,0 +1,144 @@ +--- +title: "SGLang" +description: "LLM-Inferenz mit SGLang betreiben, das Modellgewichte direkt aus einem RustFS-Bucket streamt." +--- + +In dieser Anleitung verbinden Sie [SGLang](https://github.com/sgl-project/sglang) — das Serving-Framework für große Sprach- und multimodale Modelle — mit **RustFS** als Speicher für die Modellgewichte. Sie laden ein Modell in einen RustFS-Bucket hoch, richten SGLang auf die `s3://`-URI und bedienen OpenAI-kompatible Anfragen, während die Gewichte direkt aus dem Bucket gestreamt werden. Der Ablauf wurde mit `lmsysorg/sglang:v0.5.22-xeon` (SGLang 0.5.22, CPU-Engine) verifiziert, das `Qwen/Qwen2.5-0.5B-Instruct` aus einem RustFS-Bucket auf einem Nur-CPU-Host ausliefert. + +Sie benötigen Docker und ein kleines Hugging-Face-Modell. Dieses Setup ist für lokale Integrationstests gedacht, nicht für den Produktivbetrieb. + +## Architektur + +```mermaid +flowchart LR + Upload["aws s3 cp"] -->|"weights"| RustFS["RustFS :9000"] + RustFS -->|"s3:// runai streamer"| SGLang["SGLang :30000"] + Client["OpenAI SDK / curl"] -->|"completions"| SGLang +``` + +Wenn `--model-path` eine Objektspeicher-URI ist, wechselt SGLang automatisch zum Ladeformat `runai_streamer`: Die Metadatendateien (`config.json`, Tokenizer) werden einmal in den lokalen Cache geladen, danach streamen die Gewichtstensoren verzögert während das Engine lädt. Der Bucket bleibt die einzige Kopie des Modells, Serving-Hosts brauchen keinen lokalen Modellspeicher. + +:::note[Das CPU-Image enthält keinen S3-Streamer] + +Das `xeon`-CPU-Image enthält kein `runai-model-streamer-s3`; der Serverstart mit einem `s3://`-Modellpfad schlägt mit `ImportError: S3 files module not found` fehl. Installieren Sie das Paket vor dem Start im Container — die virtuelle Umgebung des Images wird mit `uv` verwaltet und hat kein `pip`-Modul. + +::: + +## 1. Modell nach RustFS hochladen + +Laden Sie die Modelldateien von Hugging Face und kopieren Sie sie in den Bucket. Ersetzen Sie alle Verbindungsplatzhalter: + +```bash +docker run --rm -v "$PWD/models:/models" --entrypoint python3 \ + lmsysorg/sglang:v0.5.22-xeon -c \ + 'from huggingface_hub import snapshot_download; snapshot_download("Qwen/Qwen2.5-0.5B-Instruct", local_dir="/models/qwen2.5-0.5b-instruct")' + +aws s3 cp models/qwen2.5-0.5b-instruct \ + s3://my-bucket/sglang-models/qwen2.5-0.5b-instruct --recursive \ + --endpoint-url http://:9000 \ + --acl private +``` + +Exportieren Sie die Zugangsdaten für die folgenden Befehle: + +```bash +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_DEFAULT_REGION=us-east-1 +``` + +Verwenden Sie ein Präfix pro Modell, sodass mehrere Modelle denselben Bucket teilen. Der Streamer liest Gewichtsdateien ausschließlich im Format `.safetensors`. + +## 2. SGLang gegen den Bucket starten + +Starten Sie das CPU-Image, installieren Sie das S3-Streamer-Paket und starten Sie den Server mit der Bucket-URI: + +```bash +docker run -d --name sglang -p 30000:30000 \ + -e AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY \ + -e AWS_DEFAULT_REGION \ + -e AWS_ENDPOINT_URL=http://:9000 \ + --entrypoint bash lmsysorg/sglang:v0.5.22-xeon -c \ + 'uv pip install -q runai-model-streamer-s3 && \ + python3 -m sglang.launch_server \ + --model-path s3://my-bucket/sglang-models/qwen2.5-0.5b-instruct \ + --served-model-name qwen2.5-0.5b-instruct \ + --host 0.0.0.0 --port 30000 \ + --device cpu --trust-remote-code \ + --disable-overlap-schedule \ + --mem-fraction-static 0.45' +``` + +`AWS_ENDPOINT_URL` richtet den Streamer auf RustFS; Anfragen an den eigenen Endpunkt verwenden Path-Style-Adressierung. `--mem-fraction-static` bestimmt den KV-Cache-Anteil am Hostspeicher auf dem CPU-Backend — erhöhen Sie den Wert auf Hosts mit mehr freiem RAM. Warten Sie auf die Startzeile: + +```bash +docker logs -f sglang +``` + +```text +The server is fired up and ready to roll! +``` + +## 3. Inferenz ausführen + +Prüfen Sie, dass das Modell bereitgestellt wird, und senden Sie eine OpenAI-kompatible Completion-Anfrage: + +```bash +curl -s http://localhost:30000/v1/models +``` + +```json +{"object":"list","data":[{"id":"qwen2.5-0.5b-instruct","object":"model","owned_by":"sglang","root":"qwen2.5-0.5b-instruct","max_model_len":32768}]} +``` + +```bash +curl -s http://localhost:30000/v1/completions \ + -H "Content-Type: application/json" \ + -d '{"model":"qwen2.5-0.5b-instruct","prompt":"RustFS is","max_tokens":16,"temperature":0}' +``` + +```json +{"object":"text_completion","model":"qwen2.5-0.5b-instruct","choices":[{"index":0,"text":" a file system for the Linux kernel that is designed to be used in embedded systems","finish_reason":"length"}],"usage":{"prompt_tokens":4,"total_tokens":20,"completion_tokens":16}} +``` + +![SGLang-Modellgewichte in der RustFS-Konsole](./images/rustfs-sglang-models.png) + +## 4. Anhalten oder Zurücksetzen + +Demo abreißen und die Bucket-Objekte behalten: + +```bash +docker rm -f sglang +``` + +Gespeichertes Modell löschen: + +```bash +aws s3 rm s3://my-bucket/sglang-models --recursive \ + --endpoint-url http://:9000 +``` + +## Fehlerbehebung + +### `ImportError: S3 files module not found` + +Das S3-Backend des RunAI-Streamers fehlt. Führen Sie wie in Schritt 2 gezeigt `uv pip install runai-model-streamer-s3` im Container aus, bevor Sie den Server starten. + +### `pip: command not found` oder `No module named pip` + +Das `/opt/.venv` des Images wurde von `uv` ohne `pip` erstellt. Verwenden Sie `uv pip install` statt `pip install`. + +### `Loaded weights leave no GPU memory for the KV cache` + +`--mem-fraction-static` ist für die geladenen Gewichte auf diesem Host zu klein. Die Fehlermeldung nennt den minimal machbaren Wert — starten Sie den Server mit einem größeren Anteil neu (zum Beispiel `0.45` auf einem 16-GB-Host für ein 0,5-B-Modell). + +### Anfragen schlagen mit Signatur- oder Zugriffsfehlern fehl + +Stellen Sie sicher, dass im Server-Container alle vier Variablen gesetzt sind: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION` und `AWS_ENDPOINT_URL` einschließlich des Schemas. + +## Nächste Schritte + +- Lesen Sie vor weiteren Serving-Operationen die [S3-Kompatibilitätshinweise](/administration/protocols/s3). +- Erstellen Sie dedizierte Produktionszugangsdaten mit [Zugangsdatenverwaltung](/security-compliance/iam/access-token). +- Folgen Sie der [SGLang-Dokumentation](https://docs.sglang.io/) für Tensorparallelität, Quantisierung und größere Modelle auf demselben bucket-gestützten Modelllager. diff --git a/content/de/developer/integration/big-data/images/rustfs-pulsar-offload.png b/content/de/developer/integration/big-data/images/rustfs-pulsar-offload.png new file mode 100644 index 000000000..ca6b201b3 Binary files /dev/null and b/content/de/developer/integration/big-data/images/rustfs-pulsar-offload.png differ diff --git a/content/de/developer/integration/big-data/meta.json b/content/de/developer/integration/big-data/meta.json index cf81bd4aa..86ba5f7ab 100644 --- a/content/de/developer/integration/big-data/meta.json +++ b/content/de/developer/integration/big-data/meta.json @@ -14,6 +14,7 @@ "pyiceberg", "spark", "seatunnel", - "zeppelin" + "zeppelin", + "pulsar" ] } diff --git a/content/de/developer/integration/big-data/pulsar.md b/content/de/developer/integration/big-data/pulsar.md new file mode 100644 index 000000000..bc43668bc --- /dev/null +++ b/content/de/developer/integration/big-data/pulsar.md @@ -0,0 +1,170 @@ +--- +title: "Apache Pulsar" +description: "Kalten Pulsar-Topic-Backlog aus BookKeeper mit Tiered Storage in einen RustFS-Bucket auslagern." +--- + +In dieser Anleitung verbinden Sie [Apache Pulsar](https://github.com/apache/pulsar) über seinen Tiered-Storage-S3-Offloader mit **RustFS**. Sie betreiben einen Standalone-Broker, dessen Offloading auf einen RustFS-Bucket zeigt, produzieren Nachrichten, lösen eine Ledger-Auslagerung aus und lesen die ausgelagerten Nachrichten aus dem Bucket zurück. Der Ablauf wurde mit `apachepulsar/pulsar-all:4.0.4` verifiziert, das ein Topic nach RustFS auslagert und Kaltlesevorgänge daraus bedient. + +Sie benötigen Docker. Dieses Setup ist für lokale Integrationstests gedacht, nicht für den Produktivbetrieb. + +## Architektur + +```mermaid +flowchart LR + Producer["pulsar-client produce"] --> Broker["Pulsar standalone"] + Broker -->|"hot ledgers"| BK["BookKeeper"] + Broker -->|"offload ledgers"| RustFS["RustFS :9000"] + Consumer["pulsar-client consume"] -->|"cold reads"| Broker +``` + +Alte Ledgers werden als `-ledger-`-Datenblöcke plus ein kleines `-index`-Objekt in den Bucket hochgeladen. Fordert ein Konsument Nachrichten an, die nicht mehr in BookKeeper liegen, liest der Broker sie transparent aus dem Bucket zurück. + +## 1. Standalone-Broker mit Offloading starten + +Das einfache `apachepulsar/pulsar`-Image enthält keine Offloader-Pakete und schlägt mit `No offloader found for driver 'S3'` fehl — verwenden Sie das `pulsar-all`-Image, das sie mitbringt. Der Broker wird auf einen kleinen Heap begrenzt: + +```bash +docker run -d --name pulsar -p 6650:6650 -p 8080:8080 \ + -e ACCESS_KEY_ID= \ + -e ACCESS_KEY_SECRET= \ + -e PULSAR_MEM="-Xms512m -Xmx512m" \ + --entrypoint bash apachepulsar/pulsar-all:4.0.4 -c ' + sed -i -e "s|^managedLedgerMaxEntriesPerLedger=.*|managedLedgerMaxEntriesPerLedger=100|" \ + -e "s|^managedLedgerMinLedgerRolloverTimeMinutes=.*|managedLedgerMinLedgerRolloverTimeMinutes=1|" \ + /pulsar/conf/standalone.conf + printf "\nmanagedLedgerOffloadDriver=S3\nmanagedLedgerOffloadBucket=my-bucket\nmanagedLedgerOffloadServiceEndpoint=http://:9000\nmanagedLedgerOffloadRegion=us-east-1\n" >> /pulsar/conf/standalone.conf + exec bin/pulsar standalone -nss + ' +``` + +Die beiden `managedLedger*`-Änderungen verkleinern die Ledgers, sodass ein Roll noch in der Demo passiert; der angehängte Block aktiviert das Offloading. + +:::note[Der Treiber S3 liest managedLedgerOffload*-Schlüssel, nicht s3ManagedLedgerOffload*] + +Für den Treiber `S3` löst der Offloader die generischen Schlüssel `managedLedgerOffloadBucket`, `managedLedgerOffloadServiceEndpoint` und `managedLedgerOffloadRegion` auf. Die Namen `s3ManagedLedgerOffload*` gehören zum Treiber `aws-s3` und werden hier ignoriert — mit ihnen stürzt der Offloader mit `NullPointerException: endpoint` ab. Dasselbe gilt für Zugangsdaten: Der Treiber `S3` liest die Umgebungsvariablen `ACCESS_KEY_ID` und `ACCESS_KEY_SECRET`, nicht das Paar `AWS_*`. + +::: + +Warten Sie auf den Broker: + +```bash +docker logs -f pulsar 2>&1 | grep -m1 "The ledger offloader was created" +``` + +## 2. Nachrichten produzieren und das Topic am Leben halten + +Topics ohne Subscription löscht der Inactive-Topic-Aufräumdienst binnen Minuten, womit die Demo vorzeitig endet. Produzieren Sie eine erste Charge und legen Sie danach eine dauerhafte Subscription an, die das Topic registriert hält: + +```bash +docker exec pulsar bin/pulsar-client produce -m "hello rustfs offload" -n 300 \ + persistent://public/default/rustfs-test +``` + +```text +300 messages successfully produced +``` + +```bash +timeout 30 docker exec pulsar bin/pulsar-client consume -s keepalive -n 1 -p Earliest \ + persistent://public/default/rustfs-test +``` + +## 3. Ledger rollen und Offloading auslösen + +Ein Ledger rollt nur, wenn nach Ablauf von `managedLedgerMinLedgerRolloverTimeMinutes` noch ein Schreibvorgang erfolgt — die Prüfung läuft beim Schreiben, nicht im Hintergrund. Warten Sie mindestens eine Minute, produzieren Sie eine weitere Charge, um das Rollen zu erzwingen, und lagern Sie alles vor dem letzten Kilobyte aus: + +```bash +sleep 65 +docker exec pulsar bin/pulsar-client produce -m "rollover" -n 120 \ + persistent://public/default/rustfs-test +docker exec pulsar bin/pulsar-admin topics offload -s 1K \ + persistent://public/default/rustfs-test +``` + +```text +Offload triggered for persistent://public/default/rustfs-test for messages before 11:0:-1 +``` + +Das Offloading dauert einen Moment — warten Sie auf den Abschluss: + +```bash +sleep 30 +docker exec pulsar bin/pulsar-admin topics offload-status \ + persistent://public/default/rustfs-test +``` + +```text +Offload was a success +``` + +Bestätigen Sie, dass die Ledger-Objekte in RustFS gelandet sind — Datenblock plus Index, im Bucket-Wurzelverzeichnis: + +```bash +aws s3 ls s3://my-bucket/ --recursive \ + --endpoint-url http://:9000 | grep ledger +``` + +```text +2026-10-25 02:34:49 21057 7f9379cc-c21d-4829-a857-4c39ec02e391-ledger-9 +2026-10-25 02:34:49 214 7f9379cc-c21d-4829-a857-4c39ec02e391-ledger-9-index +``` + +## 4. Ausgelagerte Nachrichten zurücklesen + +Konsumieren Sie mit einer frischen Subscription ab der frühesten Position. Das erste Ledger liegt jetzt nur noch in RustFS, daher ist jede Nachricht, die der Broker liefert, ein Kaltlesevorgang aus dem Bucket: + +```bash +timeout 90 docker exec pulsar bin/pulsar-client consume -s coldread -n 420 -p Earliest \ + persistent://public/default/rustfs-test 2>&1 | grep -c "^-----" +``` + +```text +420 +``` + +Alle 420 Nachrichten — die 300 aus dem ausgelagerten Ledger plus die 120 aus dem aktiven — kommen über den Bucket zurück. + +![Ausgelagerte Pulsar-Ledger-Objekte in der RustFS-Konsole](./images/rustfs-pulsar-offload.png) + +## 5. Anhalten oder Zurücksetzen + +Broker abreißen und die Bucket-Objekte behalten: + +```bash +docker rm -f pulsar +``` + +Ausgelagerte Ledgers löschen: + +```bash +aws s3 rm s3://my-bucket/ --recursive --exclude "*" --include "*ledger*" \ + --endpoint-url http://:9000 +``` + +## Fehlerbehebung + +### `No offloader found for driver 'S3'. Please make sure you dropped the offloader nar packages` + +Das Image enthält die Offloader-NARs nicht. Verwenden Sie wie in Schritt 1 `apachepulsar/pulsar-all`. + +### `NullPointerException: endpoint` beim Offloading + +Der Offloader hat keinen Service-Endpoint gelesen — die Schlüssel `managedLedgerOffload*` fehlen oder wurden mit den Namen `s3ManagedLedgerOffload*` des `aws-s3`-Treibers geschrieben. Hängen Sie den Block aus Schritt 1 an und starten Sie neu. + +### `Couldn't get the access key id.` im Broker-Log + +Der Treiber `S3` liest nicht `AWS_ACCESS_KEY_ID`. Exportieren Sie `ACCESS_KEY_ID` und `ACCESS_KEY_SECRET` auf dem Broker-Container. + +### `Topic ... not found` während der Demo + +Der Inactive-Topic-Aufräumdienst hat das Topic gelöscht, weil es keine Subscription hatte. Wiederholen Sie Schritt 2 und belassen Sie die `keepalive`-Subscription. + +### `Offload was a success`, aber keine Objekte erscheinen + +Hochgeladen werden nur geschlossene Ledgers unterhalb der Größenschwelle, und ein Ledger rollt beim nächsten Schreibvorgang nach Ablauf des Mindestalters. Produzieren Sie eine weitere Charge und lösen Sie das Offloading erneut aus. + +## Nächste Schritte + +- Lesen Sie vor automatischen Offload-Schwellwerten auf Namespaces die [S3-Kompatibilitätshinweise](/administration/protocols/s3). +- Erstellen Sie dedizierte Produktionszugangsdaten mit [Zugangsdatenverwaltung](/security-compliance/iam/access-token). +- Folgen Sie der [Pulsar-Tiered-Storage-Dokumentation](https://pulsar.apache.org/docs/next/cookbooks-tiered-storage/) für Namespace-Schwellwerte, Retention-Richtlinien und Multi-Node-Broker gegen denselben Bucket. diff --git a/content/de/developer/integration/cloud-native/camel.md b/content/de/developer/integration/cloud-native/camel.md new file mode 100644 index 000000000..12ff570b6 --- /dev/null +++ b/content/de/developer/integration/cloud-native/camel.md @@ -0,0 +1,127 @@ +--- +title: "Apache Camel" +description: "Daten mit Apache-Camel-Integration-Routen in einen RustFS-Bucket hinein und heraus bewegen." +--- + +In dieser Anleitung verbinden Sie [Apache Camel](https://github.com/apache/camel) — das vielseitige Integrationsframework — über seine S3-Komponente mit **RustFS**. Sie schreiben eine Integration mit zwei Routen in YAML: eine Produzentenroute, die per Timer eine Nachricht in den Bucket schreibt, und eine Konsumentenroute, die diese Objekte zurückliest und protokolliert. Der Ablauf wurde mit Camel JBang auf `eclipse-temurin:21-jdk` unter Verwendung der Komponente `aws2-s3` mit übersteuertem Endpunkt und Path-Style-Adressierung verifiziert. + +Sie benötigen Docker. Dieses Setup ist für lokale Integrationstests gedacht, nicht für den Produktivbetrieb. + +## Architektur + +```mermaid +flowchart LR + Timer["timer:tick"] -->|"CamelAwsS3Key header"| Producer["aws2-s3 producer"] + Producer -->|"PutObject"| RustFS["RustFS :9000"] + RustFS -->|"prefix=camel/ poll"| Consumer["aws2-s3 consumer"] + Consumer --> Log["log:from-rustfs"] +``` + +Camel JBang führt die Routendatei direkt aus und löst die S3-Komponenten-Abhängigkeiten automatisch auf — kein Projektgerüst, kein Build, kein Deploy. + +## 1. Integrationsroute schreiben + +Erstellen Sie die Routendatei. Die Zugangsdaten kommen aus der AWS-Standard-Credential-Chain (Umgebungsvariablen), daher tragen die URIs nur Adressierung: + +```yaml title="route.yaml" +- from: + uri: "timer:tick" + parameters: + period: "5000" + steps: + - setBody: + simple: "Hello from Camel at ${date:now:yyyy-MM-dd HH:mm:ss}" + - setHeader: + name: "CamelAwsS3Key" + simple: "camel/hello-${date:now:HHmmss}.txt" + - to: + uri: "aws2-s3://my-bucket?overrideEndpoint=true&uriEndpointOverride=http://:9000&forcePathStyle=true&useDefaultCredentialsProvider=true" +- from: + uri: "aws2-s3://my-bucket?prefix=camel/&deleteAfterRead=false&maxMessagesPerPoll=5&overrideEndpoint=true&uriEndpointOverride=http://:9000&forcePathStyle=true&useDefaultCredentialsProvider=true" + steps: + - to: + uri: "log:from-rustfs" +``` + +Der Produzent setzt den Objektschlüssel über den Header `CamelAwsS3Key` — hier ein Zeitstempel pro Nachricht, sodass jeder Tick ein neues Objekt schreibt. Der Konsument pollt das Präfix `camel/` mit `deleteAfterRead=false`; Objekte bleiben nach dem Konsum im Bucket. + +:::note[Komponentenname und Endpunkt-Optionen] + +Die Komponente, die Camel JBang in diesem Workflow auflöst, ist `aws2-s3`. `overrideEndpoint=true` aktiviert den eigenen Endpunkt, `uriEndpointOverride` trägt seine URL, `forcePathStyle=true` wählt die Path-Style-Adressierung, die RustFS erwartet, und `useDefaultCredentialsProvider=true` lässt die Komponente die Zugangsdaten aus den Standard-Umgebungsvariablen lesen. + +::: + +## 2. Route mit Camel JBang ausführen + +Führen Sie die Integration in einem Container mit exportierten Zugangsdaten aus. JBang fragt beim ersten Einsatz nach Vertrauen — fügen Sie daher vor dem Start die Trust-Regeln hinzu: + +```bash +docker run --rm --network bridge \ + -e AWS_ACCESS_KEY_ID= \ + -e AWS_SECRET_ACCESS_KEY= \ + -e AWS_REGION=us-east-1 \ + -v "$PWD:/routes" \ + --entrypoint bash eclipse-temurin:21-jdk -c ' + export JBANG_DIR=/root/.jbang PATH=/root/.jbang/bin:$PATH + curl -Ls https://sh.jbang.dev | bash >/dev/null 2>&1 + jbang trust add https://github.com/apache/ 2>/dev/null || true + jbang trust add /routes 2>/dev/null || true + cd /routes && jbang camel@apache/camel run route.yaml + ' +``` + +Der erste Lauf lädt die Camel-Runtime und die Abhängigkeiten herunter und startet die Routen. Der Konsument protokolliert jedes gelesene Objekt: + +```text +2026-10-25 02:02:41.069 INFO 120 --- [-s3://my-bucket] from-rustfs : Exchange[ExchangePattern: InOnly, BodyType: byte[], Body: Hello from Camel at 2026-10-25 02:02:38] +``` + +## 3. Objekte prüfen + +Bestätigen Sie, dass die Objekte des Produzenten in RustFS liegen. Ersetzen Sie alle Verbindungsplatzhalter: + +```bash +aws s3 ls s3://my-bucket/camel/ \ + --endpoint-url http://:9000 +``` + +```text +2026-10-25 02:02:13 39 hello-020213.txt +2026-10-25 02:02:18 39 hello-020218.txt +2026-10-25 02:02:23 39 hello-020223.txt +``` + +![Apache-Camel-Routenobjekte in der RustFS-Konsole](./images/rustfs-camel-route.png) + +## 4. Anhalten oder Zurücksetzen + +Stoppen Sie den Container, um die Routen zu beenden. Demo-Objekte löschen: + +```bash +aws s3 rm s3://my-bucket/camel --recursive \ + --endpoint-url http://:9000 +``` + +## Fehlerbehebung + +### `Cannot find component with name: aws-s3` + +Ihre Camel-Version löst die Komponente unter einem anderen Namen auf — dieser Workflow verwendet `aws2-s3`. Nutzen Sie den Namen, den die Laufzeit in der Vorschlagsliste (`Did you mean: ... aws2-s3 ...`) meldet. + +### `is not from a trusted source` + +JBang weigert sich, nicht vertrauenswürdigen Code auszuführen. Führen Sie wie in Schritt 2 `jbang trust add https://github.com/apache/` und `jbang trust add /routes` vor `jbang camel@apache/camel run` aus. + +### `useDefaultCredentialsProvider is set to false` + +Die Komponente fand keine Credential-Konfiguration in der URI. Fügen Sie entweder `useDefaultCredentialsProvider=true` hinzu und exportieren Sie `AWS_ACCESS_KEY_ID` und `AWS_SECRET_ACCESS_KEY`, oder konfigurieren Sie die Endpunkt-Optionen `accessKey` und `secretKey` direkt. + +### Konsument liest nichts + +Der Konsument pollt nur das durch `prefix=camel/` gegebene Präfix. Bestätigen Sie, dass der Produzent lief (`timer`-Logzeilen erscheinen) und sein `CamelAwsS3Key`-Header mit `camel/` beginnt. + +## Nächste Schritte + +- Lesen Sie vor Multipart- oder Streaming-Operationen die [S3-Kompatibilitätshinweise](/administration/protocols/s3). +- Erstellen Sie dedizierte Produktionszugangsdaten mit [Zugangsdatenverwaltung](/security-compliance/iam/access-token). +- Folgen Sie der [Camel-S3-Komponenten-Dokumentation](https://camel.apache.org/components/latest/aws2-s3-component.html) für Download-Bereiche, Bucket-Operationen und Konsumenten-Tuning auf denselben Routen. diff --git a/content/de/developer/integration/cloud-native/dapr.md b/content/de/developer/integration/cloud-native/dapr.md new file mode 100644 index 000000000..f481181e0 --- /dev/null +++ b/content/de/developer/integration/cloud-native/dapr.md @@ -0,0 +1,144 @@ +--- +title: "Dapr" +description: "RustFS-Objekte aus jeder Dapr-Anwendung über das AWS-S3-Binding lesen und schreiben." +--- + +In dieser Anleitung verbinden Sie [Dapr](https://github.com/dapr/dapr) — die CNCF-portable Runtime für verteilte Anwendungen — über sein AWS-S3-Binding mit **RustFS**. Sie deklarieren eine Binding-Komponente mit einem RustFS-Bucket als Backend, starten einen Sidecar und nutzen die HTTP-API des Bindings, um ein Objekt zu erzeugen, zurückzulesen und unabhängig zu bestätigen. Der Ablauf wurde mit `daprio/dapr:1.19.0` gegen einen RustFS-Bucket mit Path-Style-Adressierung verifiziert. + +Sie benötigen Docker. Dieses Setup ist für lokale Integrationstests gedacht, nicht für den Produktivbetrieb. + +## Architektur + +```mermaid +flowchart LR + App["Your app"] -->|"POST /v1.0/bindings/rustfs-s3"| Daprd["daprd sidecar :3500"] + Daprd -->|"bindings.aws.s3"| RustFS["RustFS :9000"] +``` + +Anwendungen halten keine S3-Zugangsdaten oder Endpunkte. Der Sidecar stellt den Bucket als benanntes Binding bereit, und jede Operation ist ein POST an den Binding-Endpunkt mit einer `operation` und optionalen `metadata`. + +## 1. Binding-Komponente deklarieren + +Erstellen Sie die Komponentendatei. Der `endpoint` enthält das Schema, und `forcePathStyle` entspricht der Path-Style-Adressierung von RustFS: + +```yaml title="rustfs-s3.yaml" +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: rustfs-s3 +spec: + type: bindings.aws.s3 + version: v1 + metadata: + - name: bucket + value: my-bucket + - name: endpoint + value: http://:9000 + - name: accessKey + value: + - name: secretKey + value: + - name: region + value: us-east-1 + - name: forcePathStyle + value: "true" +``` + +## 2. Sidecar starten + +Führen Sie `daprd` mit eingebundenem Komponentenverzeichnis aus. `--resources-path` ersetzt das veraltete `--components-path`: + +```bash +docker run -d --name dapr \ + -v "$PWD:/components" \ + daprio/dapr:1.19.0 /daprd \ + --app-id s3demo \ + --dapr-http-port 3500 \ + --resources-path /components +``` + +Bestätigen Sie die Initialisierung des Bindings — das Log endet mit: + +```bash +docker logs dapr 2>&1 | grep "successful init" +``` + +```text +successful init for output binding (rustfs-s3 (bindings.aws.s3/v1)) +``` + +## 3. Objekt über das Binding erzeugen + +Rufen Sie das Binding von einem beliebigen HTTP-Client auf, der den Sidecar erreicht. Die Metadaten `key` legen den Objektnamen fest: + +```bash +curl -s -X POST http://localhost:3500/v1.0/bindings/rustfs-s3 \ + -H "Content-Type: application/json" \ + -d '{"operation":"create","data":"hello from dapr","metadata":{"key":"dapr/hello.txt"}}' +``` + +```json +{"location":"http://:9000/my-bucket/dapr/hello.txt","versionID":null} +``` + +## 4. Objekt zurücklesen + +Die Operation `get` liefert den Objektinhalt im Antwortkörper: + +```bash +curl -s -X POST http://localhost:3500/v1.0/bindings/rustfs-s3 \ + -H "Content-Type: application/json" \ + -d '{"operation":"get","metadata":{"key":"dapr/hello.txt"}}' +``` + +```text +hello from dapr +``` + +Bestätigen Sie dasselbe Objekt mit einem unabhängigen Client: + +```bash +aws s3 cp s3://my-bucket/dapr/hello.txt - \ + --endpoint-url http://:9000 +``` + +```text +hello from dapr +``` + +![Dapr-Binding-Objekte in der RustFS-Konsole](./images/rustfs-dapr-binding.png) + +## 5. Anhalten oder Zurücksetzen + +Sidecar abreißen und die Bucket-Objekte behalten: + +```bash +docker rm -f dapr +``` + +Demo-Objekt löschen: + +```bash +aws s3 rm s3://my-bucket/dapr/hello.txt \ + --endpoint-url http://:9000 +``` + +## Fehlerbehebung + +### Binding schlägt beim Initialisieren mit einem S3-Fehler fehl + +Prüfen Sie die vier Adressierungswerte gemeinsam: `endpoint` muss das Schema enthalten, `region` ist auch für Nicht-AWS-Endpunkte Pflicht, und `forcePathStyle` muss `"true"` sein. Ohne `forcePathStyle` sendet Dapr virtual-hosted-Anfragen, die RustFS ablehnt. + +### `no such binding` beim Aufruf + +Der Sidecar hat die Komponente nicht geladen — prüfen Sie, dass die Datei auf `.yaml` endet, im an `--resources-path` übergebenen Verzeichnis liegt und die Init-Zeile aus Schritt 2 im Log erscheint. + +### `get` liefert leeren Inhalt + +Der `metadata.key` stimmt nicht mit dem beim Erzeugen verwendeten Key überein. Keys sind case-sensitiv und enthalten das Präfix. + +## Nächste Schritte + +- Lesen Sie vor den Operationen `list`, `delete` oder `presign` die [S3-Kompatibilitätshinweise](/administration/protocols/s3). +- Erstellen Sie dedizierte Produktionszugangsdaten mit [Zugangsdatenverwaltung](/security-compliance/iam/access-token) und referenzieren Sie sie über einen Dapr-Secret-Store statt Klartextwerten in der Komponente. +- Folgen Sie der [Dapr-S3-Binding-Dokumentation](https://docs.dapr.io/reference/components-reference/supported-bindings/s3/) für den vollen Operationssatz und das Retry-Verhalten. diff --git a/content/de/developer/integration/cloud-native/images/rustfs-camel-route.png b/content/de/developer/integration/cloud-native/images/rustfs-camel-route.png new file mode 100644 index 000000000..3f73da421 Binary files /dev/null and b/content/de/developer/integration/cloud-native/images/rustfs-camel-route.png differ diff --git a/content/de/developer/integration/cloud-native/images/rustfs-dapr-binding.png b/content/de/developer/integration/cloud-native/images/rustfs-dapr-binding.png new file mode 100644 index 000000000..d56de00e0 Binary files /dev/null and b/content/de/developer/integration/cloud-native/images/rustfs-dapr-binding.png differ diff --git a/content/de/developer/integration/cloud-native/meta.json b/content/de/developer/integration/cloud-native/meta.json index f41fcbe2c..14b98a7ea 100644 --- a/content/de/developer/integration/cloud-native/meta.json +++ b/content/de/developer/integration/cloud-native/meta.json @@ -3,6 +3,8 @@ "pages": [ "cortex", "flux", - "celld" + "celld", + "camel", + "dapr" ] } diff --git a/content/de/developer/integration/devops/helm.md b/content/de/developer/integration/devops/helm.md new file mode 100644 index 000000000..d83773c10 --- /dev/null +++ b/content/de/developer/integration/devops/helm.md @@ -0,0 +1,137 @@ +--- +title: "Helm" +description: "Ein privates Helm-Chart-Repository mit dem helm-s3-Plugin in einem RustFS-Bucket betreiben." +--- + +In dieser Anleitung verbinden Sie [Helm](https://github.com/helm/helm) — den CNCF-Paketmanager für Kubernetes — über das Plugin [helm-s3](https://github.com/hypnoglow/helm-s3) mit **RustFS**, sodass ein RustFS-Bucket zum privaten Chart-Repository wird. Sie initialisieren das Repository im Bucket, pushen ein gepacktes Chart, fügen das Repository in Helm hinzu und ziehen das Chart zurück. Der Ablauf wurde mit `alpine/helm:3.16.4` und helm-s3 `v0.17.2` gegen einen RustFS-Bucket verifiziert. + +Sie benötigen Docker. Dieses Setup ist für lokale Integrationstests gedacht, nicht für den Produktivbetrieb. + +## Architektur + +```mermaid +flowchart LR + Dev["helm package"] -->|"helm s3 push"| Repo["s3://my-bucket/charts"] + Repo -->|"index.yaml"| Helm["helm repo add"] + Helm -->|"helm pull / install"| K8s["cluster"] +``` + +Das Bucket-Präfix enthält `index.yaml` und pro Chart-Version eine `.tgz`-Datei. Helm liest und schreibt es über das Plugin, daher ist das Repository von überall erreichbar, wo RustFS erreichbar ist. + +## 1. Plugin installieren und Repository initialisieren + +Das `alpine/helm`-Image enthält kein `git` — installieren Sie das Plugin daher aus dem Release-Archiv statt über die Repository-URL: + +```bash +cd /tmp +wget -q https://github.com/hypnoglow/helm-s3/releases/download/v0.17.2/helm-s3_0.17.2_linux_amd64.tar.gz +mkdir -p helm-s3 && tar -xzf helm-s3_0.17.2_linux_amd64.tar.gz -C helm-s3 +helm plugin install helm-s3 +``` + +Richten Sie das Plugin über seine eigenen Umgebungsvariablen auf RustFS und initialisieren Sie das Repository-Präfix: + +```bash +export AWS_ENDPOINT=:9000 +export AWS_DISABLE_SSL=true +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_DEFAULT_REGION=us-east-1 + +helm s3 init s3://my-bucket/charts +``` + +```text +Initialized empty repository at s3://my-bucket/charts +``` + +:::note[helm-s3 nutzt AWS_ENDPOINT, nicht AWS_ENDPOINT_URL] + +Das Plugin baut seine Anfrage-URLs aus `AWS_ENDPOINT` (Host und Port, ohne Schema) zusammen mit `AWS_DISABLE_SSL=true` für Klartext-HTTP. Die Standardvariable `AWS_ENDPOINT_URL` wird nicht berücksichtigt; die dadurch ausgelöste virtual-hosted-Adressierung des SDKs scheitert an RustFS mit `403 Forbidden`. + +::: + +## 2. Chart pushen + +Registrieren Sie das Repository vor dem ersten Push — das Plugin liest die Helm-Repositories-Datei und bricht ab, wenn sie fehlt: + +```bash +helm repo add rustfs-charts s3://my-bucket/charts +``` + +Erstellen und packen Sie ein Demo-Chart und pushen Sie es. `helm s3 push` erwartet den **Repository-Namen**, nicht die `s3://`-URI: + +```bash +helm create demo +helm package demo +helm s3 push ./demo-0.1.0.tgz rustfs-charts +``` + +```text +Successfully uploaded the chart to the repository. +``` + +## 3. Aus dem Bucket installieren oder ziehen + +Aktualisieren Sie den Repository-Index und prüfen Sie, dass das Chart auffindbar ist: + +```bash +helm repo update +helm search repo rustfs-charts/demo +``` + +```text +NAME CHART VERSION APP VERSION DESCRIPTION +rustfs-charts/demo 0.1.0 1.16.0 A Helm chart for Kubernetes +``` + +Ziehen Sie das Chart aus RustFS zurück: + +```bash +helm pull rustfs-charts/demo --destination ./pullback +tar -tzf ./pullback/demo-0.1.0.tgz | head -2 +``` + +```text +demo/Chart.yaml +demo/values.yaml +``` + +![Helm-Chart-Repository-Objekte in der RustFS-Konsole](./images/rustfs-helm-charts.png) + +## 4. Anhalten oder Zurücksetzen + +Charts sind gewöhnliche Bucket-Objekte, das Aufräumen ist ein Präfix-Löschen: + +```bash +aws s3 rm s3://my-bucket/charts --recursive \ + --endpoint-url http://:9000 +``` + +## Fehlerbehebung + +### `head s3 object: Forbidden` bei `helm s3 init` + +Das Plugin nutzt virtual-hosted-Adressierung — `AWS_ENDPOINT` und `AWS_DISABLE_SSL` sind nicht gesetzt. Exportieren Sie sie exakt wie in Schritt 1. + +### `check if index exists in the storage` schlägt mit einem Git-Fehler fehl + +Das Image enthält kein `git`, daher kann `helm plugin install ` nicht klonen. Installieren Sie wie in Schritt 1 aus dem Release-Archiv. + +### `The index file already exists under the provided URI` + +Das Repository-Präfix wurde bereits initialisiert. Nutzen Sie `helm s3 init --ignore-if-exists s3://my-bucket/charts` erneut oder ersetzen Sie den Index mit `--force`. + +### `repo with name s3://... not found` beim Push + +`helm s3 push` erwartet den mit `helm repo add` registrierten Repository-Namen — verwenden Sie `helm s3 push ./demo-0.1.0.tgz rustfs-charts`. + +### `couldn't load repositories file` beim Push + +Helms `repositories.yaml` existiert in dieser Umgebung noch nicht. Führen Sie vor dem Push `helm repo add rustfs-charts s3://my-bucket/charts` aus. + +## Nächste Schritte + +- Lesen Sie vor größeren Repositories mit vielen Chart-Versionen die [S3-Kompatibilitätshinweise](/administration/protocols/s3). +- Erstellen Sie dedizierte Produktionszugangsdaten mit [Zugangsdatenverwaltung](/security-compliance/iam/access-token). +- Folgen Sie der [helm-s3-Dokumentation](https://github.com/hypnoglow/helm-s3) für Re-Indexierung, Chart-Löschung und CI-Nutzung auf demselben Bucket. diff --git a/content/de/developer/integration/devops/images/rustfs-helm-charts.png b/content/de/developer/integration/devops/images/rustfs-helm-charts.png new file mode 100644 index 000000000..c63042b28 Binary files /dev/null and b/content/de/developer/integration/devops/images/rustfs-helm-charts.png differ diff --git a/content/de/developer/integration/devops/meta.json b/content/de/developer/integration/devops/meta.json index 55eada6ae..1f41e2a72 100644 --- a/content/de/developer/integration/devops/meta.json +++ b/content/de/developer/integration/devops/meta.json @@ -5,6 +5,7 @@ "opensearch", "gitea", "jenkins", - "terraform" + "terraform", + "helm" ] } diff --git a/content/de/developer/integration/observability/fluent-bit.md b/content/de/developer/integration/observability/fluent-bit.md new file mode 100644 index 000000000..880acc02a --- /dev/null +++ b/content/de/developer/integration/observability/fluent-bit.md @@ -0,0 +1,154 @@ +--- +title: "Fluent Bit" +description: "Logs und Events aus Fluent Bit über das S3-Output-Plugin in einen RustFS-Bucket liefern." +--- + +In dieser Anleitung verbinden Sie [Fluent Bit](https://github.com/fluent/fluent-bit) — den CNCF-Logprozessor und -Forwarder — mit **RustFS** als Logziel. Sie betreiben Fluent Bit mit dem `s3`-Output, puffern Datensätze auf der Festplatte, laden sie in einen RustFS-Bucket hoch und lesen eine ausgelieferte Logdatei zurück. Der Ablauf wurde mit `fluent/fluent-bit:3.2.10` schreibend in einen RustFS-Bucket über Path-Style-Adressierung verifiziert. + +Sie benötigen Docker. Dieses Setup ist für lokale Integrationstests gedacht, nicht für den Produktivbetrieb. + +## Architektur + +```mermaid +flowchart LR + Input["Inputs: tail, dummy, http"] -->|"tagged records"| FB["Fluent Bit"] + FB -->|"buffer (store_dir)"| Disk["Local buffer"] + Disk -->|"PutObject"| RustFS["RustFS :9000"] + Reader["aws cli / Console"] -->|"JSON lines"| RustFS +``` + +Der S3-Output puffert getaggte Datensätze im `store_dir` und lädt eine Datei hoch, sobald sie `total_file_size` erreicht oder `upload_timeout` abläuft — was zuerst eintritt. Jedes hochgeladene Objekt ist eine Datei aus JSON-Zeilen. + +## 1. Bucket anlegen + +Der S3-Output erstellt niemals Buckets; Uploads schlagen mit `NoSuchBucket` fehl, wenn der Bucket fehlt. Legen Sie ihn zuerst an und ersetzen Sie alle Verbindungsplatzhalter: + +```bash +aws s3 mb s3://my-bucket \ + --endpoint-url http://:9000 +``` + +## 2. S3-Output konfigurieren + +Erstellen Sie die Fluent-Bit-Konfiguration: + +```text title="fluent-bit.conf" +[SERVICE] + Flush 2 + Log_Level info + +[INPUT] + Name dummy + Tag rustfs.test + Rate 200 + +[OUTPUT] + Name s3 + Match * + endpoint http://:9000 + region us-east-1 + bucket my-bucket + total_file_size 1M + use_put_object On + upload_timeout 30s + store_dir /tmp/flb-store + json_date_key date + json_date_format iso8601 +``` + +`use_put_object On` lädt jede gepufferte Datei mit einem einzigen `PutObject`-Aufruf hoch. `upload_timeout` erzwingt nach 30 Sekunden einen Upload, auch wenn der Puffer `total_file_size` noch nicht erreicht hat — Dateneingänge mit geringem Volumen brauchen diese Option, sonst bleiben ihre Datensätze auf der lokalen Platte. + +:::note[Eigene Endpunkte verwenden standardmäßig Path-Style] + +Fluent Bit 3.2 kennt keine Option `s3_force_path_style` — das Plugin weist sie als unbekannte Eigenschaft zurück. Wenn `endpoint` auf einen eigenen S3-Dienst zeigt, erfolgen die Anfragen im Path-Style, was den RustFS-Standardeinstellungen entspricht. Ein zusätzlicher Schalter ist nicht nötig. + +::: + +## 3. Fluent Bit starten + +Starten Sie den Container und übergeben Sie die Zugangsdaten über die Standard-AWS-Umgebungsvariablen: + +```bash +docker run -d --name fluent-bit \ + -e AWS_ACCESS_KEY_ID= \ + -e AWS_SECRET_ACCESS_KEY= \ + -v "$PWD/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro" \ + fluent/fluent-bit:3.2.10 +``` + +Warten Sie auf den ersten Upload und prüfen Sie, dass der Worker sauber startete: + +```bash +docker logs fluent-bit 2>&1 | grep "worker #0 started" +``` + +```text +[output:s3:s3.0] worker #0 started +``` + +## 4. Hochgeladene Objekte prüfen + +Listen Sie den Bucket — das Standard-Schlüssellayout ist `fluent-bit-logs//YYYY/MM/DD/HH/MM/`: + +```bash +aws s3 ls s3://my-bucket/ --recursive \ + --endpoint-url http://:9000 +``` + +```text +2026-10-25 01:13:28 1021326 fluent-bit-logs/rustfs.test/2026/10/25/01/13/59-objectbmjH9744 +2026-10-25 01:16:05 333735 fluent-bit-logs/rustfs.test/2026/10/25/01/15/34-objectKfCq13io +``` + +Lesen Sie ein Objekt zurück — jede Zeile ist ein JSON-Datensatz: + +```bash +aws s3 cp s3://my-bucket/fluent-bit-logs/rustfs.test/2026/10/25/01/13/59-objectbmjH9744 - \ + --endpoint-url http://:9000 | head -2 +``` + +```json +{"date":"2026-10-25T01:13:59.204844Z","message":"dummy"} +{"date":"2026-10-25T01:13:59.206396Z","message":"dummy"} +``` + +![Fluent-Bit-Logs in der RustFS-Konsole](./images/rustfs-fluentbit-logs.png) + +## 5. Anhalten oder Zurücksetzen + +Demo abreißen und die Bucket-Objekte behalten: + +```bash +docker rm -f fluent-bit +``` + +Hochgeladene Logs löschen: + +```bash +aws s3 rm s3://my-bucket/fluent-bit-logs --recursive \ + --endpoint-url http://:9000 +``` + +## Fehlerbehebung + +### `PutObject request failed` mit `NoSuchBucket` + +Der Ausgabe-Bucket existiert nicht. Legen Sie ihn wie in Schritt 1 an — das Plugin erstellt keine Buckets. + +### `[config] s3: unknown configuration property 's3_force_path_style'` + +Diese Option gibt es in Fluent Bit 3.2 nicht. Entfernen Sie sie; eigene Endpunkte nutzen bereits Path-Style-Adressierung. + +### Objekte erscheinen nie und das Log bleibt still + +Der Puffer hat `total_file_size` nicht erreicht, und es ist kein `upload_timeout` gesetzt. Ergänzen Sie `upload_timeout 30s`, damit schwache Eingänge periodisch hochladen. + +### `InvalidAccessKeyId` im Fluent-Bit-Log + +Die Umgebungsvariablen für Zugangsdaten fehlen im Container oder sind falsch. Der S3-Output liest `AWS_ACCESS_KEY_ID` und `AWS_SECRET_ACCESS_KEY`. + +## Nächste Schritte + +- Lesen Sie vor Multipart-Uploads oder Komprimierung die [S3-Kompatibilitätshinweise](/administration/protocols/s3). +- Erstellen Sie dedizierte Produktionszugangsdaten mit [Zugangsdatenverwaltung](/security-compliance/iam/access-token). +- Folgen Sie der [Fluent-Bit-S3-Output-Dokumentation](https://docs.fluentbit.io/manual/pipeline/outputs/s3) für `s3_key_format`, Komprimierung und `tail`-Input-Tuning auf demselben Bucket. diff --git a/content/de/developer/integration/observability/images/rustfs-fluentbit-logs.png b/content/de/developer/integration/observability/images/rustfs-fluentbit-logs.png new file mode 100644 index 000000000..e395d48ce Binary files /dev/null and b/content/de/developer/integration/observability/images/rustfs-fluentbit-logs.png differ diff --git a/content/de/developer/integration/observability/meta.json b/content/de/developer/integration/observability/meta.json index 97ef4d6e6..dd9c3a167 100644 --- a/content/de/developer/integration/observability/meta.json +++ b/content/de/developer/integration/observability/meta.json @@ -1,6 +1,7 @@ { "title": "Observability", "pages": [ + "fluent-bit", "fluentd", "greptimedb", "loki", diff --git a/content/en/developer/integration/ai/images/rustfs-sglang-models.png b/content/en/developer/integration/ai/images/rustfs-sglang-models.png new file mode 100644 index 000000000..2ba97d48b Binary files /dev/null and b/content/en/developer/integration/ai/images/rustfs-sglang-models.png differ diff --git a/content/en/developer/integration/ai/meta.json b/content/en/developer/integration/ai/meta.json index 9f8472c17..f03ec4897 100644 --- a/content/en/developer/integration/ai/meta.json +++ b/content/en/developer/integration/ai/meta.json @@ -3,6 +3,7 @@ "pages": [ "mlflow", "ray", + "sglang", "vllm" ] } diff --git a/content/en/developer/integration/ai/sglang.md b/content/en/developer/integration/ai/sglang.md new file mode 100644 index 000000000..4b1989de4 --- /dev/null +++ b/content/en/developer/integration/ai/sglang.md @@ -0,0 +1,144 @@ +--- +title: "SGLang" +description: "Serve LLM inference with SGLang streaming model weights directly from a RustFS bucket." +--- + +This guide connects [SGLang](https://github.com/sgl-project/sglang) — the serving framework for large language models and multimodal models — to **RustFS** as its model-weight store. You will upload a model into a RustFS bucket, point SGLang at the `s3://` URI, and serve OpenAI-compatible requests with the weights streamed straight from the bucket. The workflow was verified with `lmsysorg/sglang:v0.5.22-xeon` (SGLang 0.5.22, CPU engine) serving `Qwen/Qwen2.5-0.5B-Instruct` from a RustFS bucket on a CPU-only host. + +You need Docker and a small Hugging Face model. This deployment is intended for local integration testing, not production. + +## Architecture + +```mermaid +flowchart LR + Upload["aws s3 cp"] -->|"weights"| RustFS["RustFS :9000"] + RustFS -->|"s3:// runai streamer"| SGLang["SGLang :30000"] + Client["OpenAI SDK / curl"] -->|"completions"| SGLang +``` + +When `--model-path` is an object-storage URI, SGLang switches to its `runai_streamer` load format automatically: the metadata files (`config.json`, tokenizer) download once into the local cache, then weight tensors stream lazily while the engine loads. The bucket stays the single copy of the model, so serving hosts need no local model store. + +:::note[The CPU image ships without the S3 streamer] + +The `xeon` CPU image does not include `runai-model-streamer-s3`, and starting the server with an `s3://` model path fails with `ImportError: S3 files module not found`. Install the package inside the container before launching the server — the image's virtual environment is managed with `uv`, which has no `pip` module. + +::: + +## 1. Upload the model to RustFS + +Fetch the model files from Hugging Face and copy them into the bucket, replacing all connection placeholders: + +```bash +docker run --rm -v "$PWD/models:/models" --entrypoint python3 \ + lmsysorg/sglang:v0.5.22-xeon -c \ + 'from huggingface_hub import snapshot_download; snapshot_download("Qwen/Qwen2.5-0.5B-Instruct", local_dir="/models/qwen2.5-0.5b-instruct")' + +aws s3 cp models/qwen2.5-0.5b-instruct \ + s3://my-bucket/sglang-models/qwen2.5-0.5b-instruct --recursive \ + --endpoint-url http://:9000 \ + --acl private +``` + +Export the credentials for the following commands: + +```bash +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_DEFAULT_REGION=us-east-1 +``` + +Keep one model per prefix so several models can share the bucket. The streamer reads weight files in `.safetensors` format only. + +## 2. Start SGLang against the bucket + +Run the CPU image, install the S3 streamer package, and launch the server against the bucket URI: + +```bash +docker run -d --name sglang -p 30000:30000 \ + -e AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY \ + -e AWS_DEFAULT_REGION \ + -e AWS_ENDPOINT_URL=http://:9000 \ + --entrypoint bash lmsysorg/sglang:v0.5.22-xeon -c \ + 'uv pip install -q runai-model-streamer-s3 && \ + python3 -m sglang.launch_server \ + --model-path s3://my-bucket/sglang-models/qwen2.5-0.5b-instruct \ + --served-model-name qwen2.5-0.5b-instruct \ + --host 0.0.0.0 --port 30000 \ + --device cpu --trust-remote-code \ + --disable-overlap-schedule \ + --mem-fraction-static 0.45' +``` + +`AWS_ENDPOINT_URL` points the streamer at RustFS; requests use path-style addressing against the custom endpoint. `--mem-fraction-static` sizes the KV cache share of host memory on the CPU backend — raise it on hosts with more free RAM. Wait for the startup line: + +```bash +docker logs -f sglang +``` + +```text +The server is fired up and ready to roll! +``` + +## 3. Run inference + +Confirm the model is served, then send an OpenAI-compatible completion request: + +```bash +curl -s http://localhost:30000/v1/models +``` + +```json +{"object":"list","data":[{"id":"qwen2.5-0.5b-instruct","object":"model","owned_by":"sglang","root":"qwen2.5-0.5b-instruct","max_model_len":32768}]} +``` + +```bash +curl -s http://localhost:30000/v1/completions \ + -H "Content-Type: application/json" \ + -d '{"model":"qwen2.5-0.5b-instruct","prompt":"RustFS is","max_tokens":16,"temperature":0}' +``` + +```json +{"object":"text_completion","model":"qwen2.5-0.5b-instruct","choices":[{"index":0,"text":" a file system for the Linux kernel that is designed to be used in embedded systems","finish_reason":"length"}],"usage":{"prompt_tokens":4,"total_tokens":20,"completion_tokens":16}} +``` + +![SGLang model weights stored in the RustFS Console](./images/rustfs-sglang-models.png) + +## 4. Stop or reset + +To tear down the demo while keeping the bucket objects: + +```bash +docker rm -f sglang +``` + +To delete the stored model: + +```bash +aws s3 rm s3://my-bucket/sglang-models --recursive \ + --endpoint-url http://:9000 +``` + +## Troubleshooting + +### `ImportError: S3 files module not found` + +The S3 backend of the runai streamer is missing. Run `uv pip install runai-model-streamer-s3` inside the container before starting the server, as shown in step 2. + +### `pip: command not found` or `No module named pip` + +The image's `/opt/.venv` is created by `uv` without `pip`. Use `uv pip install` instead of `pip install`. + +### `Loaded weights leave no GPU memory for the KV cache` + +`--mem-fraction-static` is too small for the loaded weights on this host. The error message reports the minimum viable value — restart the server with a larger fraction (for example `0.45` on a 16 GB host for a 0.5 B model). + +### Requests fail with signature or access errors + +Confirm all four variables are set for the server container: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`, and `AWS_ENDPOINT_URL` including the scheme. + +## Next steps + +- Review [S3 compatibility notes](/administration/protocols/s3) before adopting additional serving operations. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token). +- Follow the [SGLang documentation](https://docs.sglang.io/) for tensor parallelism, quantization, and larger models on the same bucket-backed model store. diff --git a/content/en/developer/integration/big-data/images/rustfs-pulsar-offload.png b/content/en/developer/integration/big-data/images/rustfs-pulsar-offload.png new file mode 100644 index 000000000..ca6b201b3 Binary files /dev/null and b/content/en/developer/integration/big-data/images/rustfs-pulsar-offload.png differ diff --git a/content/en/developer/integration/big-data/meta.json b/content/en/developer/integration/big-data/meta.json index 834b14a48..da5a85bf9 100644 --- a/content/en/developer/integration/big-data/meta.json +++ b/content/en/developer/integration/big-data/meta.json @@ -14,6 +14,7 @@ "pyiceberg", "spark", "seatunnel", - "zeppelin" + "zeppelin", + "pulsar" ] } diff --git a/content/en/developer/integration/big-data/pulsar.md b/content/en/developer/integration/big-data/pulsar.md new file mode 100644 index 000000000..e7d77434f --- /dev/null +++ b/content/en/developer/integration/big-data/pulsar.md @@ -0,0 +1,170 @@ +--- +title: "Apache Pulsar" +description: "Offload cold Pulsar topic backlog from BookKeeper into a RustFS bucket with tiered storage." +--- + +This guide connects [Apache Pulsar](https://github.com/apache/pulsar) to **RustFS** through its tiered-storage S3 offloader. You will run a standalone broker with offloading pointed at a RustFS bucket, produce messages, trigger a ledger offload, and read the offloaded messages back from the bucket. The workflow was verified with `apachepulsar/pulsar-all:4.0.4` offloading a topic to RustFS and serving cold reads from it. + +You need Docker. This deployment is intended for local integration testing, not production. + +## Architecture + +```mermaid +flowchart LR + Producer["pulsar-client produce"] --> Broker["Pulsar standalone"] + Broker -->|"hot ledgers"| BK["BookKeeper"] + Broker -->|"offload ledgers"| RustFS["RustFS :9000"] + Consumer["pulsar-client consume"] -->|"cold reads"| Broker +``` + +Old ledgers are uploaded to the bucket as `-ledger-` data blocks plus a small `-index` object, and the broker reads them back transparently when a consumer asks for messages that are no longer in BookKeeper. + +## 1. Start the standalone broker with offloading + +The plain `apachepulsar/pulsar` image ships without offloader packages and fails with `No offloader found for driver 'S3'` — use the `pulsar-all` image, which includes them. The broker is capped to a small heap: + +```bash +docker run -d --name pulsar -p 6650:6650 -p 8080:8080 \ + -e ACCESS_KEY_ID= \ + -e ACCESS_KEY_SECRET= \ + -e PULSAR_MEM="-Xms512m -Xmx512m" \ + --entrypoint bash apachepulsar/pulsar-all:4.0.4 -c ' + sed -i -e "s|^managedLedgerMaxEntriesPerLedger=.*|managedLedgerMaxEntriesPerLedger=100|" \ + -e "s|^managedLedgerMinLedgerRolloverTimeMinutes=.*|managedLedgerMinLedgerRolloverTimeMinutes=1|" \ + /pulsar/conf/standalone.conf + printf "\nmanagedLedgerOffloadDriver=S3\nmanagedLedgerOffloadBucket=my-bucket\nmanagedLedgerOffloadServiceEndpoint=http://:9000\nmanagedLedgerOffloadRegion=us-east-1\n" >> /pulsar/conf/standalone.conf + exec bin/pulsar standalone -nss + ' +``` + +The two `managedLedger*` edits shrink ledgers so a rollover happens within the demo; the appended block enables offloading. + +:::note[Driver S3 reads managedLedgerOffload* keys, not s3ManagedLedgerOffload*] + +For driver `S3` the offloader resolves the generic `managedLedgerOffloadBucket`, `managedLedgerOffloadServiceEndpoint`, and `managedLedgerOffloadRegion` keys. The `s3ManagedLedgerOffload*` names belong to the `aws-s3` driver and are ignored here — with them, the offloader crashes with `NullPointerException: endpoint`. The same applies to credentials: driver `S3` reads `ACCESS_KEY_ID` and `ACCESS_KEY_SECRET` environment variables, not the `AWS_*` pair. + +::: + +Wait for the broker: + +```bash +docker logs -f pulsar 2>&1 | grep -m1 "The ledger offloader was created" +``` + +## 2. Produce messages and keep the topic alive + +Topics with no subscription are deleted by the inactive-topic cleanup within minutes, which cancels the demo mid-flight. Produce a first batch, then create a durable subscription that keeps the topic registered: + +```bash +docker exec pulsar bin/pulsar-client produce -m "hello rustfs offload" -n 300 \ + persistent://public/default/rustfs-test +``` + +```text +300 messages successfully produced +``` + +```bash +timeout 30 docker exec pulsar bin/pulsar-client consume -s keepalive -n 1 -p Earliest \ + persistent://public/default/rustfs-test +``` + +## 3. Roll a ledger and trigger the offload + +A ledger only rolls when a write happens after `managedLedgerMinLedgerRolloverTimeMinutes` has passed — the check runs on write, not in the background. Wait at least one minute, produce another batch to force the roll, then offload everything older than the last kilobyte: + +```bash +sleep 65 +docker exec pulsar bin/pulsar-client produce -m "rollover" -n 120 \ + persistent://public/default/rustfs-test +docker exec pulsar bin/pulsar-admin topics offload -s 1K \ + persistent://public/default/rustfs-test +``` + +```text +Offload triggered for persistent://public/default/rustfs-test for messages before 11:0:-1 +``` + +The offload takes a moment — wait for completion: + +```bash +sleep 30 +docker exec pulsar bin/pulsar-admin topics offload-status \ + persistent://public/default/rustfs-test +``` + +```text +Offload was a success +``` + +Confirm the ledger objects landed in RustFS — data block plus index, at the bucket root: + +```bash +aws s3 ls s3://my-bucket/ --recursive \ + --endpoint-url http://:9000 | grep ledger +``` + +```text +2026-10-25 02:34:49 21057 7f9379cc-c21d-4829-a857-4c39ec02e391-ledger-9 +2026-10-25 02:34:49 214 7f9379cc-c21d-4829-a857-4c39ec02e391-ledger-9-index +``` + +## 4. Read the offloaded messages back + +Consume from the earliest position with a fresh subscription. The first ledger now lives only in RustFS, so every message the broker returns is a cold read from the bucket: + +```bash +timeout 90 docker exec pulsar bin/pulsar-client consume -s coldread -n 420 -p Earliest \ + persistent://public/default/rustfs-test 2>&1 | grep -c "^-----" +``` + +```text +420 +``` + +All 420 messages — the 300 from the offloaded ledger plus the 120 from the active one — come back through the bucket. + +![Pulsar offloaded ledger objects in the RustFS Console](./images/rustfs-pulsar-offload.png) + +## 5. Stop or reset + +To tear down the broker while keeping the bucket objects: + +```bash +docker rm -f pulsar +``` + +To delete the offloaded ledgers: + +```bash +aws s3 rm s3://my-bucket/ --recursive --exclude "*" --include "*ledger*" \ + --endpoint-url http://:9000 +``` + +## Troubleshooting + +### `No offloader found for driver 'S3'. Please make sure you dropped the offloader nar packages` + +The image does not include the offloader NARs. Use `apachepulsar/pulsar-all` as shown in step 1. + +### `NullPointerException: endpoint` when offloading + +The offloader read no service endpoint — the `managedLedgerOffload*` keys are missing or were written with the `s3ManagedLedgerOffload*` names of the `aws-s3` driver. Append the exact block from step 1 and restart. + +### `Couldn't get the access key id.` in the broker log + +Driver `S3` does not read `AWS_ACCESS_KEY_ID`. Export `ACCESS_KEY_ID` and `ACCESS_KEY_SECRET` on the broker container. + +### `Topic ... not found` during the demo + +The inactive-topic cleanup deleted the topic because it had no subscription. Re-run step 2 and keep the `keepalive` subscription in place. + +### `Offload was a success` but no objects appear + +Only closed ledgers older than the size threshold are uploaded, and a ledger rolls on the next write after the minimum rollover age. Produce another batch, then trigger the offload again. + +## Next steps + +- Review [S3 compatibility notes](/administration/protocols/s3) before enabling automatic offload thresholds on namespaces. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token). +- Follow the [Pulsar tiered storage documentation](https://pulsar.apache.org/docs/next/cookbooks-tiered-storage/) for namespace thresholds, retention policies, and multi-node brokers against the same bucket. diff --git a/content/en/developer/integration/cloud-native/camel.md b/content/en/developer/integration/cloud-native/camel.md new file mode 100644 index 000000000..3521f126d --- /dev/null +++ b/content/en/developer/integration/cloud-native/camel.md @@ -0,0 +1,127 @@ +--- +title: "Apache Camel" +description: "Route data in and out of a RustFS bucket with Apache Camel integration routes." +--- + +This guide connects [Apache Camel](https://github.com/apache/camel) — the versatile integration framework — to **RustFS** through its S3 component. You will write a two-route integration in YAML: a producer route that writes a message to a bucket on a timer, and a consumer route that reads those objects back and logs them. The workflow was verified with Camel JBang on `eclipse-temurin:21-jdk`, using the `aws2-s3` component with an overridden endpoint and path-style addressing. + +You need Docker. This deployment is intended for local integration testing, not production. + +## Architecture + +```mermaid +flowchart LR + Timer["timer:tick"] -->|"CamelAwsS3Key header"| Producer["aws2-s3 producer"] + Producer -->|"PutObject"| RustFS["RustFS :9000"] + RustFS -->|"prefix=camel/ poll"| Consumer["aws2-s3 consumer"] + Consumer --> Log["log:from-rustfs"] +``` + +Camel JBang runs the route file directly and resolves the S3 component dependencies automatically — no project scaffolding, build, or deploy step. + +## 1. Write the integration route + +Create the route file. Credentials come from the default AWS provider chain (environment variables), so the URIs carry only addressing: + +```yaml title="route.yaml" +- from: + uri: "timer:tick" + parameters: + period: "5000" + steps: + - setBody: + simple: "Hello from Camel at ${date:now:yyyy-MM-dd HH:mm:ss}" + - setHeader: + name: "CamelAwsS3Key" + simple: "camel/hello-${date:now:HHmmss}.txt" + - to: + uri: "aws2-s3://my-bucket?overrideEndpoint=true&uriEndpointOverride=http://:9000&forcePathStyle=true&useDefaultCredentialsProvider=true" +- from: + uri: "aws2-s3://my-bucket?prefix=camel/&deleteAfterRead=false&maxMessagesPerPoll=5&overrideEndpoint=true&uriEndpointOverride=http://:9000&forcePathStyle=true&useDefaultCredentialsProvider=true" + steps: + - to: + uri: "log:from-rustfs" +``` + +The producer sets the object key through the `CamelAwsS3Key` header — here a per-message timestamp, so every tick writes a new object. The consumer polls the `camel/` prefix with `deleteAfterRead=false`, so objects stay in the bucket after they are consumed. + +:::note[Component name and endpoint options] + +The component resolved by Camel JBang in this workflow is `aws2-s3`. `overrideEndpoint=true` activates the custom endpoint, `uriEndpointOverride` carries its URL, and `forcePathStyle=true` selects the path-style addressing RustFS expects. `useDefaultCredentialsProvider=true` makes the component read credentials from the standard environment variables. + +::: + +## 2. Run the route with Camel JBang + +Run the integration in a container with the credentials exported. JBang asks to trust the sources on first use, so add the trust rules before starting: + +```bash +docker run --rm --network bridge \ + -e AWS_ACCESS_KEY_ID= \ + -e AWS_SECRET_ACCESS_KEY= \ + -e AWS_REGION=us-east-1 \ + -v "$PWD:/routes" \ + --entrypoint bash eclipse-temurin:21-jdk -c ' + export JBANG_DIR=/root/.jbang PATH=/root/.jbang/bin:$PATH + curl -Ls https://sh.jbang.dev | bash >/dev/null 2>&1 + jbang trust add https://github.com/apache/ 2>/dev/null || true + jbang trust add /routes 2>/dev/null || true + cd /routes && jbang camel@apache/camel run route.yaml + ' +``` + +The first run downloads the Camel runtime and dependencies, then starts the routes. Consumers log each object they read: + +```text +2026-10-25 02:02:41.069 INFO 120 --- [-s3://my-bucket] from-rustfs : Exchange[ExchangePattern: InOnly, BodyType: byte[], Body: Hello from Camel at 2026-10-25 02:02:38] +``` + +## 3. Verify the objects + +Confirm the producer's objects sit in RustFS, replacing all connection placeholders: + +```bash +aws s3 ls s3://my-bucket/camel/ \ + --endpoint-url http://:9000 +``` + +```text +2026-10-25 02:02:13 39 hello-020213.txt +2026-10-25 02:02:18 39 hello-020218.txt +2026-10-25 02:02:23 39 hello-020223.txt +``` + +![Apache Camel route objects in the RustFS Console](./images/rustfs-camel-route.png) + +## 4. Stop or reset + +Stop the container to end the routes. To delete the demo objects: + +```bash +aws s3 rm s3://my-bucket/camel --recursive \ + --endpoint-url http://:9000 +``` + +## Troubleshooting + +### `Cannot find component with name: aws-s3` + +Your Camel version resolves the component under a different name — this workflow runs against `aws2-s3`. Use the name your runtime reports in the suggestion list (`Did you mean: ... aws2-s3 ...`). + +### `is not from a trusted source` + +JBang refuses to run untrusted code. Run `jbang trust add https://github.com/apache/` and `jbang trust add /routes` before `jbang camel@apache/camel run`, as shown in step 2. + +### `useDefaultCredentialsProvider is set to false` + +The component found no credential configuration in the URI. Either add `useDefaultCredentialsProvider=true` and export `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, or configure the `accessKey` and `secretKey` endpoint options directly. + +### Consumer reads nothing + +The consumer only polls the prefix given by `prefix=camel/`. Confirm the producer ran (`timer` log lines appear) and that its `CamelAwsS3Key` header starts with `camel/`. + +## Next steps + +- Review [S3 compatibility notes](/administration/protocols/s3) before adopting multipart or streaming operations on this component. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token). +- Follow the [Camel S3 component documentation](https://camel.apache.org/components/latest/aws2-s3-component.html) for download ranges, bucket operations, and consumer tuning on the same routes. diff --git a/content/en/developer/integration/cloud-native/dapr.md b/content/en/developer/integration/cloud-native/dapr.md new file mode 100644 index 000000000..bffcd12dd --- /dev/null +++ b/content/en/developer/integration/cloud-native/dapr.md @@ -0,0 +1,144 @@ +--- +title: "Dapr" +description: "Read and write RustFS objects from any Dapr application through the AWS S3 binding." +--- + +This guide connects [Dapr](https://github.com/dapr/dapr) — the CNCF portable runtime for building distributed applications — to **RustFS** through its AWS S3 binding. You will declare a binding component backed by a RustFS bucket, run a sidecar, and use the binding's HTTP API to create, read back, and confirm an object. The workflow was verified with `daprio/dapr:1.19.0` against a RustFS bucket with path-style addressing. + +You need Docker. This deployment is intended for local integration testing, not production. + +## Architecture + +```mermaid +flowchart LR + App["Your app"] -->|"POST /v1.0/bindings/rustfs-s3"| Daprd["daprd sidecar :3500"] + Daprd -->|"bindings.aws.s3"| RustFS["RustFS :9000"] +``` + +Applications never hold S3 credentials or endpoints. The sidecar exposes the bucket as a named binding, and every operation is a POST to the binding endpoint with an `operation` and optional `metadata`. + +## 1. Define the binding component + +Create the component file. The `endpoint` includes the scheme, and `forcePathStyle` matches RustFS path-style addressing: + +```yaml title="rustfs-s3.yaml" +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: rustfs-s3 +spec: + type: bindings.aws.s3 + version: v1 + metadata: + - name: bucket + value: my-bucket + - name: endpoint + value: http://:9000 + - name: accessKey + value: + - name: secretKey + value: + - name: region + value: us-east-1 + - name: forcePathStyle + value: "true" +``` + +## 2. Start the sidecar + +Run `daprd` with the components directory mounted. `--resources-path` replaces the deprecated `--components-path`: + +```bash +docker run -d --name dapr \ + -v "$PWD:/components" \ + daprio/dapr:1.19.0 /daprd \ + --app-id s3demo \ + --dapr-http-port 3500 \ + --resources-path /components +``` + +Confirm the binding initialized — the log ends with: + +```bash +docker logs dapr 2>&1 | grep "successful init" +``` + +```text +successful init for output binding (rustfs-s3 (bindings.aws.s3/v1)) +``` + +## 3. Create an object through the binding + +Invoke the binding from any HTTP client that can reach the sidecar. The `key` metadata sets the object name: + +```bash +curl -s -X POST http://localhost:3500/v1.0/bindings/rustfs-s3 \ + -H "Content-Type: application/json" \ + -d '{"operation":"create","data":"hello from dapr","metadata":{"key":"dapr/hello.txt"}}' +``` + +```json +{"location":"http://:9000/my-bucket/dapr/hello.txt","versionID":null} +``` + +## 4. Read the object back + +The `get` operation returns the object content in the response body: + +```bash +curl -s -X POST http://localhost:3500/v1.0/bindings/rustfs-s3 \ + -H "Content-Type: application/json" \ + -d '{"operation":"get","metadata":{"key":"dapr/hello.txt"}}' +``` + +```text +hello from dapr +``` + +Confirm the same object from an independent client: + +```bash +aws s3 cp s3://my-bucket/dapr/hello.txt - \ + --endpoint-url http://:9000 +``` + +```text +hello from dapr +``` + +![Dapr binding objects in the RustFS Console](./images/rustfs-dapr-binding.png) + +## 5. Stop or reset + +To tear down the sidecar while keeping the bucket objects: + +```bash +docker rm -f dapr +``` + +To delete the demo object: + +```bash +aws s3 rm s3://my-bucket/dapr/hello.txt \ + --endpoint-url http://:9000 +``` + +## Troubleshooting + +### Binding fails to init with an S3 error + +Check the four addressing values together: `endpoint` must include the scheme, `region` is required even for non-AWS endpoints, and `forcePathStyle` must be `"true"`. Omitting `forcePathStyle` sends virtual-hosted requests that RustFS rejects. + +### `no such binding` when invoking + +The sidecar did not load the component — confirm the file ends in `.yaml`, sits in the directory passed to `--resources-path`, and the init line from step 2 appears in the logs. + +### `get` returns empty content + +The `metadata.key` does not match the key used at create time. Keys are case-sensitive and include the prefix. + +## Next steps + +- Review [S3 compatibility notes](/administration/protocols/s3) before adopting the binding's `list`, `delete`, or `presign` operations. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token), and reference them through a Dapr secret store instead of plain component values. +- Follow the [Dapr S3 binding documentation](https://docs.dapr.io/reference/components-reference/supported-bindings/s3/) for the full operation set and retry behavior. diff --git a/content/en/developer/integration/cloud-native/images/rustfs-camel-route.png b/content/en/developer/integration/cloud-native/images/rustfs-camel-route.png new file mode 100644 index 000000000..3f73da421 Binary files /dev/null and b/content/en/developer/integration/cloud-native/images/rustfs-camel-route.png differ diff --git a/content/en/developer/integration/cloud-native/images/rustfs-dapr-binding.png b/content/en/developer/integration/cloud-native/images/rustfs-dapr-binding.png new file mode 100644 index 000000000..d56de00e0 Binary files /dev/null and b/content/en/developer/integration/cloud-native/images/rustfs-dapr-binding.png differ diff --git a/content/en/developer/integration/cloud-native/meta.json b/content/en/developer/integration/cloud-native/meta.json index fbe75f4b9..054d578ac 100644 --- a/content/en/developer/integration/cloud-native/meta.json +++ b/content/en/developer/integration/cloud-native/meta.json @@ -3,6 +3,8 @@ "pages": [ "cortex", "flux", - "celld" + "celld", + "camel", + "dapr" ] } diff --git a/content/en/developer/integration/devops/helm.md b/content/en/developer/integration/devops/helm.md new file mode 100644 index 000000000..c247290a8 --- /dev/null +++ b/content/en/developer/integration/devops/helm.md @@ -0,0 +1,137 @@ +--- +title: "Helm" +description: "Host a private Helm chart repository in a RustFS bucket with the helm-s3 plugin." +--- + +This guide connects [Helm](https://github.com/helm/helm) — the CNCF package manager for Kubernetes — to **RustFS** through the [helm-s3](https://github.com/hypnoglow/helm-s3) plugin, so a RustFS bucket becomes a private chart repository. You will initialize the repository in the bucket, push a packaged chart, then add the repository to Helm and pull the chart back. The workflow was verified with `alpine/helm:3.16.4` and helm-s3 `v0.17.2` against a RustFS bucket. + +You need Docker. This deployment is intended for local integration testing, not production. + +## Architecture + +```mermaid +flowchart LR + Dev["helm package"] -->|"helm s3 push"| Repo["s3://my-bucket/charts"] + Repo -->|"index.yaml"| Helm["helm repo add"] + Helm -->|"helm pull / install"| K8s["cluster"] +``` + +The bucket prefix holds `index.yaml` and one `.tgz` per chart version. Helm reads and writes it through the plugin, so the repository is reachable from anywhere that can reach RustFS. + +## 1. Install the plugin and initialize the repository + +The `alpine/helm` image ships without `git`, so install the plugin from its release archive instead of the repository URL: + +```bash +cd /tmp +wget -q https://github.com/hypnoglow/helm-s3/releases/download/v0.17.2/helm-s3_0.17.2_linux_amd64.tar.gz +mkdir -p helm-s3 && tar -xzf helm-s3_0.17.2_linux_amd64.tar.gz -C helm-s3 +helm plugin install helm-s3 +``` + +Point the plugin at RustFS through its own environment variables and initialize the repository prefix: + +```bash +export AWS_ENDPOINT=:9000 +export AWS_DISABLE_SSL=true +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_DEFAULT_REGION=us-east-1 + +helm s3 init s3://my-bucket/charts +``` + +```text +Initialized empty repository at s3://my-bucket/charts +``` + +:::note[helm-s3 uses AWS_ENDPOINT, not AWS_ENDPOINT_URL] + +The plugin builds its request URLs from `AWS_ENDPOINT` (host and port, no scheme) plus `AWS_DISABLE_SSL=true` for plain HTTP. The standard `AWS_ENDPOINT_URL` variable is not honored, and the SDK-default virtual-hosted addressing it triggers fails against RustFS with `403 Forbidden`. + +::: + +## 2. Push a chart + +Register the repository before the first push — the plugin reads the Helm repositories file and errors if it is missing: + +```bash +helm repo add rustfs-charts s3://my-bucket/charts +``` + +Create and package a demo chart, then push it. `helm s3 push` takes the **repository name**, not the `s3://` URI: + +```bash +helm create demo +helm package demo +helm s3 push ./demo-0.1.0.tgz rustfs-charts +``` + +```text +Successfully uploaded the chart to the repository. +``` + +## 3. Install or pull from the bucket + +Refresh the repository index and confirm the chart is discoverable: + +```bash +helm repo update +helm search repo rustfs-charts/demo +``` + +```text +NAME CHART VERSION APP VERSION DESCRIPTION +rustfs-charts/demo 0.1.0 1.16.0 A Helm chart for Kubernetes +``` + +Pull the chart back out of RustFS: + +```bash +helm pull rustfs-charts/demo --destination ./pullback +tar -tzf ./pullback/demo-0.1.0.tgz | head -2 +``` + +```text +demo/Chart.yaml +demo/values.yaml +``` + +![Helm chart repository objects in the RustFS Console](./images/rustfs-helm-charts.png) + +## 4. Stop or reset + +Charts are plain bucket objects, so cleanup is a prefix delete: + +```bash +aws s3 rm s3://my-bucket/charts --recursive \ + --endpoint-url http://:9000 +``` + +## Troubleshooting + +### `head s3 object: Forbidden` on `helm s3 init` + +The plugin is using virtual-hosted addressing — `AWS_ENDPOINT` and `AWS_DISABLE_SSL` are not set. Export them exactly as shown in step 1. + +### `check if index exists in the storage` fails with a git error + +The image has no `git`, so `helm plugin install ` cannot clone. Install from the release archive as shown in step 1. + +### `The index file already exists under the provided URI` + +The repository prefix was initialized before. Re-run with `helm s3 init --ignore-if-exists s3://my-bucket/charts`, or pass `--force` to replace the index. + +### `repo with name s3://... not found` on push + +`helm s3 push` expects the repository name registered with `helm repo add` — use `helm s3 push ./demo-0.1.0.tgz rustfs-charts`. + +### `couldn't load repositories file` during push + +Helm's `repositories.yaml` does not exist yet in this environment. Run `helm repo add rustfs-charts s3://my-bucket/charts` before pushing. + +## Next steps + +- Review [S3 compatibility notes](/administration/protocols/s3) before hosting larger repositories with many chart versions. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token). +- Follow the [helm-s3 documentation](https://github.com/hypnoglow/helm-s3) for repository reindexing, chart deletion, and CI usage on the same bucket. diff --git a/content/en/developer/integration/devops/images/rustfs-helm-charts.png b/content/en/developer/integration/devops/images/rustfs-helm-charts.png new file mode 100644 index 000000000..c63042b28 Binary files /dev/null and b/content/en/developer/integration/devops/images/rustfs-helm-charts.png differ diff --git a/content/en/developer/integration/devops/meta.json b/content/en/developer/integration/devops/meta.json index 55eada6ae..1f41e2a72 100644 --- a/content/en/developer/integration/devops/meta.json +++ b/content/en/developer/integration/devops/meta.json @@ -5,6 +5,7 @@ "opensearch", "gitea", "jenkins", - "terraform" + "terraform", + "helm" ] } diff --git a/content/en/developer/integration/observability/fluent-bit.md b/content/en/developer/integration/observability/fluent-bit.md new file mode 100644 index 000000000..fea1c6865 --- /dev/null +++ b/content/en/developer/integration/observability/fluent-bit.md @@ -0,0 +1,154 @@ +--- +title: "Fluent Bit" +description: "Ship logs and events from Fluent Bit into a RustFS bucket through the S3 output plugin." +--- + +This guide connects [Fluent Bit](https://github.com/fluent/fluent-bit) — the CNCF log processor and forwarder — to **RustFS** as a log destination. You will run Fluent Bit with its `s3` output, buffer records on disk, upload them into a RustFS bucket, and read a delivered log file back. The workflow was verified with `fluent/fluent-bit:3.2.10` writing to a RustFS bucket over path-style addressing. + +You need Docker. This deployment is intended for local integration testing, not production. + +## Architecture + +```mermaid +flowchart LR + Input["Inputs: tail, dummy, http"] -->|"tagged records"| FB["Fluent Bit"] + FB -->|"buffer (store_dir)"| Disk["Local buffer"] + Disk -->|"PutObject"| RustFS["RustFS :9000"] + Reader["aws cli / Console"] -->|"JSON lines"| RustFS +``` + +The S3 output buffers tagged records in `store_dir` and uploads a file once it reaches `total_file_size`, or when `upload_timeout` expires — whichever happens first. Each uploaded object is a file of JSON lines. + +## 1. Create the bucket + +The S3 output never creates buckets, and uploads fail with `NoSuchBucket` when the bucket is missing. Create it first, replacing all connection placeholders: + +```bash +aws s3 mb s3://my-bucket \ + --endpoint-url http://:9000 +``` + +## 2. Configure the S3 output + +Create the Fluent Bit configuration: + +```text title="fluent-bit.conf" +[SERVICE] + Flush 2 + Log_Level info + +[INPUT] + Name dummy + Tag rustfs.test + Rate 200 + +[OUTPUT] + Name s3 + Match * + endpoint http://:9000 + region us-east-1 + bucket my-bucket + total_file_size 1M + use_put_object On + upload_timeout 30s + store_dir /tmp/flb-store + json_date_key date + json_date_format iso8601 +``` + +`use_put_object On` uploads each buffered file with a single `PutObject` call. `upload_timeout` forces an upload after 30 seconds even when the buffer has not reached `total_file_size` — low-volume inputs need it or their records stay on local disk. + +:::note[Custom endpoints use path-style by default] + +Fluent Bit 3.2 has no `s3_force_path_style` option — the plugin rejects it as an unknown property. When `endpoint` points at a custom S3 service, requests address the bucket path-style, which matches RustFS defaults. No extra flag is required. + +::: + +## 3. Run Fluent Bit + +Start the container with credentials passed through the standard AWS environment variables: + +```bash +docker run -d --name fluent-bit \ + -e AWS_ACCESS_KEY_ID= \ + -e AWS_SECRET_ACCESS_KEY= \ + -v "$PWD/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro" \ + fluent/fluent-bit:3.2.10 +``` + +Wait for the first upload, then confirm the worker started cleanly: + +```bash +docker logs fluent-bit 2>&1 | grep "worker #0 started" +``` + +```text +[output:s3:s3.0] worker #0 started +``` + +## 4. Verify the uploaded objects + +List the bucket — the default key layout is `fluent-bit-logs//YYYY/MM/DD/HH/MM/`: + +```bash +aws s3 ls s3://my-bucket/ --recursive \ + --endpoint-url http://:9000 +``` + +```text +2026-10-25 01:13:28 1021326 fluent-bit-logs/rustfs.test/2026/10/25/01/13/59-objectbmjH9744 +2026-10-25 01:16:05 333735 fluent-bit-logs/rustfs.test/2026/10/25/01/15/34-objectKfCq13io +``` + +Read one object back — every line is a JSON record: + +```bash +aws s3 cp s3://my-bucket/fluent-bit-logs/rustfs.test/2026/10/25/01/13/59-objectbmjH9744 - \ + --endpoint-url http://:9000 | head -2 +``` + +```json +{"date":"2026-10-25T01:13:59.204844Z","message":"dummy"} +{"date":"2026-10-25T01:13:59.206396Z","message":"dummy"} +``` + +![Fluent Bit logs delivered to the RustFS Console](./images/rustfs-fluentbit-logs.png) + +## 5. Stop or reset + +To tear down the demo while keeping the bucket objects: + +```bash +docker rm -f fluent-bit +``` + +To delete the uploaded logs: + +```bash +aws s3 rm s3://my-bucket/fluent-bit-logs --recursive \ + --endpoint-url http://:9000 +``` + +## Troubleshooting + +### `PutObject request failed` with `NoSuchBucket` + +The output bucket does not exist. Create it as shown in step 1 — the plugin never creates buckets. + +### `[config] s3: unknown configuration property 's3_force_path_style'` + +Fluent Bit 3.2 does not have this option. Remove it; custom endpoints already use path-style addressing. + +### Objects never appear and the log stays quiet + +The buffer has not reached `total_file_size` and no `upload_timeout` is set. Add `upload_timeout 30s` so low-volume inputs flush periodically. + +### `InvalidAccessKeyId` in the Fluent Bit log + +The credential environment variables are missing from the container, or the values are wrong. The S3 output reads `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. + +## Next steps + +- Review [S3 compatibility notes](/administration/protocols/s3) before enabling multipart uploads or compression on this output. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token). +- Follow the [Fluent Bit S3 output documentation](https://docs.fluentbit.io/manual/pipeline/outputs/s3) for `s3_key_format`, compression, and `tail` input tuning on the same bucket. diff --git a/content/en/developer/integration/observability/images/rustfs-fluentbit-logs.png b/content/en/developer/integration/observability/images/rustfs-fluentbit-logs.png new file mode 100644 index 000000000..e395d48ce Binary files /dev/null and b/content/en/developer/integration/observability/images/rustfs-fluentbit-logs.png differ diff --git a/content/en/developer/integration/observability/meta.json b/content/en/developer/integration/observability/meta.json index 97ef4d6e6..dd9c3a167 100644 --- a/content/en/developer/integration/observability/meta.json +++ b/content/en/developer/integration/observability/meta.json @@ -1,6 +1,7 @@ { "title": "Observability", "pages": [ + "fluent-bit", "fluentd", "greptimedb", "loki", diff --git a/content/fr/developer/integration/ai/images/rustfs-sglang-models.png b/content/fr/developer/integration/ai/images/rustfs-sglang-models.png new file mode 100644 index 000000000..2ba97d48b Binary files /dev/null and b/content/fr/developer/integration/ai/images/rustfs-sglang-models.png differ diff --git a/content/fr/developer/integration/ai/meta.json b/content/fr/developer/integration/ai/meta.json index a94881703..02233008e 100644 --- a/content/fr/developer/integration/ai/meta.json +++ b/content/fr/developer/integration/ai/meta.json @@ -3,6 +3,7 @@ "pages": [ "mlflow", "ray", + "sglang", "vllm" ] } diff --git a/content/fr/developer/integration/ai/sglang.md b/content/fr/developer/integration/ai/sglang.md new file mode 100644 index 000000000..0dc4bee3f --- /dev/null +++ b/content/fr/developer/integration/ai/sglang.md @@ -0,0 +1,144 @@ +--- +title: "SGLang" +description: "Servir l'inférence LLM avec SGLang en streamant les poids du modèle directement depuis un bucket RustFS." +--- + +Ce guide connecte [SGLang](https://github.com/sgl-project/sglang) — le framework de service pour grands modèles de langage et modèles multimodaux — à **RustFS** comme stockage des poids de modèle. Vous allez charger un modèle dans un bucket RustFS, pointer SGLang vers l'URI `s3://` et servir des requêtes compatibles OpenAI avec les poids streamés directement depuis le bucket. Le workflow a été vérifié avec `lmsysorg/sglang:v0.5.22-xeon` (SGLang 0.5.22, moteur CPU) servant `Qwen/Qwen2.5-0.5B-Instruct` depuis un bucket RustFS sur un hôte CPU uniquement. + +Vous avez besoin de Docker et d'un petit modèle Hugging Face. Ce déploiement est destiné aux tests d'intégration locaux, pas à la production. + +## Architecture + +```mermaid +flowchart LR + Upload["aws s3 cp"] -->|"weights"| RustFS["RustFS :9000"] + RustFS -->|"s3:// runai streamer"| SGLang["SGLang :30000"] + Client["OpenAI SDK / curl"] -->|"completions"| SGLang +``` + +Quand `--model-path` est une URI de stockage objet, SGLang bascule automatiquement vers son format de chargement `runai_streamer` : les fichiers de métadonnées (`config.json`, tokenizer) sont téléchargés une fois dans le cache local, puis les tenseurs de poids sont streamés à la demande pendant que le moteur charge. Le bucket reste l'unique copie du modèle, donc les hôtes de service n'ont pas besoin de magasin de modèles local. + +:::note[L'image CPU n'embarque pas le streamer S3] + +L'image CPU `xeon` n'inclut pas `runai-model-streamer-s3` ; démarrer le serveur avec un chemin de modèle `s3://` échoue avec `ImportError: S3 files module not found`. Installez le paquet dans le conteneur avant de lancer le serveur — l'environnement virtuel de l'image est géré avec `uv`, qui n'a pas de module `pip`. + +::: + +## 1. Charger le modèle dans RustFS + +Récupérez les fichiers du modèle depuis Hugging Face et copiez-les dans le bucket, en remplaçant tous les paramètres fictifs de connexion : + +```bash +docker run --rm -v "$PWD/models:/models" --entrypoint python3 \ + lmsysorg/sglang:v0.5.22-xeon -c \ + 'from huggingface_hub import snapshot_download; snapshot_download("Qwen/Qwen2.5-0.5B-Instruct", local_dir="/models/qwen2.5-0.5b-instruct")' + +aws s3 cp models/qwen2.5-0.5b-instruct \ + s3://my-bucket/sglang-models/qwen2.5-0.5b-instruct --recursive \ + --endpoint-url http://:9000 \ + --acl private +``` + +Exportez les identifiants pour les commandes suivantes : + +```bash +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_DEFAULT_REGION=us-east-1 +``` + +Utilisez un préfixe par modèle afin que plusieurs modèles partagent le même bucket. Le streamer ne lit que les fichiers de poids au format `.safetensors`. + +## 2. Démarrer SGLang sur le bucket + +Lancez l'image CPU, installez le paquet du streamer S3 et démarrez le serveur sur l'URI du bucket : + +```bash +docker run -d --name sglang -p 30000:30000 \ + -e AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY \ + -e AWS_DEFAULT_REGION \ + -e AWS_ENDPOINT_URL=http://:9000 \ + --entrypoint bash lmsysorg/sglang:v0.5.22-xeon -c \ + 'uv pip install -q runai-model-streamer-s3 && \ + python3 -m sglang.launch_server \ + --model-path s3://my-bucket/sglang-models/qwen2.5-0.5b-instruct \ + --served-model-name qwen2.5-0.5b-instruct \ + --host 0.0.0.0 --port 30000 \ + --device cpu --trust-remote-code \ + --disable-overlap-schedule \ + --mem-fraction-static 0.45' +``` + +`AWS_ENDPOINT_URL` dirige le streamer vers RustFS ; les requêtes vers le point de terminaison personnalisé utilisent l'adressage path-style. `--mem-fraction-static` fixe la part de mémoire hôte réservée au cache KV sur le backend CPU — augmentez-la sur les hôtes disposant de plus de RAM libre. Attendez la ligne de démarrage : + +```bash +docker logs -f sglang +``` + +```text +The server is fired up and ready to roll! +``` + +## 3. Exécuter l'inférence + +Vérifiez que le modèle est servi, puis envoyez une requête de complétion compatible OpenAI : + +```bash +curl -s http://localhost:30000/v1/models +``` + +```json +{"object":"list","data":[{"id":"qwen2.5-0.5b-instruct","object":"model","owned_by":"sglang","root":"qwen2.5-0.5b-instruct","max_model_len":32768}]} +``` + +```bash +curl -s http://localhost:30000/v1/completions \ + -H "Content-Type: application/json" \ + -d '{"model":"qwen2.5-0.5b-instruct","prompt":"RustFS is","max_tokens":16,"temperature":0}' +``` + +```json +{"object":"text_completion","model":"qwen2.5-0.5b-instruct","choices":[{"index":0,"text":" a file system for the Linux kernel that is designed to be used in embedded systems","finish_reason":"length"}],"usage":{"prompt_tokens":4,"total_tokens":20,"completion_tokens":16}} +``` + +![Poids du modèle SGLang dans la console RustFS](./images/rustfs-sglang-models.png) + +## 4. Arrêter ou réinitialiser + +Pour démonter la démo tout en conservant les objets du bucket : + +```bash +docker rm -f sglang +``` + +Pour supprimer le modèle stocké : + +```bash +aws s3 rm s3://my-bucket/sglang-models --recursive \ + --endpoint-url http://:9000 +``` + +## Dépannage + +### `ImportError: S3 files module not found` + +Le backend S3 du streamer RunAI est absent. Exécutez `uv pip install runai-model-streamer-s3` dans le conteneur avant de démarrer le serveur, comme indiqué à l'étape 2. + +### `pip: command not found` ou `No module named pip` + +Le `/opt/.venv` de l'image a été créé par `uv` sans `pip`. Utilisez `uv pip install` au lieu de `pip install`. + +### `Loaded weights leave no GPU memory for the KV cache` + +`--mem-fraction-static` est trop faible pour les poids chargés sur cet hôte. Le message d'erreur indique la valeur minimale viable — relancez le serveur avec une fraction plus grande (par exemple `0.45` sur un hôte de 16 Go pour un modèle de 0,5 B). + +### Les requêtes échouent avec des erreurs de signature ou d'accès + +Vérifiez que les quatre variables sont définies pour le conteneur du serveur : `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION` et `AWS_ENDPOINT_URL` avec le schéma inclus. + +## Prochaines étapes + +- Consultez les [notes de compatibilité S3](/administration/protocols/s3) avant d'adopter d'autres opérations de service. +- Créez des identifiants de production dédiés avec la [gestion des clés d'accès](/security-compliance/iam/access-token). +- Suivez la [documentation SGLang](https://docs.sglang.io/) pour le parallélisme de tenseurs, la quantification et des modèles plus grands sur le même magasin de modèles adossé au bucket. diff --git a/content/fr/developer/integration/big-data/images/rustfs-pulsar-offload.png b/content/fr/developer/integration/big-data/images/rustfs-pulsar-offload.png new file mode 100644 index 000000000..ca6b201b3 Binary files /dev/null and b/content/fr/developer/integration/big-data/images/rustfs-pulsar-offload.png differ diff --git a/content/fr/developer/integration/big-data/meta.json b/content/fr/developer/integration/big-data/meta.json index a158e1478..1166bdfc9 100644 --- a/content/fr/developer/integration/big-data/meta.json +++ b/content/fr/developer/integration/big-data/meta.json @@ -14,6 +14,7 @@ "pyiceberg", "spark", "seatunnel", - "zeppelin" + "zeppelin", + "pulsar" ] } diff --git a/content/fr/developer/integration/big-data/pulsar.md b/content/fr/developer/integration/big-data/pulsar.md new file mode 100644 index 000000000..45c016520 --- /dev/null +++ b/content/fr/developer/integration/big-data/pulsar.md @@ -0,0 +1,170 @@ +--- +title: "Apache Pulsar" +description: "Décharger le backlog froid des topics Pulsar de BookKeeper vers un bucket RustFS avec le stockage hiérarchisé." +--- + +Ce guide connecte [Apache Pulsar](https://github.com/apache/pulsar) à **RustFS** via son offloader S3 de stockage hiérarchisé (tiered storage). Vous allez exécuter un broker standalone dont le déchargement pointe vers un bucket RustFS, produire des messages, déclencher un déchargement de ledger, puis relire les messages déchargés depuis le bucket. Le workflow a été vérifié avec `apachepulsar/pulsar-all:4.0.4`, qui décharge un topic vers RustFS et sert des lectures à froid depuis celui-ci. + +Vous avez besoin de Docker. Ce déploiement est destiné aux tests d'intégration locaux, pas à la production. + +## Architecture + +```mermaid +flowchart LR + Producer["pulsar-client produce"] --> Broker["Pulsar standalone"] + Broker -->|"hot ledgers"| BK["BookKeeper"] + Broker -->|"offload ledgers"| RustFS["RustFS :9000"] + Consumer["pulsar-client consume"] -->|"cold reads"| Broker +``` + +Les anciens ledgers sont téléversés dans le bucket sous forme de blocs de données `-ledger-` plus un petit objet `-index`. Quand un consommateur demande des messages qui ne sont plus dans BookKeeper, le broker les relit de façon transparente depuis le bucket. + +## 1. Démarrer le broker standalone avec déchargement + +L'image simple `apachepulsar/pulsar` n'embarque pas les paquets d'offloader et échoue avec `No offloader found for driver 'S3'` — utilisez l'image `pulsar-all`, qui les inclut. Le broker est plafonné à un petit tas : + +```bash +docker run -d --name pulsar -p 6650:6650 -p 8080:8080 \ + -e ACCESS_KEY_ID= \ + -e ACCESS_KEY_SECRET= \ + -e PULSAR_MEM="-Xms512m -Xmx512m" \ + --entrypoint bash apachepulsar/pulsar-all:4.0.4 -c ' + sed -i -e "s|^managedLedgerMaxEntriesPerLedger=.*|managedLedgerMaxEntriesPerLedger=100|" \ + -e "s|^managedLedgerMinLedgerRolloverTimeMinutes=.*|managedLedgerMinLedgerRolloverTimeMinutes=1|" \ + /pulsar/conf/standalone.conf + printf "\nmanagedLedgerOffloadDriver=S3\nmanagedLedgerOffloadBucket=my-bucket\nmanagedLedgerOffloadServiceEndpoint=http://:9000\nmanagedLedgerOffloadRegion=us-east-1\n" >> /pulsar/conf/standalone.conf + exec bin/pulsar standalone -nss + ' +``` + +Les deux modifications `managedLedger*` rapetissent les ledgers pour qu'un basculement survienne pendant la démo ; le bloc ajouté active le déchargement. + +:::note[Le pilote S3 lit les clés managedLedgerOffload*, pas s3ManagedLedgerOffload*] + +Pour le pilote `S3`, l'offloader résout les clés génériques `managedLedgerOffloadBucket`, `managedLedgerOffloadServiceEndpoint` et `managedLedgerOffloadRegion`. Les noms `s3ManagedLedgerOffload*` appartiennent au pilote `aws-s3` et sont ignorés ici — avec eux, l'offloader plante avec `NullPointerException: endpoint`. Il en va de même pour les identifiants : le pilote `S3` lit les variables d'environnement `ACCESS_KEY_ID` et `ACCESS_KEY_SECRET`, pas la paire `AWS_*`. + +::: + +Attendez le broker : + +```bash +docker logs -f pulsar 2>&1 | grep -m1 "The ledger offloader was created" +``` + +## 2. Produire des messages et garder le topic en vie + +Les topics sans subscription sont supprimés par le nettoyage des topics inactifs en quelques minutes, ce qui annule la démo en cours de route. Produisez un premier lot, puis créez une subscription durable qui maintient le topic enregistré : + +```bash +docker exec pulsar bin/pulsar-client produce -m "hello rustfs offload" -n 300 \ + persistent://public/default/rustfs-test +``` + +```text +300 messages successfully produced +``` + +```bash +timeout 30 docker exec pulsar bin/pulsar-client consume -s keepalive -n 1 -p Earliest \ + persistent://public/default/rustfs-test +``` + +## 3. Faire basculer un ledger et déclencher le déchargement + +Un ledger ne bascule que si une écriture survient après `managedLedgerMinLedgerRolloverTimeMinutes` — la vérification se fait à l'écriture, pas en tâche de fond. Attendez au moins une minute, produisez un autre lot pour forcer le basculement, puis déchargez tout ce qui précède le dernier kilooctet : + +```bash +sleep 65 +docker exec pulsar bin/pulsar-client produce -m "rollover" -n 120 \ + persistent://public/default/rustfs-test +docker exec pulsar bin/pulsar-admin topics offload -s 1K \ + persistent://public/default/rustfs-test +``` + +```text +Offload triggered for persistent://public/default/rustfs-test for messages before 11:0:-1 +``` + +Le déchargement prend un moment — attendez la fin : + +```bash +sleep 30 +docker exec pulsar bin/pulsar-admin topics offload-status \ + persistent://public/default/rustfs-test +``` + +```text +Offload was a success +``` + +Confirmez que les objets de ledger ont atterri dans RustFS — bloc de données plus index, à la racine du bucket : + +```bash +aws s3 ls s3://my-bucket/ --recursive \ + --endpoint-url http://:9000 | grep ledger +``` + +```text +2026-10-25 02:34:49 21057 7f9379cc-c21d-4829-a857-4c39ec02e391-ledger-9 +2026-10-25 02:34:49 214 7f9379cc-c21d-4829-a857-4c39ec02e391-ledger-9-index +``` + +## 4. Relire les messages déchargés + +Consommez depuis la position la plus ancienne avec une subscription neuve. Le premier ledger ne vit plus que dans RustFS, donc chaque message renvoyé par le broker est une lecture à froid depuis le bucket : + +```bash +timeout 90 docker exec pulsar bin/pulsar-client consume -s coldread -n 420 -p Earliest \ + persistent://public/default/rustfs-test 2>&1 | grep -c "^-----" +``` + +```text +420 +``` + +Les 420 messages — les 300 du ledger déchargé plus les 120 du ledger actif — reviennent via le bucket. + +![Objets de ledger Pulsar déchargés dans la console RustFS](./images/rustfs-pulsar-offload.png) + +## 5. Arrêter ou réinitialiser + +Pour démonter le broker tout en conservant les objets du bucket : + +```bash +docker rm -f pulsar +``` + +Pour supprimer les ledgers déchargés : + +```bash +aws s3 rm s3://my-bucket/ --recursive --exclude "*" --include "*ledger*" \ + --endpoint-url http://:9000 +``` + +## Dépannage + +### `No offloader found for driver 'S3'. Please make sure you dropped the offloader nar packages` + +L'image n'inclut pas les NAR d'offloader. Utilisez `apachepulsar/pulsar-all` comme à l'étape 1. + +### `NullPointerException: endpoint` lors du déchargement + +L'offloader n'a lu aucun endpoint de service — les clés `managedLedgerOffload*` manquent ou ont été écrites avec les noms `s3ManagedLedgerOffload*` du pilote `aws-s3`. Ajoutez le bloc exact de l'étape 1 et redémarrez. + +### `Couldn't get the access key id.` dans le journal du broker + +Le pilote `S3` ne lit pas `AWS_ACCESS_KEY_ID`. Exportez `ACCESS_KEY_ID` et `ACCESS_KEY_SECRET` sur le conteneur du broker. + +### `Topic ... not found` pendant la démo + +Le nettoyage des topics inactifs a supprimé le topic faute de subscription. Relancez l'étape 2 et conservez la subscription `keepalive`. + +### `Offload was a success` mais aucun objet n'apparaît + +Seuls les ledgers fermés antérieurs au seuil de taille sont téléversés, et un ledger bascule à la prochaine écriture après l'âge minimal. Produisez un autre lot, puis déclenchez à nouveau le déchargement. + +## Prochaines étapes + +- Consultez les [notes de compatibilité S3](/administration/protocols/s3) avant d'activer des seuils de déchargement automatiques sur les namespaces. +- Créez des identifiants de production dédiés avec la [gestion des clés d'accès](/security-compliance/iam/access-token). +- Suivez la [documentation du stockage hiérarchisé de Pulsar](https://pulsar.apache.org/docs/next/cookbooks-tiered-storage/) pour les seuils de namespace, les politiques de rétention et les brokers multi-nœuds contre le même bucket. diff --git a/content/fr/developer/integration/cloud-native/camel.md b/content/fr/developer/integration/cloud-native/camel.md new file mode 100644 index 000000000..04363841d --- /dev/null +++ b/content/fr/developer/integration/cloud-native/camel.md @@ -0,0 +1,127 @@ +--- +title: "Apache Camel" +description: "Router les données vers et depuis un bucket RustFS avec des routes d'intégration Apache Camel." +--- + +Ce guide connecte [Apache Camel](https://github.com/apache/camel) — le cadre d'intégration polyvalent — à **RustFS** via son composant S3. Vous allez écrire une intégration à deux routes en YAML : une route productrice qui écrit un message dans le bucket à intervalle régulier, et une route consommatrice qui relit ces objets et les journalise. Le workflow a été vérifié avec Camel JBang sur `eclipse-temurin:21-jdk`, en utilisant le composant `aws2-s3` avec un endpoint redéfini et l'adressage path-style. + +Vous avez besoin de Docker. Ce déploiement est destiné aux tests d'intégration locaux, pas à la production. + +## Architecture + +```mermaid +flowchart LR + Timer["timer:tick"] -->|"CamelAwsS3Key header"| Producer["aws2-s3 producer"] + Producer -->|"PutObject"| RustFS["RustFS :9000"] + RustFS -->|"prefix=camel/ poll"| Consumer["aws2-s3 consumer"] + Consumer --> Log["log:from-rustfs"] +``` + +Camel JBang exécute directement le fichier de routes et résout automatiquement les dépendances du composant S3 — aucun échafaudage de projet, aucune étape de build ou de déploiement. + +## 1. Écrire la route d'intégration + +Créez le fichier de routes. Les identifiants proviennent de la chaîne d'identifiants AWS par défaut (variables d'environnement), donc les URI ne portent que l'adressage : + +```yaml title="route.yaml" +- from: + uri: "timer:tick" + parameters: + period: "5000" + steps: + - setBody: + simple: "Hello from Camel at ${date:now:yyyy-MM-dd HH:mm:ss}" + - setHeader: + name: "CamelAwsS3Key" + simple: "camel/hello-${date:now:HHmmss}.txt" + - to: + uri: "aws2-s3://my-bucket?overrideEndpoint=true&uriEndpointOverride=http://:9000&forcePathStyle=true&useDefaultCredentialsProvider=true" +- from: + uri: "aws2-s3://my-bucket?prefix=camel/&deleteAfterRead=false&maxMessagesPerPoll=5&overrideEndpoint=true&uriEndpointOverride=http://:9000&forcePathStyle=true&useDefaultCredentialsProvider=true" + steps: + - to: + uri: "log:from-rustfs" +``` + +Le producteur fixe la clé d'objet via l'en-tête `CamelAwsS3Key` — ici un horodatage par message, de sorte que chaque battement écrit un nouvel objet. Le consommateur interroge le préfixe `camel/` avec `deleteAfterRead=false` : les objets restent dans le bucket après consommation. + +:::note[Nom du composant et options d'endpoint] + +Le composant résolu par Camel JBang dans ce workflow est `aws2-s3`. `overrideEndpoint=true` active l'endpoint personnalisé, `uriEndpointOverride` porte son URL, `forcePathStyle=true` sélectionne l'adressage path-style attendu par RustFS, et `useDefaultCredentialsProvider=true` fait lire les identifiants depuis les variables d'environnement standard. + +::: + +## 2. Exécuter la route avec Camel JBang + +Exécutez l'intégration dans un conteneur avec les identifiants exportés. JBang demande de faire confiance aux sources au premier usage — ajoutez donc les règles de confiance avant de démarrer : + +```bash +docker run --rm --network bridge \ + -e AWS_ACCESS_KEY_ID= \ + -e AWS_SECRET_ACCESS_KEY= \ + -e AWS_REGION=us-east-1 \ + -v "$PWD:/routes" \ + --entrypoint bash eclipse-temurin:21-jdk -c ' + export JBANG_DIR=/root/.jbang PATH=/root/.jbang/bin:$PATH + curl -Ls https://sh.jbang.dev | bash >/dev/null 2>&1 + jbang trust add https://github.com/apache/ 2>/dev/null || true + jbang trust add /routes 2>/dev/null || true + cd /routes && jbang camel@apache/camel run route.yaml + ' +``` + +La première exécution télécharge le runtime Camel et ses dépendances, puis démarre les routes. Le consommateur journalise chaque objet lu : + +```text +2026-10-25 02:02:41.069 INFO 120 --- [-s3://my-bucket] from-rustfs : Exchange[ExchangePattern: InOnly, BodyType: byte[], Body: Hello from Camel at 2026-10-25 02:02:38] +``` + +## 3. Vérifier les objets + +Confirmez que les objets du producteur sont bien dans RustFS, en remplaçant tous les paramètres fictifs de connexion : + +```bash +aws s3 ls s3://my-bucket/camel/ \ + --endpoint-url http://:9000 +``` + +```text +2026-10-25 02:02:13 39 hello-020213.txt +2026-10-25 02:02:18 39 hello-020218.txt +2026-10-25 02:02:23 39 hello-020223.txt +``` + +![Objets de route Apache Camel dans la console RustFS](./images/rustfs-camel-route.png) + +## 4. Arrêter ou réinitialiser + +Arrêtez le conteneur pour terminer les routes. Supprimer les objets de démonstration : + +```bash +aws s3 rm s3://my-bucket/camel --recursive \ + --endpoint-url http://:9000 +``` + +## Dépannage + +### `Cannot find component with name: aws-s3` + +Votre version de Camel résout le composant sous un autre nom — ce workflow utilise `aws2-s3`. Utilisez le nom que votre runtime propose dans la liste de suggestions (`Did you mean: ... aws2-s3 ...`). + +### `is not from a trusted source` + +JBang refuse d'exécuter du code non fiable. Exécutez `jbang trust add https://github.com/apache/` et `jbang trust add /routes` avant `jbang camel@apache/camel run`, comme à l'étape 2. + +### `useDefaultCredentialsProvider is set to false` + +Le composant n'a trouvé aucune configuration d'identifiants dans l'URI. Ajoutez `useDefaultCredentialsProvider=true` et exportez `AWS_ACCESS_KEY_ID` et `AWS_SECRET_ACCESS_KEY`, ou configurez directement les options d'endpoint `accessKey` et `secretKey`. + +### Le consommateur ne lit rien + +Le consommateur n'interroge que le préfixe donné par `prefix=camel/`. Confirmez que le producteur a tourné (lignes de journal `timer` visibles) et que son en-tête `CamelAwsS3Key` commence par `camel/`. + +## Prochaines étapes + +- Consultez les [notes de compatibilité S3](/administration/protocols/s3) avant d'adopter des opérations multiparties ou en flux sur ce composant. +- Créez des identifiants de production dédiés avec la [gestion des clés d'accès](/security-compliance/iam/access-token). +- Suivez la [documentation du composant S3 de Camel](https://camel.apache.org/components/latest/aws2-s3-component.html) pour les plages de téléchargement, les opérations de bucket et le réglage du consommateur sur les mêmes routes. diff --git a/content/fr/developer/integration/cloud-native/dapr.md b/content/fr/developer/integration/cloud-native/dapr.md new file mode 100644 index 000000000..43294d898 --- /dev/null +++ b/content/fr/developer/integration/cloud-native/dapr.md @@ -0,0 +1,144 @@ +--- +title: "Dapr" +description: "Lire et écrire des objets RustFS depuis n'importe quelle application Dapr via le binding AWS S3." +--- + +Ce guide connecte [Dapr](https://github.com/dapr/dapr) — le runtime portable de la CNCF pour applications distribuées — à **RustFS** via son binding AWS S3. Vous allez déclarer un composant de binding adossé à un bucket RustFS, exécuter un sidecar et utiliser l'API HTTP du binding pour créer un objet, le relire et le confirmer de façon indépendante. Le workflow a été vérifié avec `daprio/dapr:1.19.0` contre un bucket RustFS avec l'adressage path-style. + +Vous avez besoin de Docker. Ce déploiement est destiné aux tests d'intégration locaux, pas à la production. + +## Architecture + +```mermaid +flowchart LR + App["Your app"] -->|"POST /v1.0/bindings/rustfs-s3"| Daprd["daprd sidecar :3500"] + Daprd -->|"bindings.aws.s3"| RustFS["RustFS :9000"] +``` + +Les applications ne détiennent aucun identifiant ni endpoint S3. Le sidecar expose le bucket comme un binding nommé, et chaque opération est un POST vers l'endpoint du binding avec une `operation` et des `metadata` optionnelles. + +## 1. Déclarer le composant de binding + +Créez le fichier du composant. L'`endpoint` inclut le schéma et `forcePathStyle` correspond à l'adressage path-style de RustFS : + +```yaml title="rustfs-s3.yaml" +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: rustfs-s3 +spec: + type: bindings.aws.s3 + version: v1 + metadata: + - name: bucket + value: my-bucket + - name: endpoint + value: http://:9000 + - name: accessKey + value: + - name: secretKey + value: + - name: region + value: us-east-1 + - name: forcePathStyle + value: "true" +``` + +## 2. Démarrer le sidecar + +Exécutez `daprd` avec le répertoire des composants monté. `--resources-path` remplace le `--components-path` obsolète : + +```bash +docker run -d --name dapr \ + -v "$PWD:/components" \ + daprio/dapr:1.19.0 /daprd \ + --app-id s3demo \ + --dapr-http-port 3500 \ + --resources-path /components +``` + +Confirmez l'initialisation du binding — le journal se termine par : + +```bash +docker logs dapr 2>&1 | grep "successful init" +``` + +```text +successful init for output binding (rustfs-s3 (bindings.aws.s3/v1)) +``` + +## 3. Créer un objet via le binding + +Appelez le binding depuis n'importe quel client HTTP atteignant le sidecar. La métadonnée `key` fixe le nom de l'objet : + +```bash +curl -s -X POST http://localhost:3500/v1.0/bindings/rustfs-s3 \ + -H "Content-Type: application/json" \ + -d '{"operation":"create","data":"hello from dapr","metadata":{"key":"dapr/hello.txt"}}' +``` + +```json +{"location":"http://:9000/my-bucket/dapr/hello.txt","versionID":null} +``` + +## 4. Relire l'objet + +L'opération `get` renvoie le contenu de l'objet dans le corps de la réponse : + +```bash +curl -s -X POST http://localhost:3500/v1.0/bindings/rustfs-s3 \ + -H "Content-Type: application/json" \ + -d '{"operation":"get","metadata":{"key":"dapr/hello.txt"}}' +``` + +```text +hello from dapr +``` + +Confirmez le même objet depuis un client indépendant : + +```bash +aws s3 cp s3://my-bucket/dapr/hello.txt - \ + --endpoint-url http://:9000 +``` + +```text +hello from dapr +``` + +![Objets du binding Dapr dans la console RustFS](./images/rustfs-dapr-binding.png) + +## 5. Arrêter ou réinitialiser + +Pour démonter le sidecar tout en conservant les objets du bucket : + +```bash +docker rm -f dapr +``` + +Pour supprimer l'objet de démonstration : + +```bash +aws s3 rm s3://my-bucket/dapr/hello.txt \ + --endpoint-url http://:9000 +``` + +## Dépannage + +### Le binding échoue à s'initialiser avec une erreur S3 + +Vérifiez ensemble les quatre valeurs d'adressage : `endpoint` doit inclure le schéma, `region` est requise même pour des endpoints non AWS, et `forcePathStyle` doit valoir `"true"`. Sans `forcePathStyle`, Dapr envoie des requêtes virtual-hosted que RustFS refuse. + +### `no such binding` à l'appel + +Le sidecar n'a pas chargé le composant — confirmez que le fichier se termine par `.yaml`, se trouve dans le répertoire passé à `--resources-path`, et que la ligne d'initialisation de l'étape 2 apparaît dans le journal. + +### `get` renvoie un contenu vide + +La `metadata.key` ne correspond pas à la clé utilisée lors de la création. Les clés sont sensibles à la casse et incluent le préfixe. + +## Prochaines étapes + +- Consultez les [notes de compatibilité S3](/administration/protocols/s3) avant d'adopter les opérations `list`, `delete` ou `presign` du binding. +- Créez des identifiants de production dédiés avec la [gestion des clés d'accès](/security-compliance/iam/access-token) et référencez-les via un store de secrets Dapr plutôt qu'en clair dans le composant. +- Suivez la [documentation du binding S3 de Dapr](https://docs.dapr.io/reference/components-reference/supported-bindings/s3/) pour l'ensemble des opérations et le comportement des tentatives. diff --git a/content/fr/developer/integration/cloud-native/images/rustfs-camel-route.png b/content/fr/developer/integration/cloud-native/images/rustfs-camel-route.png new file mode 100644 index 000000000..3f73da421 Binary files /dev/null and b/content/fr/developer/integration/cloud-native/images/rustfs-camel-route.png differ diff --git a/content/fr/developer/integration/cloud-native/images/rustfs-dapr-binding.png b/content/fr/developer/integration/cloud-native/images/rustfs-dapr-binding.png new file mode 100644 index 000000000..d56de00e0 Binary files /dev/null and b/content/fr/developer/integration/cloud-native/images/rustfs-dapr-binding.png differ diff --git a/content/fr/developer/integration/cloud-native/meta.json b/content/fr/developer/integration/cloud-native/meta.json index 00e256bc5..dfbde1c1b 100644 --- a/content/fr/developer/integration/cloud-native/meta.json +++ b/content/fr/developer/integration/cloud-native/meta.json @@ -3,6 +3,8 @@ "pages": [ "cortex", "flux", - "celld" + "celld", + "camel", + "dapr" ] } diff --git a/content/fr/developer/integration/devops/helm.md b/content/fr/developer/integration/devops/helm.md new file mode 100644 index 000000000..5f0fe53c7 --- /dev/null +++ b/content/fr/developer/integration/devops/helm.md @@ -0,0 +1,137 @@ +--- +title: "Helm" +description: "Héberger un dépôt privé de charts Helm dans un bucket RustFS avec le plugin helm-s3." +--- + +Ce guide connecte [Helm](https://github.com/helm/helm) — le gestionnaire de paquets CNCF pour Kubernetes — à **RustFS** via le plugin [helm-s3](https://github.com/hypnoglow/helm-s3), pour qu'un bucket RustFS devienne un dépôt privé de charts. Vous allez initialiser le dépôt dans le bucket, pousser un chart empaqueté, puis ajouter le dépôt à Helm et rapatrier le chart. Le workflow a été vérifié avec `alpine/helm:3.16.4` et helm-s3 `v0.17.2` sur un bucket RustFS. + +Vous avez besoin de Docker. Ce déploiement est destiné aux tests d'intégration locaux, pas à la production. + +## Architecture + +```mermaid +flowchart LR + Dev["helm package"] -->|"helm s3 push"| Repo["s3://my-bucket/charts"] + Repo -->|"index.yaml"| Helm["helm repo add"] + Helm -->|"helm pull / install"| K8s["cluster"] +``` + +Le préfixe du bucket contient `index.yaml` et un fichier `.tgz` par version de chart. Helm le lit et l'écrit via le plugin, donc le dépôt est accessible partout où RustFS l'est. + +## 1. Installer le plugin et initialiser le dépôt + +L'image `alpine/helm` n'embarque pas `git` — installez donc le plugin depuis son archive de version plutôt que depuis l'URL du dépôt : + +```bash +cd /tmp +wget -q https://github.com/hypnoglow/helm-s3/releases/download/v0.17.2/helm-s3_0.17.2_linux_amd64.tar.gz +mkdir -p helm-s3 && tar -xzf helm-s3_0.17.2_linux_amd64.tar.gz -C helm-s3 +helm plugin install helm-s3 +``` + +Pointez le plugin vers RustFS via ses propres variables d'environnement et initialisez le préfixe du dépôt : + +```bash +export AWS_ENDPOINT=:9000 +export AWS_DISABLE_SSL=true +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_DEFAULT_REGION=us-east-1 + +helm s3 init s3://my-bucket/charts +``` + +```text +Initialized empty repository at s3://my-bucket/charts +``` + +:::note[helm-s3 utilise AWS_ENDPOINT, pas AWS_ENDPOINT_URL] + +Le plugin construit ses URL de requête à partir de `AWS_ENDPOINT` (hôte et port, sans schéma) et de `AWS_DISABLE_SSL=true` pour le HTTP en clair. La variable standard `AWS_ENDPOINT_URL` n'est pas prise en compte, et l'adressage virtual-hosted par défaut du SDK échoue contre RustFS avec `403 Forbidden`. + +::: + +## 2. Pousser un chart + +Enregistrez le dépôt avant la première poussée — le plugin lit le fichier de dépôts de Helm et échoue s'il est absent : + +```bash +helm repo add rustfs-charts s3://my-bucket/charts +``` + +Créez et empaquetez un chart de démonstration, puis poussez-le. `helm s3 push` accepte le **nom du dépôt**, pas l'URI `s3://` : + +```bash +helm create demo +helm package demo +helm s3 push ./demo-0.1.0.tgz rustfs-charts +``` + +```text +Successfully uploaded the chart to the repository. +``` + +## 3. Installer ou rapatrier depuis le bucket + +Rafraîchissez l'index du dépôt et confirmez que le chart est découvrable : + +```bash +helm repo update +helm search repo rustfs-charts/demo +``` + +```text +NAME CHART VERSION APP VERSION DESCRIPTION +rustfs-charts/demo 0.1.0 1.16.0 A Helm chart for Kubernetes +``` + +Rapatriez le chart depuis RustFS : + +```bash +helm pull rustfs-charts/demo --destination ./pullback +tar -tzf ./pullback/demo-0.1.0.tgz | head -2 +``` + +```text +demo/Chart.yaml +demo/values.yaml +``` + +![Objets du dépôt de charts Helm dans la console RustFS](./images/rustfs-helm-charts.png) + +## 4. Arrêter ou réinitialiser + +Les charts sont de simples objets de bucket, donc le nettoyage consiste à supprimer le préfixe : + +```bash +aws s3 rm s3://my-bucket/charts --recursive \ + --endpoint-url http://:9000 +``` + +## Dépannage + +### `head s3 object: Forbidden` sur `helm s3 init` + +Le plugin utilise l'adressage virtual-hosted — `AWS_ENDPOINT` et `AWS_DISABLE_SSL` ne sont pas définis. Exportez-les exactement comme à l'étape 1. + +### `check if index exists in the storage` échoue avec une erreur git + +L'image n'a pas de `git`, donc `helm plugin install ` ne peut pas cloner. Installez depuis l'archive de version comme à l'étape 1. + +### `The index file already exists under the provided URI` + +Le préfixe du dépôt a déjà été initialisé. Relancez avec `helm s3 init --ignore-if-exists s3://my-bucket/charts`, ou passez `--force` pour remplacer l'index. + +### `repo with name s3://... not found` à la poussée + +`helm s3 push` attend le nom de dépôt enregistré via `helm repo add` — utilisez `helm s3 push ./demo-0.1.0.tgz rustfs-charts`. + +### `couldn't load repositories file` pendant la poussée + +Le `repositories.yaml` de Helm n'existe pas encore dans cet environnement. Exécutez `helm repo add rustfs-charts s3://my-bucket/charts` avant de pousser. + +## Prochaines étapes + +- Consultez les [notes de compatibilité S3](/administration/protocols/s3) avant d'héberger des dépôts plus volumineux avec de nombreuses versions de charts. +- Créez des identifiants de production dédiés avec la [gestion des clés d'accès](/security-compliance/iam/access-token). +- Suivez la [documentation helm-s3](https://github.com/hypnoglow/helm-s3) pour la réindexation, la suppression de charts et l'usage CI sur le même bucket. diff --git a/content/fr/developer/integration/devops/images/rustfs-helm-charts.png b/content/fr/developer/integration/devops/images/rustfs-helm-charts.png new file mode 100644 index 000000000..c63042b28 Binary files /dev/null and b/content/fr/developer/integration/devops/images/rustfs-helm-charts.png differ diff --git a/content/fr/developer/integration/devops/meta.json b/content/fr/developer/integration/devops/meta.json index 55eada6ae..1f41e2a72 100644 --- a/content/fr/developer/integration/devops/meta.json +++ b/content/fr/developer/integration/devops/meta.json @@ -5,6 +5,7 @@ "opensearch", "gitea", "jenkins", - "terraform" + "terraform", + "helm" ] } diff --git a/content/fr/developer/integration/observability/fluent-bit.md b/content/fr/developer/integration/observability/fluent-bit.md new file mode 100644 index 000000000..d024b05f5 --- /dev/null +++ b/content/fr/developer/integration/observability/fluent-bit.md @@ -0,0 +1,154 @@ +--- +title: "Fluent Bit" +description: "Acheminer les journaux et événements de Fluent Bit vers un bucket RustFS via le plugin de sortie S3." +--- + +Ce guide connecte [Fluent Bit](https://github.com/fluent/fluent-bit) — le processeur et transmetteur de journaux de la CNCF — à **RustFS** comme destination de journaux. Vous allez exécuter Fluent Bit avec sa sortie `s3`, mettre les enregistrements en tampon sur disque, les téléverser dans un bucket RustFS, puis lire un fichier de journal livré. Le workflow a été vérifié avec `fluent/fluent-bit:3.2.10` écrivant dans un bucket RustFS via l'adressage path-style. + +Vous avez besoin de Docker. Ce déploiement est destiné aux tests d'intégration locaux, pas à la production. + +## Architecture + +```mermaid +flowchart LR + Input["Inputs: tail, dummy, http"] -->|"tagged records"| FB["Fluent Bit"] + FB -->|"buffer (store_dir)"| Disk["Local buffer"] + Disk -->|"PutObject"| RustFS["RustFS :9000"] + Reader["aws cli / Console"] -->|"JSON lines"| RustFS +``` + +La sortie S3 met en tampon les enregistrements étiquetés dans `store_dir` et téléverse un fichier dès qu'il atteint `total_file_size`, ou quand `upload_timeout` expire — selon ce qui survient en premier. Chaque objet téléversé est un fichier de lignes JSON. + +## 1. Créer le bucket + +La sortie S3 ne crée jamais de bucket, et les téléversements échouent avec `NoSuchBucket` quand le bucket est absent. Créez-le d'abord, en remplaçant tous les paramètres fictifs : + +```bash +aws s3 mb s3://my-bucket \ + --endpoint-url http://:9000 +``` + +## 2. Configurer la sortie S3 + +Créez la configuration Fluent Bit : + +```text title="fluent-bit.conf" +[SERVICE] + Flush 2 + Log_Level info + +[INPUT] + Name dummy + Tag rustfs.test + Rate 200 + +[OUTPUT] + Name s3 + Match * + endpoint http://:9000 + region us-east-1 + bucket my-bucket + total_file_size 1M + use_put_object On + upload_timeout 30s + store_dir /tmp/flb-store + json_date_key date + json_date_format iso8601 +``` + +`use_put_object On` téléverse chaque fichier tamponné avec un seul appel `PutObject`. `upload_timeout` force un téléversement après 30 secondes même si le tampon n'a pas atteint `total_file_size` — les entrées à faible volume en ont besoin, sinon leurs enregistrements restent sur le disque local. + +:::note[Les points de terminaison personnalisés utilisent path-style par défaut] + +Fluent Bit 3.2 n'a pas d'option `s3_force_path_style` — le plugin la rejette comme propriété inconnue. Quand `endpoint` désigne un service S3 personnalisé, les requêtes adressent le bucket en path-style, ce qui correspond aux défauts de RustFS. Aucun indicateur supplémentaire n'est nécessaire. + +::: + +## 3. Exécuter Fluent Bit + +Démarrez le conteneur en passant les identifiants via les variables d'environnement AWS standard : + +```bash +docker run -d --name fluent-bit \ + -e AWS_ACCESS_KEY_ID= \ + -e AWS_SECRET_ACCESS_KEY= \ + -v "$PWD/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro" \ + fluent/fluent-bit:3.2.10 +``` + +Attendez le premier téléversement et confirmez le démarrage du worker : + +```bash +docker logs fluent-bit 2>&1 | grep "worker #0 started" +``` + +```text +[output:s3:s3.0] worker #0 started +``` + +## 4. Vérifier les objets téléversés + +Listez le bucket — la disposition de clés par défaut est `fluent-bit-logs//YYYY/MM/DD/HH/MM/` : + +```bash +aws s3 ls s3://my-bucket/ --recursive \ + --endpoint-url http://:9000 +``` + +```text +2026-10-25 01:13:28 1021326 fluent-bit-logs/rustfs.test/2026/10/25/01/13/59-objectbmjH9744 +2026-10-25 01:16:05 333735 fluent-bit-logs/rustfs.test/2026/10/25/01/15/34-objectKfCq13io +``` + +Lisez un objet en retour — chaque ligne est un enregistrement JSON : + +```bash +aws s3 cp s3://my-bucket/fluent-bit-logs/rustfs.test/2026/10/25/01/13/59-objectbmjH9744 - \ + --endpoint-url http://:9000 | head -2 +``` + +```json +{"date":"2026-10-25T01:13:59.204844Z","message":"dummy"} +{"date":"2026-10-25T01:13:59.206396Z","message":"dummy"} +``` + +![Journaux Fluent Bit livrés dans la console RustFS](./images/rustfs-fluentbit-logs.png) + +## 5. Arrêter ou réinitialiser + +Pour démonter la démo tout en conservant les objets du bucket : + +```bash +docker rm -f fluent-bit +``` + +Pour supprimer les journaux téléversés : + +```bash +aws s3 rm s3://my-bucket/fluent-bit-logs --recursive \ + --endpoint-url http://:9000 +``` + +## Dépannage + +### `PutObject request failed` avec `NoSuchBucket` + +Le bucket de sortie n'existe pas. Créez-le comme indiqué à l'étape 1 — le plugin ne crée jamais de bucket. + +### `[config] s3: unknown configuration property 's3_force_path_style'` + +Cette option n'existe pas dans Fluent Bit 3.2. Supprimez-la ; les points de terminaison personnalisés utilisent déjà l'adressage path-style. + +### Les objets n'apparaissent jamais et le journal reste silencieux + +Le tampon n'a pas atteint `total_file_size` et aucun `upload_timeout` n'est défini. Ajoutez `upload_timeout 30s` pour que les entrées à faible volume se transfèrent périodiquement. + +### `InvalidAccessKeyId` dans le journal Fluent Bit + +Les variables d'environnement d'identifiants manquent dans le conteneur ou sont erronées. La sortie S3 lit `AWS_ACCESS_KEY_ID` et `AWS_SECRET_ACCESS_KEY`. + +## Prochaines étapes + +- Consultez les [notes de compatibilité S3](/administration/protocols/s3) avant d'activer les téléversements multiparties ou la compression sur cette sortie. +- Créez des identifiants de production dédiés avec la [gestion des clés d'accès](/security-compliance/iam/access-token). +- Suivez la [documentation de la sortie S3 de Fluent Bit](https://docs.fluentbit.io/manual/pipeline/outputs/s3) pour `s3_key_format`, la compression et le réglage de l'entrée `tail` sur le même bucket. diff --git a/content/fr/developer/integration/observability/images/rustfs-fluentbit-logs.png b/content/fr/developer/integration/observability/images/rustfs-fluentbit-logs.png new file mode 100644 index 000000000..e395d48ce Binary files /dev/null and b/content/fr/developer/integration/observability/images/rustfs-fluentbit-logs.png differ diff --git a/content/fr/developer/integration/observability/meta.json b/content/fr/developer/integration/observability/meta.json index a25277229..c132d7b8e 100644 --- a/content/fr/developer/integration/observability/meta.json +++ b/content/fr/developer/integration/observability/meta.json @@ -1,6 +1,7 @@ { "title": "Observabilité", "pages": [ + "fluent-bit", "fluentd", "greptimedb", "loki", diff --git a/content/ja/developer/integration/ai/images/rustfs-sglang-models.png b/content/ja/developer/integration/ai/images/rustfs-sglang-models.png new file mode 100644 index 000000000..2ba97d48b Binary files /dev/null and b/content/ja/developer/integration/ai/images/rustfs-sglang-models.png differ diff --git a/content/ja/developer/integration/ai/meta.json b/content/ja/developer/integration/ai/meta.json index 9f8472c17..f03ec4897 100644 --- a/content/ja/developer/integration/ai/meta.json +++ b/content/ja/developer/integration/ai/meta.json @@ -3,6 +3,7 @@ "pages": [ "mlflow", "ray", + "sglang", "vllm" ] } diff --git a/content/ja/developer/integration/ai/sglang.md b/content/ja/developer/integration/ai/sglang.md new file mode 100644 index 000000000..731f866be --- /dev/null +++ b/content/ja/developer/integration/ai/sglang.md @@ -0,0 +1,144 @@ +--- +title: "SGLang" +description: "RustFS バケットからモデルの重みを直接ストリーミングして SGLang で LLM 推論を提供する。" +--- + +このガイドは、大規模言語モデルやマルチモーダルモデル向けのサービングフレームワーク [SGLang](https://github.com/sgl-project/sglang) を、モデルの重みの保存先として **RustFS** に接続します。モデルを RustFS バケットにアップロードし、SGLang に `s3://` URI を指定すると、重みをバケットから直接ストリーミングしながら OpenAI 互換リクエストに応答します。このワークフローは `lmsysorg/sglang:v0.5.22-xeon`(SGLang 0.5.22、CPU エンジン)で `Qwen/Qwen2.5-0.5B-Instruct` を CPU 専用ホスト上の RustFS バケットから提供して検証しました。 + +Docker と小さな Hugging Face モデルが必要です。このデプロイはローカルでの結合テスト用であり、本番環境向けではありません。 + +## アーキテクチャ + +```mermaid +flowchart LR + Upload["aws s3 cp"] -->|"weights"| RustFS["RustFS :9000"] + RustFS -->|"s3:// runai streamer"| SGLang["SGLang :30000"] + Client["OpenAI SDK / curl"] -->|"completions"| SGLang +``` + +`--model-path` がオブジェクトストレージ URI のとき、SGLang は自動的にロード形式を `runai_streamer` に切り替えます。メタデータファイル(`config.json`、トークナイザ)はローカルキャッシュに一度だけダウンロードされ、その後、重みテンソルはエンジンのロード中に遅延ストリーミングされます。バケットがモデルの唯一のコピーであり続けるため、サービングホストにローカルのモデルストアは不要です。 + +:::note[CPU イメージには S3 ストリーマーが含まれない] + +`xeon` CPU イメージには `runai-model-streamer-s3` が含まれず、`s3://` のモデルパスでのサーバー起動は `ImportError: S3 files module not found` で失敗します。サーバー起動前にコンテナ内へパッケージをインストールしてください。イメージの仮想環境は `uv` で管理されており、`pip` モジュールはありません。 + +::: + +## 1. モデルを RustFS へアップロードする + +Hugging Face からモデルファイルを取得してバケットへコピーし、接続プレースホルダーをすべて置き換えます: + +```bash +docker run --rm -v "$PWD/models:/models" --entrypoint python3 \ + lmsysorg/sglang:v0.5.22-xeon -c \ + 'from huggingface_hub import snapshot_download; snapshot_download("Qwen/Qwen2.5-0.5B-Instruct", local_dir="/models/qwen2.5-0.5b-instruct")' + +aws s3 cp models/qwen2.5-0.5b-instruct \ + s3://my-bucket/sglang-models/qwen2.5-0.5b-instruct --recursive \ + --endpoint-url http://:9000 \ + --acl private +``` + +以降のコマンドのために認証情報をエクスポートします: + +```bash +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_DEFAULT_REGION=us-east-1 +``` + +モデルごとに独立したプレフィックスを使えば、複数のモデルで同じバケットを共有できます。ストリーマーが読み取る重みファイルは `.safetensors` 形式のみです。 + +## 2. バケットを指して SGLang を起動する + +CPU イメージを起動し、S3 ストリーマーパッケージをインストールしてから、バケット URI でサーバーを起動します: + +```bash +docker run -d --name sglang -p 30000:30000 \ + -e AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY \ + -e AWS_DEFAULT_REGION \ + -e AWS_ENDPOINT_URL=http://:9000 \ + --entrypoint bash lmsysorg/sglang:v0.5.22-xeon -c \ + 'uv pip install -q runai-model-streamer-s3 && \ + python3 -m sglang.launch_server \ + --model-path s3://my-bucket/sglang-models/qwen2.5-0.5b-instruct \ + --served-model-name qwen2.5-0.5b-instruct \ + --host 0.0.0.0 --port 30000 \ + --device cpu --trust-remote-code \ + --disable-overlap-schedule \ + --mem-fraction-static 0.45' +``` + +`AWS_ENDPOINT_URL` はストリーマーを RustFS に向けるもので、カスタムエンドポイントへのリクエストはパススタイル addressing を使います。`--mem-fraction-static` は CPU バックエンドで KV キャッシュに割り当てるホストメモリの割合です。空きメモリが多いホストでは値を上げてください。起動完了の行を待ちます: + +```bash +docker logs -f sglang +``` + +```text +The server is fired up and ready to roll! +``` + +## 3. 推論を実行する + +モデルが提供されていることを確認し、OpenAI 互換の補完リクエストを送ります: + +```bash +curl -s http://localhost:30000/v1/models +``` + +```json +{"object":"list","data":[{"id":"qwen2.5-0.5b-instruct","object":"model","owned_by":"sglang","root":"qwen2.5-0.5b-instruct","max_model_len":32768}]} +``` + +```bash +curl -s http://localhost:30000/v1/completions \ + -H "Content-Type: application/json" \ + -d '{"model":"qwen2.5-0.5b-instruct","prompt":"RustFS is","max_tokens":16,"temperature":0}' +``` + +```json +{"object":"text_completion","model":"qwen2.5-0.5b-instruct","choices":[{"index":0,"text":" a file system for the Linux kernel that is designed to be used in embedded systems","finish_reason":"length"}],"usage":{"prompt_tokens":4,"total_tokens":20,"completion_tokens":16}} +``` + +![RustFS コンソールに保存された SGLang のモデル重み](./images/rustfs-sglang-models.png) + +## 4. 停止またはリセット + +バケットのオブジェクトを残したままデモを解体するには: + +```bash +docker rm -f sglang +``` + +保存したモデルを削除するには: + +```bash +aws s3 rm s3://my-bucket/sglang-models --recursive \ + --endpoint-url http://:9000 +``` + +## トラブルシューティング + +### `ImportError: S3 files module not found` + +RunAI ストリーマーの S3 バックエンドがありません。手順 2 のように、サーバー起動前にコンテナ内で `uv pip install runai-model-streamer-s3` を実行してください。 + +### `pip: command not found` または `No module named pip` + +イメージの `/opt/.venv` は `uv` によって `pip` なしで作成されています。`pip install` ではなく `uv pip install` を使ってください。 + +### `Loaded weights leave no GPU memory for the KV cache` + +このホストでは `--mem-fraction-static` が読み込み済みの重みに対して小さすぎます。エラーメッセージに最小値が表示されるので、より大きい割合(16 GB ホストの 0.5 B モデルなら `0.45` など)で再起動してください。 + +### リクエストが署名エラーやアクセスエラーで失敗する + +サーバーコンテナで 4 つの変数すべてが設定されているか確認してください:`AWS_ACCESS_KEY_ID`、`AWS_SECRET_ACCESS_KEY`、`AWS_DEFAULT_REGION`、そしてスキーム付きの `AWS_ENDPOINT_URL` です。 + +## 次のステップ + +- 追加のサービング操作を採用する前に [S3 互換性についての注意](/administration/protocols/s3) を確認してください。 +- [アクセスキー管理](/security-compliance/iam/access-token) で本番用の専用認証情報を作成してください。 +- [SGLang ドキュメント](https://docs.sglang.io/) に従い、同じバケットベースのモデルストアの上でテンソル並列化・量子化・より大きなモデルを利用してください。 diff --git a/content/ja/developer/integration/big-data/images/rustfs-pulsar-offload.png b/content/ja/developer/integration/big-data/images/rustfs-pulsar-offload.png new file mode 100644 index 000000000..ca6b201b3 Binary files /dev/null and b/content/ja/developer/integration/big-data/images/rustfs-pulsar-offload.png differ diff --git a/content/ja/developer/integration/big-data/meta.json b/content/ja/developer/integration/big-data/meta.json index 41793019b..2a3807a35 100644 --- a/content/ja/developer/integration/big-data/meta.json +++ b/content/ja/developer/integration/big-data/meta.json @@ -14,6 +14,7 @@ "pyiceberg", "spark", "seatunnel", - "zeppelin" + "zeppelin", + "pulsar" ] } diff --git a/content/ja/developer/integration/big-data/pulsar.md b/content/ja/developer/integration/big-data/pulsar.md new file mode 100644 index 000000000..b023516d8 --- /dev/null +++ b/content/ja/developer/integration/big-data/pulsar.md @@ -0,0 +1,170 @@ +--- +title: "Apache Pulsar" +description: "Pulsar のコールドな topic バックログを BookKeeper から RustFS バケットへ階層化ストレージでオフロードする。" +--- + +このガイドは、[Apache Pulsar](https://github.com/apache/pulsar) を階層化ストレージ(Tiered Storage)の S3 オフローダーを通じて **RustFS** に接続します。オフロード先を RustFS バケットに向けたスタンドアロンブローカーを実行し、メッセージを生成し、レジャーのオフロードをトリガーした後、オフロードされたメッセージをバケットから読み戻します。このワークフローは `apachepulsar/pulsar-all:4.0.4` で topic を RustFS にオフロードし、そこからコールドリードすることで検証しました。 + +Docker が必要です。このデプロイはローカルでの結合テスト用であり、本番環境向けではありません。 + +## アーキテクチャ + +```mermaid +flowchart LR + Producer["pulsar-client produce"] --> Broker["Pulsar standalone"] + Broker -->|"hot ledgers"| BK["BookKeeper"] + Broker -->|"offload ledgers"| RustFS["RustFS :9000"] + Consumer["pulsar-client consume"] -->|"cold reads"| Broker +``` + +古いレジャーは `-ledger-` のデータブロックと小さな `-index` オブジェクトとしてバケットへアップロードされます。コンシューマーが BookKeeper に存在しないメッセージを要求すると、ブローカーは透過的にバケットから読み戻します。 + +## 1. オフロード設定付きでスタンドアロンブローカーを起動する + +プレーンな `apachepulsar/pulsar` イメージにはオフローダーのパッケージが含まれず、`No offloader found for driver 'S3'` で失敗します。それらを含む `pulsar-all` イメージを使用してください。ブローカーのヒープは小さく制限しています: + +```bash +docker run -d --name pulsar -p 6650:6650 -p 8080:8080 \ + -e ACCESS_KEY_ID= \ + -e ACCESS_KEY_SECRET= \ + -e PULSAR_MEM="-Xms512m -Xmx512m" \ + --entrypoint bash apachepulsar/pulsar-all:4.0.4 -c ' + sed -i -e "s|^managedLedgerMaxEntriesPerLedger=.*|managedLedgerMaxEntriesPerLedger=100|" \ + -e "s|^managedLedgerMinLedgerRolloverTimeMinutes=.*|managedLedgerMinLedgerRolloverTimeMinutes=1|" \ + /pulsar/conf/standalone.conf + printf "\nmanagedLedgerOffloadDriver=S3\nmanagedLedgerOffloadBucket=my-bucket\nmanagedLedgerOffloadServiceEndpoint=http://:9000\nmanagedLedgerOffloadRegion=us-east-1\n" >> /pulsar/conf/standalone.conf + exec bin/pulsar standalone -nss + ' +``` + +2 つの `managedLedger*` の編集はレジャーを小さくしてデモ内でロールが発生するようにするもので、追記したブロックがオフロードを有効化します。 + +:::note[ドライバー S3 が読むのは managedLedgerOffload* キー(s3ManagedLedgerOffload* ではない)] + +ドライバー `S3` の場合、オフローダーは汎用キーの `managedLedgerOffloadBucket`・`managedLedgerOffloadServiceEndpoint`・`managedLedgerOffloadRegion` を解決します。`s3ManagedLedgerOffload*` という名前は `aws-s3` ドライバーのもので、ここでは無視されます。これらを使うとオフローダーは `NullPointerException: endpoint` でクラッシュします。認証情報も同様です。ドライバー `S3` は環境変数 `ACCESS_KEY_ID` と `ACCESS_KEY_SECRET` を読み、`AWS_*` のペアは読みません。 + +::: + +ブローカーの起動を待ちます: + +```bash +docker logs -f pulsar 2>&1 | grep -m1 "The ledger offloader was created" +``` + +## 2. メッセージを生成し、topic を存続させる + +subscription のない topic は非アクティブ topic のクリーンアップによって数分で削除され、デモが途中で消えます。最初のバッチを生成した後、topic を登録し続ける永続的な subscription を作成します: + +```bash +docker exec pulsar bin/pulsar-client produce -m "hello rustfs offload" -n 300 \ + persistent://public/default/rustfs-test +``` + +```text +300 messages successfully produced +``` + +```bash +timeout 30 docker exec pulsar bin/pulsar-client consume -s keepalive -n 1 -p Earliest \ + persistent://public/default/rustfs-test +``` + +## 3. レジャーをロールし、オフロードをトリガーする + +レジャーは `managedLedgerMinLedgerRolloverTimeMinutes` の経過後に書き込みが発生してはじめてロールします。チェックは書き込み時に行われ、バックグラウンドでは行われません。1 分以上待ち、別のバッチを生成してロールを強制してから、最後の 1 KB より前のすべてをオフロードします: + +```bash +sleep 65 +docker exec pulsar bin/pulsar-client produce -m "rollover" -n 120 \ + persistent://public/default/rustfs-test +docker exec pulsar bin/pulsar-admin topics offload -s 1K \ + persistent://public/default/rustfs-test +``` + +```text +Offload triggered for persistent://public/default/rustfs-test for messages before 11:0:-1 +``` + +オフロードには時間がかかります。完了を待ちます: + +```bash +sleep 30 +docker exec pulsar bin/pulsar-admin topics offload-status \ + persistent://public/default/rustfs-test +``` + +```text +Offload was a success +``` + +レジャーのオブジェクトが RustFS に着いたことを確認します。データブロックとインデックスがバケットのルートに置かれます: + +```bash +aws s3 ls s3://my-bucket/ --recursive \ + --endpoint-url http://:9000 | grep ledger +``` + +```text +2026-10-25 02:34:49 21057 7f9379cc-c21d-4829-a857-4c39ec02e391-ledger-9 +2026-10-25 02:34:49 214 7f9379cc-c21d-4829-a857-4c39ec02e391-ledger-9-index +``` + +## 4. オフロード済みメッセージを読み戻す + +新しい subscription で最古の位置から消費します。最初のレジャーはもう RustFS にしか存在しないため、ブローカーが返すすべてのメッセージがバケットからのコールドリードです: + +```bash +timeout 90 docker exec pulsar bin/pulsar-client consume -s coldread -n 420 -p Earliest \ + persistent://public/default/rustfs-test 2>&1 | grep -c "^-----" +``` + +```text +420 +``` + +420 件すべてのメッセージ(オフロード済みレジャーの 300 件とアクティブなレジャーの 120 件)がバケット経由で返ってきます。 + +![RustFS コンソール上の Pulsar オフロード済みレジャーのオブジェクト](./images/rustfs-pulsar-offload.png) + +## 5. 停止またはリセット + +バケットのオブジェクトを残したままブローカーを解体するには: + +```bash +docker rm -f pulsar +``` + +オフロード済みのレジャーを削除するには: + +```bash +aws s3 rm s3://my-bucket/ --recursive --exclude "*" --include "*ledger*" \ + --endpoint-url http://:9000 +``` + +## トラブルシューティング + +### `No offloader found for driver 'S3'. Please make sure you dropped the offloader nar packages` + +イメージにオフローダーの NAR が含まれていません。手順 1 のとおり `apachepulsar/pulsar-all` を使用してください。 + +### オフロード時に `NullPointerException: endpoint` + +オフローダーがサービスエンドポイントを読み取れていません。`managedLedgerOffload*` キーが欠けているか、`aws-s3` ドライバーの `s3ManagedLedgerOffload*` 名で書かれています。手順 1 のブロックを正確に追記して再起動してください。 + +### ブローカーログに `Couldn't get the access key id.` + +ドライバー `S3` は `AWS_ACCESS_KEY_ID` を読みません。ブローカーコンテナで `ACCESS_KEY_ID` と `ACCESS_KEY_SECRET` をエクスポートしてください。 + +### デモ中に `Topic ... not found` + +非アクティブ topic のクリーンアップが、subscription のない topic を削除しました。手順 2 をやり直し、`keepalive` subscription を維持してください。 + +### `Offload was a success` なのにオブジェクトが現れない + +アップロードされるのはサイズしきい値より前の閉じたレジャーだけで、レジャーは最短ロール年齢を過ぎた後の次の書き込みでロールします。さらにバッチを生成してから、オフロードを再度トリガーしてください。 + +## 次のステップ + +- namespace で自動オフロードしきい値を有効にする前に [S3 互換性についての注意](/administration/protocols/s3) を確認してください。 +- [アクセスキー管理](/security-compliance/iam/access-token) で本番用の専用認証情報を作成してください。 +- [Pulsar 階層化ストレージのドキュメント](https://pulsar.apache.org/docs/next/cookbooks-tiered-storage/) に従い、同じバケットで namespace のしきい値・保持ポリシー・マルチノードブローカーを利用してください。 diff --git a/content/ja/developer/integration/cloud-native/camel.md b/content/ja/developer/integration/cloud-native/camel.md new file mode 100644 index 000000000..6d391c636 --- /dev/null +++ b/content/ja/developer/integration/cloud-native/camel.md @@ -0,0 +1,127 @@ +--- +title: "Apache Camel" +description: "Apache Camel の統合ルートで RustFS バケットとの間のデータをルーティングする。" +--- + +このガイドは、多用途の統合フレームワーク [Apache Camel](https://github.com/apache/camel) を S3 コンポーネント経由で **RustFS** に接続します。YAML で 2 つのルートからなる統合を記述します。タイマーでメッセージをバケットに書き込むプロデューカールートと、それらのオブジェクトを読み戻してログに出力するコンシューマールートです。このワークフローは `eclipse-temurin:21-jdk` 上の Camel JBang で、エンドポイントを上書きした `aws2-s3` コンポーネントとパススタイル addressing を使って検証しました。 + +Docker が必要です。このデプロイはローカルでの結合テスト用であり、本番環境向けではありません。 + +## アーキテクチャ + +```mermaid +flowchart LR + Timer["timer:tick"] -->|"CamelAwsS3Key header"| Producer["aws2-s3 producer"] + Producer -->|"PutObject"| RustFS["RustFS :9000"] + RustFS -->|"prefix=camel/ poll"| Consumer["aws2-s3 consumer"] + Consumer --> Log["log:from-rustfs"] +``` + +Camel JBang はルートファイルを直接実行し、S3 コンポーネントの依存関係を自動解決します。プロジェクトの雛形作成もビルドもデプロイも不要です。 + +## 1. 統合ルートを書く + +ルートファイルを作成します。認証情報はデフォルトの AWS 認証情報チェーン(環境変数)から取得するため、URI は addressing 情報のみを持ちます: + +```yaml title="route.yaml" +- from: + uri: "timer:tick" + parameters: + period: "5000" + steps: + - setBody: + simple: "Hello from Camel at ${date:now:yyyy-MM-dd HH:mm:ss}" + - setHeader: + name: "CamelAwsS3Key" + simple: "camel/hello-${date:now:HHmmss}.txt" + - to: + uri: "aws2-s3://my-bucket?overrideEndpoint=true&uriEndpointOverride=http://:9000&forcePathStyle=true&useDefaultCredentialsProvider=true" +- from: + uri: "aws2-s3://my-bucket?prefix=camel/&deleteAfterRead=false&maxMessagesPerPoll=5&overrideEndpoint=true&uriEndpointOverride=http://:9000&forcePathStyle=true&useDefaultCredentialsProvider=true" + steps: + - to: + uri: "log:from-rustfs" +``` + +プロデューサーは `CamelAwsS3Key` ヘッダーでオブジェクトキーを設定します。ここではメッセージごとのタイムスタンプを使うため、ティックごとに新しいオブジェクトが書き込まれます。コンシューマーは `deleteAfterRead=false` で `camel/` プレフィックスをポーリングし、オブジェクトは消費後もバケットに残ります。 + +:::note[コンポーネント名とエンドポイントオプション] + +このワークフローで Camel JBang が解決するコンポーネントは `aws2-s3` です。`overrideEndpoint=true` がカスタムエンドポイントを有効化し、`uriEndpointOverride` がその URL を担い、`forcePathStyle=true` が RustFS が期待するパススタイル addressing を選択します。`useDefaultCredentialsProvider=true` により、コンポーネントは標準の環境変数から認証情報を読み取ります。 + +::: + +## 2. Camel JBang でルートを実行する + +認証情報をエクスポートしたコンテナで統合を実行します。JBang は初回使用時に信頼を求めるため、起動前に信頼ルールを追加します: + +```bash +docker run --rm --network bridge \ + -e AWS_ACCESS_KEY_ID= \ + -e AWS_SECRET_ACCESS_KEY= \ + -e AWS_REGION=us-east-1 \ + -v "$PWD:/routes" \ + --entrypoint bash eclipse-temurin:21-jdk -c ' + export JBANG_DIR=/root/.jbang PATH=/root/.jbang/bin:$PATH + curl -Ls https://sh.jbang.dev | bash >/dev/null 2>&1 + jbang trust add https://github.com/apache/ 2>/dev/null || true + jbang trust add /routes 2>/dev/null || true + cd /routes && jbang camel@apache/camel run route.yaml + ' +``` + +最初の実行で Camel ランタイムと依存関係がダウンロードされ、ルートが起動します。コンシューマーは読み取ったオブジェクトごとにログを出力します: + +```text +2026-10-25 02:02:41.069 INFO 120 --- [-s3://my-bucket] from-rustfs : Exchange[ExchangePattern: InOnly, BodyType: byte[], Body: Hello from Camel at 2026-10-25 02:02:38] +``` + +## 3. オブジェクトを確認する + +プロデューサーのオブジェクトが RustFS に存在することを確認します。接続プレースホルダーをすべて置き換えてください: + +```bash +aws s3 ls s3://my-bucket/camel/ \ + --endpoint-url http://:9000 +``` + +```text +2026-10-25 02:02:13 39 hello-020213.txt +2026-10-25 02:02:18 39 hello-020218.txt +2026-10-25 02:02:23 39 hello-020223.txt +``` + +![RustFS コンソール上の Apache Camel ルートのオブジェクト](./images/rustfs-camel-route.png) + +## 4. 停止またはリセット + +コンテナを停止するとルートが終了します。デモのオブジェクトを削除するには: + +```bash +aws s3 rm s3://my-bucket/camel --recursive \ + --endpoint-url http://:9000 +``` + +## トラブルシューティング + +### `Cannot find component with name: aws-s3` + +お使いの Camel バージョンは別名でコンポーネントを解決します。このワークフローは `aws2-s3` を使います。ランタイムが提案リスト(`Did you mean: ... aws2-s3 ...`)で示す名前を使ってください。 + +### `is not from a trusted source` + +JBang は信頼できないコードの実行を拒否します。手順 2 のとおり、`jbang camel@apache/camel run` の前に `jbang trust add https://github.com/apache/` と `jbang trust add /routes` を実行してください。 + +### `useDefaultCredentialsProvider is set to false` + +コンポーネントが URI 内に認証情報の設定を見つけられませんでした。`useDefaultCredentialsProvider=true` を追加して `AWS_ACCESS_KEY_ID` と `AWS_SECRET_ACCESS_KEY` をエクスポートするか、`accessKey`・`secretKey` エンドポイントオプションを直接設定してください。 + +### コンシューマーが何も読み取らない + +コンシューマーは `prefix=camel/` で指定されたプレフィックスのみをポーリングします。プロデューサーが動いたこと(`timer` のログ行)と、その `CamelAwsS3Key` ヘッダーが `camel/` で始まることを確認してください。 + +## 次のステップ + +- マルチパートやストリーミング操作を採用する前に [S3 互換性についての注意](/administration/protocols/s3) を確認してください。 +- [アクセスキー管理](/security-compliance/iam/access-token) で本番用の専用認証情報を作成してください。 +- [Camel S3 コンポーネントのドキュメント](https://camel.apache.org/components/latest/aws2-s3-component.html) に従い、同じルートでダウンロード範囲・バケット操作・コンシューマーのチューニングを利用してください。 diff --git a/content/ja/developer/integration/cloud-native/dapr.md b/content/ja/developer/integration/cloud-native/dapr.md new file mode 100644 index 000000000..1a5a8d951 --- /dev/null +++ b/content/ja/developer/integration/cloud-native/dapr.md @@ -0,0 +1,144 @@ +--- +title: "Dapr" +description: "任意の Dapr アプリケーションから AWS S3 バインディング経由で RustFS オブジェクトを読み書きする。" +--- + +このガイドは、分散アプリケーション向けの CNCF ポータブルランタイム [Dapr](https://github.com/dapr/dapr) を、AWS S3 バインディングを通じて **RustFS** に接続します。RustFS バケットをバックエンドに持つバインディングコンポーネントを宣言し、サイドカーを実行して、バインディングの HTTP API でオブジェクトを作成・読み戻し・独立に確認します。このワークフローは `daprio/dapr:1.19.0` をパススタイル addressing で RustFS バケットに対して実行して検証しました。 + +Docker が必要です。このデプロイはローカルでの結合テスト用であり、本番環境向けではありません。 + +## アーキテクチャ + +```mermaid +flowchart LR + App["Your app"] -->|"POST /v1.0/bindings/rustfs-s3"| Daprd["daprd sidecar :3500"] + Daprd -->|"bindings.aws.s3"| RustFS["RustFS :9000"] +``` + +アプリケーションは S3 の認証情報やエンドポイントを一切持ちません。サイドカーがバケットを名前付きバインディングとして公開し、すべての操作は `operation` と任意の `metadata` を添えてバインディングエンドポイントへ POST するだけです。 + +## 1. バインディングコンポーネントを定義する + +コンポーネントファイルを作成します。`endpoint` にはスキーマを含め、`forcePathStyle` は RustFS のパススタイル addressing に合わせます: + +```yaml title="rustfs-s3.yaml" +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: rustfs-s3 +spec: + type: bindings.aws.s3 + version: v1 + metadata: + - name: bucket + value: my-bucket + - name: endpoint + value: http://:9000 + - name: accessKey + value: + - name: secretKey + value: + - name: region + value: us-east-1 + - name: forcePathStyle + value: "true" +``` + +## 2. サイドカーを起動する + +コンポーネントディレクトリをマウントして `daprd` を実行します。`--resources-path` は非推奨の `--components-path` を置き換えるものです: + +```bash +docker run -d --name dapr \ + -v "$PWD:/components" \ + daprio/dapr:1.19.0 /daprd \ + --app-id s3demo \ + --dapr-http-port 3500 \ + --resources-path /components +``` + +バインディングの初期化を確認します。ログは次の行で終わります: + +```bash +docker logs dapr 2>&1 | grep "successful init" +``` + +```text +successful init for output binding (rustfs-s3 (bindings.aws.s3/v1)) +``` + +## 3. バインディング経由でオブジェクトを作成する + +サイドカーに到達できる任意の HTTP クライアントからバインディングを呼び出します。`key` メタデータがオブジェクト名を決めます: + +```bash +curl -s -X POST http://localhost:3500/v1.0/bindings/rustfs-s3 \ + -H "Content-Type: application/json" \ + -d '{"operation":"create","data":"hello from dapr","metadata":{"key":"dapr/hello.txt"}}' +``` + +```json +{"location":"http://:9000/my-bucket/dapr/hello.txt","versionID":null} +``` + +## 4. オブジェクトを読み戻す + +`get` 操作はオブジェクトの内容をレスポンスボディで返します: + +```bash +curl -s -X POST http://localhost:3500/v1.0/bindings/rustfs-s3 \ + -H "Content-Type: application/json" \ + -d '{"operation":"get","metadata":{"key":"dapr/hello.txt"}}' +``` + +```text +hello from dapr +``` + +独立したクライアントから同じオブジェクトを確認します: + +```bash +aws s3 cp s3://my-bucket/dapr/hello.txt - \ + --endpoint-url http://:9000 +``` + +```text +hello from dapr +``` + +![RustFS コンソール上の Dapr バインディングのオブジェクト](./images/rustfs-dapr-binding.png) + +## 5. 停止またはリセット + +バケットのオブジェクトを残したままサイドカーを解体するには: + +```bash +docker rm -f dapr +``` + +デモのオブジェクトを削除するには: + +```bash +aws s3 rm s3://my-bucket/dapr/hello.txt \ + --endpoint-url http://:9000 +``` + +## トラブルシューティング + +### バインディングの初期化が S3 エラーで失敗する + +4 つの addressing 値をまとめて確認してください。`endpoint` はスキーマを含むこと、`region` は AWS 以外のエンドポイントでも必須、`forcePathStyle` は `"true"` であることが必要です。`forcePathStyle` がないと、RustFS が拒否するバーチャルホスト形式のリクエストが送られます。 + +### 呼び出し時に `no such binding` + +サイドカーがコンポーネントをロードしていません。ファイルが `.yaml` で終わり、`--resources-path` に渡したディレクトリに置かれていること、手順 2 の初期化行がログに出ていることを確認してください。 + +### `get` が空の内容を返す + +`metadata.key` が作成時に使ったキーと一致していません。キーは大文字小文字を区別し、プレフィックスを含みます。 + +## 次のステップ + +- バインディングの `list`・`delete`・`presign` 操作を採用する前に [S3 互換性についての注意](/administration/protocols/s3) を確認してください。 +- [アクセスキー管理](/security-compliance/iam/access-token) で本番用の専用認証情報を作成し、コンポーネントへの平文ではなく Dapr のシークレットストアから参照してください。 +- [Dapr S3 バインディングのドキュメント](https://docs.dapr.io/reference/components-reference/supported-bindings/s3/) に従い、全操作セットとリトライ動作を利用してください。 diff --git a/content/ja/developer/integration/cloud-native/images/rustfs-camel-route.png b/content/ja/developer/integration/cloud-native/images/rustfs-camel-route.png new file mode 100644 index 000000000..3f73da421 Binary files /dev/null and b/content/ja/developer/integration/cloud-native/images/rustfs-camel-route.png differ diff --git a/content/ja/developer/integration/cloud-native/images/rustfs-dapr-binding.png b/content/ja/developer/integration/cloud-native/images/rustfs-dapr-binding.png new file mode 100644 index 000000000..d56de00e0 Binary files /dev/null and b/content/ja/developer/integration/cloud-native/images/rustfs-dapr-binding.png differ diff --git a/content/ja/developer/integration/cloud-native/meta.json b/content/ja/developer/integration/cloud-native/meta.json index 6c0c94066..2b69a2409 100644 --- a/content/ja/developer/integration/cloud-native/meta.json +++ b/content/ja/developer/integration/cloud-native/meta.json @@ -3,6 +3,8 @@ "pages": [ "cortex", "flux", - "celld" + "celld", + "camel", + "dapr" ] } diff --git a/content/ja/developer/integration/devops/helm.md b/content/ja/developer/integration/devops/helm.md new file mode 100644 index 000000000..fbd7981c0 --- /dev/null +++ b/content/ja/developer/integration/devops/helm.md @@ -0,0 +1,137 @@ +--- +title: "Helm" +description: "helm-s3 プラグインでプライベートな Helm チャートリポジトリを RustFS バケットにホストする。" +--- + +このガイドは、Kubernetes 向け CNCF パッケージマネージャー [Helm](https://github.com/helm/helm) を、[helm-s3](https://github.com/hypnoglow/helm-s3) プラグイン経由で **RustFS** に接続し、RustFS バケットをプライベートなチャートリポジトリにします。バケット内でリポジトリを初期化し、パッケージ化したチャートをプッシュした後、そのリポジトリを Helm に追加してチャートを引き戻します。このワークフローは `alpine/helm:3.16.4` と helm-s3 `v0.17.2` を RustFS バケットに対して実行して検証しました。 + +Docker が必要です。このデプロイはローカルでの結合テスト用であり、本番環境向けではありません。 + +## アーキテクチャ + +```mermaid +flowchart LR + Dev["helm package"] -->|"helm s3 push"| Repo["s3://my-bucket/charts"] + Repo -->|"index.yaml"| Helm["helm repo add"] + Helm -->|"helm pull / install"| K8s["cluster"] +``` + +バケットのプレフィックスには `index.yaml` と、チャートのバージョンごとに 1 つの `.tgz` が格納されます。Helm はプラグイン経由で読み書きするため、RustFS に到達できる場所ならどこからでもこのリポジトリを利用できます。 + +## 1. プラグインをインストールしてリポジトリを初期化する + +`alpine/helm` イメージには `git` が含まれないため、リポジトリ URL からではなくリリースアーカイブからプラグインをインストールします: + +```bash +cd /tmp +wget -q https://github.com/hypnoglow/helm-s3/releases/download/v0.17.2/helm-s3_0.17.2_linux_amd64.tar.gz +mkdir -p helm-s3 && tar -xzf helm-s3_0.17.2_linux_amd64.tar.gz -C helm-s3 +helm plugin install helm-s3 +``` + +プラグイン独自の環境変数で RustFS を指し、リポジトリのプレフィックスを初期化します: + +```bash +export AWS_ENDPOINT=:9000 +export AWS_DISABLE_SSL=true +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_DEFAULT_REGION=us-east-1 + +helm s3 init s3://my-bucket/charts +``` + +```text +Initialized empty repository at s3://my-bucket/charts +``` + +:::note[helm-s3 は AWS_ENDPOINT を使う(AWS_ENDPOINT_URL ではない)] + +プラグインは `AWS_ENDPOINT`(ホストとポート、スキームなし)と平文 HTTP のための `AWS_DISABLE_SSL=true` からリクエスト URL を組み立てます。標準の `AWS_ENDPOINT_URL` 変数は認識されず、それによって引き起こされる SDK のバーチャルホスト形式 addressing は RustFS に対して `403 Forbidden` で失敗します。 + +::: + +## 2. チャートをプッシュする + +最初のプッシュの前にリポジトリを登録します。プラグインは Helm のリポジトリファイルを読み、存在しないと失敗します: + +```bash +helm repo add rustfs-charts s3://my-bucket/charts +``` + +デモ用チャートを作成してパッケージ化し、プッシュします。`helm s3 push` が取るのは **リポジトリ名** であって `s3://` URI ではありません: + +```bash +helm create demo +helm package demo +helm s3 push ./demo-0.1.0.tgz rustfs-charts +``` + +```text +Successfully uploaded the chart to the repository. +``` + +## 3. バケットからインストールまたは取得する + +リポジトリのインデックスを更新し、チャートが見つかることを確認します: + +```bash +helm repo update +helm search repo rustfs-charts/demo +``` + +```text +NAME CHART VERSION APP VERSION DESCRIPTION +rustfs-charts/demo 0.1.0 1.16.0 A Helm chart for Kubernetes +``` + +RustFS からチャートを引き戻します: + +```bash +helm pull rustfs-charts/demo --destination ./pullback +tar -tzf ./pullback/demo-0.1.0.tgz | head -2 +``` + +```text +demo/Chart.yaml +demo/values.yaml +``` + +![RustFS コンソール上の Helm チャートリポジトリのオブジェクト](./images/rustfs-helm-charts.png) + +## 4. 停止またはリセット + +チャートは普通のバケットオブジェクトなので、クリーンアップはプレフィックスの削除です: + +```bash +aws s3 rm s3://my-bucket/charts --recursive \ + --endpoint-url http://:9000 +``` + +## トラブルシューティング + +### `helm s3 init` で `head s3 object: Forbidden` + +プラグインがバーチャルホスト形式 addressing を使っています。`AWS_ENDPOINT` と `AWS_DISABLE_SSL` が未設定です。手順 1 のとおり正確にエクスポートしてください。 + +### `check if index exists in the storage` が git エラーで失敗する + +イメージに `git` がないため、`helm plugin install ` はクローンできません。手順 1 のようにリリースアーカイブからインストールしてください。 + +### `The index file already exists under the provided URI` + +リポジトリのプレフィックスはすでに初期化されています。`helm s3 init --ignore-if-exists s3://my-bucket/charts` を再実行するか、`--force` でインデックスを置き換えてください。 + +### プッシュ時に `repo with name s3://... not found` + +`helm s3 push` は `helm repo add` で登録したリポジトリ名を待ちます。`helm s3 push ./demo-0.1.0.tgz rustfs-charts` を使ってください。 + +### プッシュ時に `couldn't load repositories file` + +この環境にはまだ Helm の `repositories.yaml` がありません。プッシュ前に `helm repo add rustfs-charts s3://my-bucket/charts` を実行してください。 + +## 次のステップ + +- 多くのチャートバージョンを含む大きなリポジトリをホストする前に [S3 互換性についての注意](/administration/protocols/s3) を確認してください。 +- [アクセスキー管理](/security-compliance/iam/access-token) で本番用の専用認証情報を作成してください。 +- [helm-s3 ドキュメント](https://github.com/hypnoglow/helm-s3) に従い、同じバケットでリポジトリの再インデックス・チャート削除・CI 利用を活用してください。 diff --git a/content/ja/developer/integration/devops/images/rustfs-helm-charts.png b/content/ja/developer/integration/devops/images/rustfs-helm-charts.png new file mode 100644 index 000000000..c63042b28 Binary files /dev/null and b/content/ja/developer/integration/devops/images/rustfs-helm-charts.png differ diff --git a/content/ja/developer/integration/devops/meta.json b/content/ja/developer/integration/devops/meta.json index 55eada6ae..1f41e2a72 100644 --- a/content/ja/developer/integration/devops/meta.json +++ b/content/ja/developer/integration/devops/meta.json @@ -5,6 +5,7 @@ "opensearch", "gitea", "jenkins", - "terraform" + "terraform", + "helm" ] } diff --git a/content/ja/developer/integration/observability/fluent-bit.md b/content/ja/developer/integration/observability/fluent-bit.md new file mode 100644 index 000000000..f1651d155 --- /dev/null +++ b/content/ja/developer/integration/observability/fluent-bit.md @@ -0,0 +1,154 @@ +--- +title: "Fluent Bit" +description: "S3 出力プラグインを使って Fluent Bit のログとイベントを RustFS バケットへ送り込む。" +--- + +このガイドは、CNCF のログプロセッサー/フォワーダーである [Fluent Bit](https://github.com/fluent/fluent-bit) を、ログの送信先として **RustFS** に接続します。`s3` 出力を持つ Fluent Bit を実行し、レコードをディスクにバッファして RustFS バケットへアップロードし、配信されたログファイルを読み戻します。このワークフローは `fluent/fluent-bit:3.2.10` でパススタイル addressing により RustFS バケットへの書き込みを検証しました。 + +Docker が必要です。このデプロイはローカルでの結合テスト用であり、本番環境向けではありません。 + +## アーキテクチャ + +```mermaid +flowchart LR + Input["Inputs: tail, dummy, http"] -->|"tagged records"| FB["Fluent Bit"] + FB -->|"buffer (store_dir)"| Disk["Local buffer"] + Disk -->|"PutObject"| RustFS["RustFS :9000"] + Reader["aws cli / Console"] -->|"JSON lines"| RustFS +``` + +S3 出力はタグ付きレコードを `store_dir` にバッファし、`total_file_size` に達するか `upload_timeout` が切れた時点でファイルをアップロードします。アップロードされた各オブジェクトは JSON Lines 形式のファイルです。 + +## 1. バケットを作成する + +S3 出力はバケットを決して作成せず、バケットがないとアップロードは `NoSuchBucket` で失敗します。まず接続プレースホルダーをすべて置き換えてバケットを作成します: + +```bash +aws s3 mb s3://my-bucket \ + --endpoint-url http://:9000 +``` + +## 2. S3 出力を設定する + +Fluent Bit の設定ファイルを作成します: + +```text title="fluent-bit.conf" +[SERVICE] + Flush 2 + Log_Level info + +[INPUT] + Name dummy + Tag rustfs.test + Rate 200 + +[OUTPUT] + Name s3 + Match * + endpoint http://:9000 + region us-east-1 + bucket my-bucket + total_file_size 1M + use_put_object On + upload_timeout 30s + store_dir /tmp/flb-store + json_date_key date + json_date_format iso8601 +``` + +`use_put_object On` はバッファ済みファイルを 1 回の `PutObject` 呼び出しでアップロードします。`upload_timeout` は、バッファが `total_file_size` に達していなくても 30 秒後にアップロードを強制します。低流量の入力では必須で、指定しないとレコードがローカルディスクに留まり続けます。 + +:::note[カスタムエンドポイントはデフォルトでパススタイル] + +Fluent Bit 3.2 には `s3_force_path_style` オプションがなく、プラグインはそれを未知のプロパティとして拒否します。`endpoint` がカスタム S3 サービスを指す場合、リクエストはパススタイルでバケットにアクセスし、RustFS のデフォルトと一致します。追加のフラグは不要です。 + +::: + +## 3. Fluent Bit を実行する + +標準の AWS 環境変数で認証情報を渡してコンテナを起動します: + +```bash +docker run -d --name fluent-bit \ + -e AWS_ACCESS_KEY_ID= \ + -e AWS_SECRET_ACCESS_KEY= \ + -v "$PWD/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro" \ + fluent/fluent-bit:3.2.10 +``` + +最初のアップロードを待ち、ワーカーが正常に起動したことを確認します: + +```bash +docker logs fluent-bit 2>&1 | grep "worker #0 started" +``` + +```text +[output:s3:s3.0] worker #0 started +``` + +## 4. アップロードされたオブジェクトを確認する + +バケットを一覧します。デフォルトのキー配置は `fluent-bit-logs//YYYY/MM/DD/HH/MM/` です: + +```bash +aws s3 ls s3://my-bucket/ --recursive \ + --endpoint-url http://:9000 +``` + +```text +2026-10-25 01:13:28 1021326 fluent-bit-logs/rustfs.test/2026/10/25/01/13/59-objectbmjH9744 +2026-10-25 01:16:05 333735 fluent-bit-logs/rustfs.test/2026/10/25/01/15/34-objectKfCq13io +``` + +オブジェクトを 1 つ読み戻します。各行が 1 つの JSON レコードです: + +```bash +aws s3 cp s3://my-bucket/fluent-bit-logs/rustfs.test/2026/10/25/01/13/59-objectbmjH9744 - \ + --endpoint-url http://:9000 | head -2 +``` + +```json +{"date":"2026-10-25T01:13:59.204844Z","message":"dummy"} +{"date":"2026-10-25T01:13:59.206396Z","message":"dummy"} +``` + +![RustFS コンソールに配信された Fluent Bit のログ](./images/rustfs-fluentbit-logs.png) + +## 5. 停止またはリセット + +バケットのオブジェクトを残したままデモを解体するには: + +```bash +docker rm -f fluent-bit +``` + +アップロードされたログを削除するには: + +```bash +aws s3 rm s3://my-bucket/fluent-bit-logs --recursive \ + --endpoint-url http://:9000 +``` + +## トラブルシューティング + +### `PutObject request failed` と `NoSuchBucket` + +出力バケットが存在しません。手順 1 のように作成してください。プラグインはバケットを作成しません。 + +### `[config] s3: unknown configuration property 's3_force_path_style'` + +Fluent Bit 3.2 にはこのオプションがありません。削除してください。カスタムエンドポイントは既にパススタイル addressing を使います。 + +### オブジェクトが現れずログも静かなまま + +バッファが `total_file_size` に達しておらず、`upload_timeout` も設定されていません。`upload_timeout 30s` を追加すると低流量の入力も定期的に送出されます。 + +### Fluent Bit のログに `InvalidAccessKeyId` + +コンテナの認証情報環境変数が欠けているか誤っています。S3 出力は `AWS_ACCESS_KEY_ID` と `AWS_SECRET_ACCESS_KEY` を読み取ります。 + +## 次のステップ + +- この出力でマルチパートアップロードや圧縮を有効にする前に [S3 互換性についての注意](/administration/protocols/s3) を確認してください。 +- [アクセスキー管理](/security-compliance/iam/access-token) で本番用の専用認証情報を作成してください。 +- [Fluent Bit S3 出力ドキュメント](https://docs.fluentbit.io/manual/pipeline/outputs/s3) に従い、同じバケットで `s3_key_format`・圧縮・`tail` 入力のチューニングを利用してください。 diff --git a/content/ja/developer/integration/observability/images/rustfs-fluentbit-logs.png b/content/ja/developer/integration/observability/images/rustfs-fluentbit-logs.png new file mode 100644 index 000000000..e395d48ce Binary files /dev/null and b/content/ja/developer/integration/observability/images/rustfs-fluentbit-logs.png differ diff --git a/content/ja/developer/integration/observability/meta.json b/content/ja/developer/integration/observability/meta.json index c878b558d..cbddc16b7 100644 --- a/content/ja/developer/integration/observability/meta.json +++ b/content/ja/developer/integration/observability/meta.json @@ -1,6 +1,7 @@ { "title": "オブザーバビリティ", "pages": [ + "fluent-bit", "fluentd", "greptimedb", "loki", diff --git a/content/zh/developer/integration/ai/images/rustfs-sglang-models.png b/content/zh/developer/integration/ai/images/rustfs-sglang-models.png new file mode 100644 index 000000000..408f8a97a Binary files /dev/null and b/content/zh/developer/integration/ai/images/rustfs-sglang-models.png differ diff --git a/content/zh/developer/integration/ai/meta.json b/content/zh/developer/integration/ai/meta.json index 9f8472c17..f03ec4897 100644 --- a/content/zh/developer/integration/ai/meta.json +++ b/content/zh/developer/integration/ai/meta.json @@ -3,6 +3,7 @@ "pages": [ "mlflow", "ray", + "sglang", "vllm" ] } diff --git a/content/zh/developer/integration/ai/sglang.md b/content/zh/developer/integration/ai/sglang.md new file mode 100644 index 000000000..6e905301e --- /dev/null +++ b/content/zh/developer/integration/ai/sglang.md @@ -0,0 +1,144 @@ +--- +title: "SGLang" +description: "用 SGLang 直接从 RustFS 桶流式加载模型权重并提供 LLM 推理服务。" +--- + +本指南将高性能大模型服务框架 [SGLang](https://github.com/sgl-project/sglang) 连接到 **RustFS** 作为其模型权重存储。你将把模型上传到 RustFS 桶,让 SGLang 直接读取 `s3://` 路径,权重从桶中流式加载并提供 OpenAI 兼容请求。整个流程使用 `lmsysorg/sglang:v0.5.22-xeon`(SGLang 0.5.22,CPU 引擎)在纯 CPU 主机上对 `Qwen/Qwen2.5-0.5B-Instruct` 验证通过。 + +你需要 Docker 以及一个小型 Hugging Face 模型。本部署用于本地集成测试,不适用于生产环境。 + +## 架构 + +```mermaid +flowchart LR + Upload["aws s3 cp"] -->|"weights"| RustFS["RustFS :9000"] + RustFS -->|"s3:// runai streamer"| SGLang["SGLang :30000"] + Client["OpenAI SDK / curl"] -->|"completions"| SGLang +``` + +当 `--model-path` 是对象存储 URI 时,SGLang 自动切换到 `runai_streamer` 加载格式:元数据文件(`config.json`、tokenizer)先下载到本地缓存,随后权重张量在引擎加载时惰性流入。桶内始终是模型的唯一副本,服务节点无需本地模型库。 + +:::note[CPU 镜像不带 S3 流式加载器] + +`xeon` CPU 镜像不包含 `runai-model-streamer-s3`,直接用 `s3://` 模型路径启动会报 `ImportError: S3 files module not found`。启动服务前需要在容器内安装该包——镜像的虚拟环境由 `uv` 管理,没有 `pip` 模块。 + +::: + +## 1. 上传模型到 RustFS + +从 Hugging Face 获取模型文件并拷入桶,替换全部连接占位符: + +```bash +docker run --rm -v "$PWD/models:/models" --entrypoint python3 \ + lmsysorg/sglang:v0.5.22-xeon -c \ + 'from huggingface_hub import snapshot_download; snapshot_download("Qwen/Qwen2.5-0.5B-Instruct", local_dir="/models/qwen2.5-0.5b-instruct")' + +aws s3 cp models/qwen2.5-0.5b-instruct \ + s3://my-bucket/sglang-models/qwen2.5-0.5b-instruct --recursive \ + --endpoint-url http://:9000 \ + --acl private +``` + +为后续命令导出凭证: + +```bash +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_DEFAULT_REGION=us-east-1 +``` + +每个模型使用一个独立前缀,多个模型即可共用同一个桶。流式加载器仅读取 `.safetensors` 格式的权重文件。 + +## 2. 启动 SGLang 指向桶 + +运行 CPU 镜像,安装 S3 流式加载包,然后启动服务指向桶 URI: + +```bash +docker run -d --name sglang -p 30000:30000 \ + -e AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY \ + -e AWS_DEFAULT_REGION \ + -e AWS_ENDPOINT_URL=http://:9000 \ + --entrypoint bash lmsysorg/sglang:v0.5.22-xeon -c \ + 'uv pip install -q runai-model-streamer-s3 && \ + python3 -m sglang.launch_server \ + --model-path s3://my-bucket/sglang-models/qwen2.5-0.5b-instruct \ + --served-model-name qwen2.5-0.5b-instruct \ + --host 0.0.0.0 --port 30000 \ + --device cpu --trust-remote-code \ + --disable-overlap-schedule \ + --mem-fraction-static 0.45' +``` + +`AWS_ENDPOINT_URL` 把流式加载器指向 RustFS;对自定义端点的请求使用路径样式寻址。`--mem-fraction-static` 决定 CPU 后端上 KV cache 占用主机内存的比例——空闲内存更多的主机可以调高。等待启动完成: + +```bash +docker logs -f sglang +``` + +```text +The server is fired up and ready to roll! +``` + +## 3. 运行推理 + +确认模型已提供服务,然后发送 OpenAI 兼容的补全请求: + +```bash +curl -s http://localhost:30000/v1/models +``` + +```json +{"object":"list","data":[{"id":"qwen2.5-0.5b-instruct","object":"model","owned_by":"sglang","root":"qwen2.5-0.5b-instruct","max_model_len":32768}]} +``` + +```bash +curl -s http://localhost:30000/v1/completions \ + -H "Content-Type: application/json" \ + -d '{"model":"qwen2.5-0.5b-instruct","prompt":"RustFS is","max_tokens":16,"temperature":0}' +``` + +```json +{"object":"text_completion","model":"qwen2.5-0.5b-instruct","choices":[{"index":0,"text":" a file system for the Linux kernel that is designed to be used in embedded systems","finish_reason":"length"}],"usage":{"prompt_tokens":4,"total_tokens":20,"completion_tokens":16}} +``` + +![SGLang 模型权重存放在 RustFS 控制台](./images/rustfs-sglang-models.png) + +## 4. 停止或重置 + +保留桶内对象并拆除演示环境: + +```bash +docker rm -f sglang +``` + +删除已存储的模型: + +```bash +aws s3 rm s3://my-bucket/sglang-models --recursive \ + --endpoint-url http://:9000 +``` + +## 故障排查 + +### `ImportError: S3 files module not found` + +runai 流式加载器的 S3 后端缺失。按第 2 步所示,在启动服务前于容器内执行 `uv pip install runai-model-streamer-s3`。 + +### `pip: command not found` 或 `No module named pip` + +镜像中的 `/opt/.venv` 由 `uv` 创建且未内置 `pip`。请使用 `uv pip install` 而不是 `pip install`。 + +### `Loaded weights leave no GPU memory for the KV cache` + +`--mem-fraction-static` 相对当前主机加载的权重来说过小。错误信息会给出最低可行值——用更大的比例重启服务(例如 16 GB 主机上的 0.5 B 模型取 `0.45`)。 + +### 请求报签名或访问错误 + +确认服务容器的四个变量都已设置:`AWS_ACCESS_KEY_ID`、`AWS_SECRET_ACCESS_KEY`、`AWS_DEFAULT_REGION`,以及带协议前缀的 `AWS_ENDPOINT_URL`。 + +## 下一步 + +- 在启用更多服务操作前,先阅读 [S3 兼容性说明](/administration/protocols/s3)。 +- 使用[访问密钥管理](/security-compliance/iam/access-token)创建专用的生产凭证。 +- 按照 [SGLang 文档](https://docs.sglang.io/)在同一个桶支撑的模型库之上使用张量并行、量化与更大的模型。 diff --git a/content/zh/developer/integration/big-data/images/rustfs-pulsar-offload.png b/content/zh/developer/integration/big-data/images/rustfs-pulsar-offload.png new file mode 100644 index 000000000..ab696db2f Binary files /dev/null and b/content/zh/developer/integration/big-data/images/rustfs-pulsar-offload.png differ diff --git a/content/zh/developer/integration/big-data/meta.json b/content/zh/developer/integration/big-data/meta.json index 5df7d07f9..f36bf234f 100644 --- a/content/zh/developer/integration/big-data/meta.json +++ b/content/zh/developer/integration/big-data/meta.json @@ -14,6 +14,7 @@ "pyiceberg", "spark", "seatunnel", - "zeppelin" + "zeppelin", + "pulsar" ] } diff --git a/content/zh/developer/integration/big-data/pulsar.md b/content/zh/developer/integration/big-data/pulsar.md new file mode 100644 index 000000000..35985d568 --- /dev/null +++ b/content/zh/developer/integration/big-data/pulsar.md @@ -0,0 +1,170 @@ +--- +title: "Apache Pulsar" +description: "用分层存储把 Pulsar 的冷 topic backlog 从 BookKeeper 卸载到 RustFS 桶。" +--- + +本指南将 [Apache Pulsar](https://github.com/apache/pulsar) 通过其分层存储 S3 offloader 连接到 **RustFS**。你将运行一个 standalone broker 并把卸载指向 RustFS 桶,生产消息、触发 ledger 卸载,再从桶中把卸载的消息读回来。整个流程使用 `apachepulsar/pulsar-all:4.0.4` 把 topic 卸载到 RustFS 并从中冷读验证通过。 + +你需要 Docker。本部署用于本地集成测试,不适用于生产环境。 + +## 架构 + +```mermaid +flowchart LR + Producer["pulsar-client produce"] --> Broker["Pulsar standalone"] + Broker -->|"hot ledgers"| BK["BookKeeper"] + Broker -->|"offload ledgers"| RustFS["RustFS :9000"] + Consumer["pulsar-client consume"] -->|"cold reads"| Broker +``` + +旧 ledger 以 `-ledger-` 数据块加一个小体积 `-index` 对象的形式上传到桶。当消费者请求的消息已不在 BookKeeper 时,broker 会透明地从桶中读回。 + +## 1. 启动带卸载的 standalone broker + +基础镜像 `apachepulsar/pulsar` 不带 offloader 包,启动会报 `No offloader found for driver 'S3'`——请使用包含它们的 `pulsar-all` 镜像。broker 的堆内存做了上限约束: + +```bash +docker run -d --name pulsar -p 6650:6650 -p 8080:8080 \ + -e ACCESS_KEY_ID= \ + -e ACCESS_KEY_SECRET= \ + -e PULSAR_MEM="-Xms512m -Xmx512m" \ + --entrypoint bash apachepulsar/pulsar-all:4.0.4 -c ' + sed -i -e "s|^managedLedgerMaxEntriesPerLedger=.*|managedLedgerMaxEntriesPerLedger=100|" \ + -e "s|^managedLedgerMinLedgerRolloverTimeMinutes=.*|managedLedgerMinLedgerRolloverTimeMinutes=1|" \ + /pulsar/conf/standalone.conf + printf "\nmanagedLedgerOffloadDriver=S3\nmanagedLedgerOffloadBucket=my-bucket\nmanagedLedgerOffloadServiceEndpoint=http://:9000\nmanagedLedgerOffloadRegion=us-east-1\n" >> /pulsar/conf/standalone.conf + exec bin/pulsar standalone -nss + ' +``` + +两条 `managedLedger*` 修改把 ledger 调小,让演示内就能完成滚动;追加的配置块启用卸载。 + +:::note[驱动 S3 读取 managedLedgerOffload* 键,而不是 s3ManagedLedgerOffload*] + +对驱动 `S3`,offloader 解析的是通用键 `managedLedgerOffloadBucket`、`managedLedgerOffloadServiceEndpoint`、`managedLedgerOffloadRegion`。`s3ManagedLedgerOffload*` 名字属于 `aws-s3` 驱动,在这里会被忽略——用了它们,offloader 会以 `NullPointerException: endpoint` 崩溃。凭证同理:驱动 `S3` 读取环境变量 `ACCESS_KEY_ID` 和 `ACCESS_KEY_SECRET`,不是 `AWS_*` 这对。 + +::: + +等待 broker 就绪: + +```bash +docker logs -f pulsar 2>&1 | grep -m1 "The ledger offloader was created" +``` + +## 2. 生产消息并保活 topic + +没有订阅的 topic 会在几分钟内被 inactive-topic 清理删除,演示会中途作废。先生产一批消息,然后创建一个持久订阅保住 topic: + +```bash +docker exec pulsar bin/pulsar-client produce -m "hello rustfs offload" -n 300 \ + persistent://public/default/rustfs-test +``` + +```text +300 messages successfully produced +``` + +```bash +timeout 30 docker exec pulsar bin/pulsar-client consume -s keepalive -n 1 -p Earliest \ + persistent://public/default/rustfs-test +``` + +## 3. 滚动 ledger 并触发卸载 + +只有当 `managedLedgerMinLedgerRolloverTimeMinutes` 到期后仍有写入时 ledger 才会滚动——检查发生在写入时,不是后台。等满一分钟,再生产一批迫使滚动,然后把最后一 KB 之前的所有数据卸载: + +```bash +sleep 65 +docker exec pulsar bin/pulsar-client produce -m "rollover" -n 120 \ + persistent://public/default/rustfs-test +docker exec pulsar bin/pulsar-admin topics offload -s 1K \ + persistent://public/default/rustfs-test +``` + +```text +Offload triggered for persistent://public/default/rustfs-test for messages before 11:0:-1 +``` + +卸载需要一点时间——等待完成: + +```bash +sleep 30 +docker exec pulsar bin/pulsar-admin topics offload-status \ + persistent://public/default/rustfs-test +``` + +```text +Offload was a success +``` + +确认 ledger 对象已落在 RustFS——数据块加索引,位于桶根: + +```bash +aws s3 ls s3://my-bucket/ --recursive \ + --endpoint-url http://:9000 | grep ledger +``` + +```text +2026-10-25 02:34:49 21057 7f9379cc-c21d-4829-a857-4c39ec02e391-ledger-9 +2026-10-25 02:34:49 214 7f9379cc-c21d-4829-a857-4c39ec02e391-ledger-9-index +``` + +## 4. 冷读卸载的消息 + +用全新订阅从最早位置消费。第一个 ledger 现在只存在于 RustFS 中,broker 返回的每条消息都是一次从桶的冷读: + +```bash +timeout 90 docker exec pulsar bin/pulsar-client consume -s coldread -n 420 -p Earliest \ + persistent://public/default/rustfs-test 2>&1 | grep -c "^-----" +``` + +```text +420 +``` + +420 条消息——卸载 ledger 的 300 条加上活跃 ledger 的 120 条——全部经桶读回。 + +![RustFS 控制台中 Pulsar 卸载的 ledger 对象](./images/rustfs-pulsar-offload.png) + +## 5. 停止或重置 + +保留桶内对象并拆除 broker: + +```bash +docker rm -f pulsar +``` + +删除已卸载的 ledger: + +```bash +aws s3 rm s3://my-bucket/ --recursive --exclude "*" --include "*ledger*" \ + --endpoint-url http://:9000 +``` + +## 故障排查 + +### `No offloader found for driver 'S3'. Please make sure you dropped the offloader nar packages` + +镜像里没有 offloader NAR。按第 1 步使用 `apachepulsar/pulsar-all`。 + +### 卸载时报 `NullPointerException: endpoint` + +offloader 没有读到 service endpoint——`managedLedgerOffload*` 键缺失,或写成了 `aws-s3` 驱动的 `s3ManagedLedgerOffload*` 名字。按第 1 步追加配置块并重启。 + +### broker 日志出现 `Couldn't get the access key id.` + +驱动 `S3` 不读 `AWS_ACCESS_KEY_ID`。在 broker 容器上导出 `ACCESS_KEY_ID` 和 `ACCESS_KEY_SECRET`。 + +### 演示中途出现 `Topic ... not found` + +inactive-topic 清理把没有订阅的 topic 删了。重跑第 2 步,并保持 `keepalive` 订阅存在。 + +### 显示 `Offload was a success` 但桶里没有对象 + +只有早于大小阈值且已关闭的 ledger 会被上传,而 ledger 在超过最小滚动年龄后的下一次写入时才滚动。再生产一批,然后重新触发卸载。 + +## 下一步 + +- 在 namespace 上启用自动卸载阈值前,先阅读 [S3 兼容性说明](/administration/protocols/s3)。 +- 使用[访问密钥管理](/security-compliance/iam/access-token)创建专用的生产凭证。 +- 按照 [Pulsar 分层存储文档](https://pulsar.apache.org/docs/next/cookbooks-tiered-storage/)在同一个桶上使用 namespace 阈值、保留策略与多节点 broker。 diff --git a/content/zh/developer/integration/cloud-native/camel.md b/content/zh/developer/integration/cloud-native/camel.md new file mode 100644 index 000000000..31b29cfda --- /dev/null +++ b/content/zh/developer/integration/cloud-native/camel.md @@ -0,0 +1,127 @@ +--- +title: "Apache Camel" +description: "用 Apache Camel 集成路由在 RustFS 桶之间搬运数据。" +--- + +本指南将多面手集成框架 [Apache Camel](https://github.com/apache/camel) 通过其 S3 组件连接到 **RustFS**。你将用 YAML 编写两条路由:定时把消息写入桶的生产者路由,以及读回这些对象并记录日志的消费者路由。整个流程使用 Camel JBang(`eclipse-temurin:21-jdk` 镜像)和 `aws2-s3` 组件(覆写端点 + 路径样式寻址)验证通过。 + +你需要 Docker。本部署用于本地集成测试,不适用于生产环境。 + +## 架构 + +```mermaid +flowchart LR + Timer["timer:tick"] -->|"CamelAwsS3Key header"| Producer["aws2-s3 producer"] + Producer -->|"PutObject"| RustFS["RustFS :9000"] + RustFS -->|"prefix=camel/ poll"| Consumer["aws2-s3 consumer"] + Consumer --> Log["log:from-rustfs"] +``` + +Camel JBang 直接运行路由文件并自动解析 S3 组件依赖——无需项目脚手架、构建或部署步骤。 + +## 1. 编写集成路由 + +创建路由文件。凭证来自默认 AWS 凭证链(环境变量),URI 中只携带寻址信息: + +```yaml title="route.yaml" +- from: + uri: "timer:tick" + parameters: + period: "5000" + steps: + - setBody: + simple: "Hello from Camel at ${date:now:yyyy-MM-dd HH:mm:ss}" + - setHeader: + name: "CamelAwsS3Key" + simple: "camel/hello-${date:now:HHmmss}.txt" + - to: + uri: "aws2-s3://my-bucket?overrideEndpoint=true&uriEndpointOverride=http://:9000&forcePathStyle=true&useDefaultCredentialsProvider=true" +- from: + uri: "aws2-s3://my-bucket?prefix=camel/&deleteAfterRead=false&maxMessagesPerPoll=5&overrideEndpoint=true&uriEndpointOverride=http://:9000&forcePathStyle=true&useDefaultCredentialsProvider=true" + steps: + - to: + uri: "log:from-rustfs" +``` + +生产者通过 `CamelAwsS3Key` 头设置对象 key——这里是每条消息一个时间戳,因此每次触发都会写入一个新对象。消费者以 `deleteAfterRead=false` 轮询 `camel/` 前缀,对象消费后仍保留在桶中。 + +:::note[组件名与端点参数] + +本工作流中 Camel JBang 解析到的组件是 `aws2-s3`。`overrideEndpoint=true` 激活自定义端点,`uriEndpointOverride` 携带其 URL,`forcePathStyle=true` 选择 RustFS 期望的路径样式寻址,`useDefaultCredentialsProvider=true` 让组件从标准环境变量读取凭证。 + +::: + +## 2. 用 Camel JBang 运行路由 + +在导出凭证的容器中运行集成。JBang 首次使用会请求信任来源,先加好信任规则再启动: + +```bash +docker run --rm --network bridge \ + -e AWS_ACCESS_KEY_ID= \ + -e AWS_SECRET_ACCESS_KEY= \ + -e AWS_REGION=us-east-1 \ + -v "$PWD:/routes" \ + --entrypoint bash eclipse-temurin:21-jdk -c ' + export JBANG_DIR=/root/.jbang PATH=/root/.jbang/bin:$PATH + curl -Ls https://sh.jbang.dev | bash >/dev/null 2>&1 + jbang trust add https://github.com/apache/ 2>/dev/null || true + jbang trust add /routes 2>/dev/null || true + cd /routes && jbang camel@apache/camel run route.yaml + ' +``` + +首次运行会下载 Camel 运行时与依赖,然后启动路由。消费者每读到一个对象就输出一条日志: + +```text +2026-10-25 02:02:41.069 INFO 120 --- [-s3://my-bucket] from-rustfs : Exchange[ExchangePattern: InOnly, BodyType: byte[], Body: Hello from Camel at 2026-10-25 02:02:38] +``` + +## 3. 验证对象 + +确认生产者写入的对象已在 RustFS 中,替换全部连接占位符: + +```bash +aws s3 ls s3://my-bucket/camel/ \ + --endpoint-url http://:9000 +``` + +```text +2026-10-25 02:02:13 39 hello-020213.txt +2026-10-25 02:02:18 39 hello-020218.txt +2026-10-25 02:02:23 39 hello-020223.txt +``` + +![RustFS 控制台中的 Apache Camel 路由对象](./images/rustfs-camel-route.png) + +## 4. 停止或重置 + +停止容器即结束路由。删除演示对象: + +```bash +aws s3 rm s3://my-bucket/camel --recursive \ + --endpoint-url http://:9000 +``` + +## 故障排查 + +### `Cannot find component with name: aws-s3` + +你的 Camel 版本以别的名字解析组件——本工作流用的是 `aws2-s3`。以运行时建议列表(`Did you mean: ... aws2-s3 ...`)里的名字为准。 + +### `is not from a trusted source` + +JBang 拒绝运行不受信任的代码。按第 2 步先执行 `jbang trust add https://github.com/apache/` 和 `jbang trust add /routes` 再运行。 + +### `useDefaultCredentialsProvider is set to false` + +组件没有在 URI 中找到凭证配置。要么加 `useDefaultCredentialsProvider=true` 并导出 `AWS_ACCESS_KEY_ID` 和 `AWS_SECRET_ACCESS_KEY`,要么直接配置 `accessKey`、`secretKey` 端点参数。 + +### 消费者读不到数据 + +消费者只轮询 `prefix=camel/` 给出的前缀。确认生产者已运行(有 `timer` 日志),且其 `CamelAwsS3Key` 头以 `camel/` 开头。 + +## 下一步 + +- 在采用分段或流式操作前,先阅读 [S3 兼容性说明](/administration/protocols/s3)。 +- 使用[访问密钥管理](/security-compliance/iam/access-token)创建专用的生产凭证。 +- 按照 [Camel S3 组件文档](https://camel.apache.org/components/latest/aws2-s3-component.html)在相同路由上使用下载区间、桶操作与消费者调优。 diff --git a/content/zh/developer/integration/cloud-native/dapr.md b/content/zh/developer/integration/cloud-native/dapr.md new file mode 100644 index 000000000..8bc621a00 --- /dev/null +++ b/content/zh/developer/integration/cloud-native/dapr.md @@ -0,0 +1,144 @@ +--- +title: "Dapr" +description: "任何 Dapr 应用都能通过 AWS S3 binding 读写 RustFS 对象。" +--- + +本指南将 CNCF 的分布式应用运行时 [Dapr](https://github.com/dapr/dapr) 通过其 AWS S3 binding 连接到 **RustFS**。你将声明一个以 RustFS 桶为后端的 binding 组件,运行 sidecar,并用 binding 的 HTTP API 创建对象、读回并独立确认。整个流程使用 `daprio/dapr:1.19.0` 以路径样式寻址对 RustFS 桶验证通过。 + +你需要 Docker。本部署用于本地集成测试,不适用于生产环境。 + +## 架构 + +```mermaid +flowchart LR + App["Your app"] -->|"POST /v1.0/bindings/rustfs-s3"| Daprd["daprd sidecar :3500"] + Daprd -->|"bindings.aws.s3"| RustFS["RustFS :9000"] +``` + +应用不持有任何 S3 凭证或端点。sidecar 把桶暴露为命名 binding,每个操作都是一次对 binding 端点的 POST,携带 `operation` 与可选 `metadata`。 + +## 1. 声明 binding 组件 + +创建组件文件。`endpoint` 带协议前缀,`forcePathStyle` 对齐 RustFS 的路径样式寻址: + +```yaml title="rustfs-s3.yaml" +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: rustfs-s3 +spec: + type: bindings.aws.s3 + version: v1 + metadata: + - name: bucket + value: my-bucket + - name: endpoint + value: http://:9000 + - name: accessKey + value: + - name: secretKey + value: + - name: region + value: us-east-1 + - name: forcePathStyle + value: "true" +``` + +## 2. 启动 sidecar + +挂载组件目录运行 `daprd`。`--resources-path` 取代了已弃用的 `--components-path`: + +```bash +docker run -d --name dapr \ + -v "$PWD:/components" \ + daprio/dapr:1.19.0 /daprd \ + --app-id s3demo \ + --dapr-http-port 3500 \ + --resources-path /components +``` + +确认 binding 已初始化——日志出现: + +```bash +docker logs dapr 2>&1 | grep "successful init" +``` + +```text +successful init for output binding (rustfs-s3 (bindings.aws.s3/v1)) +``` + +## 3. 通过 binding 创建对象 + +从任何能访问 sidecar 的 HTTP 客户端调用 binding。`key` 元数据决定对象名: + +```bash +curl -s -X POST http://localhost:3500/v1.0/bindings/rustfs-s3 \ + -H "Content-Type: application/json" \ + -d '{"operation":"create","data":"hello from dapr","metadata":{"key":"dapr/hello.txt"}}' +``` + +```json +{"location":"http://:9000/my-bucket/dapr/hello.txt","versionID":null} +``` + +## 4. 读回对象 + +`get` 操作在响应体中返回对象内容: + +```bash +curl -s -X POST http://localhost:3500/v1.0/bindings/rustfs-s3 \ + -H "Content-Type: application/json" \ + -d '{"operation":"get","metadata":{"key":"dapr/hello.txt"}}' +``` + +```text +hello from dapr +``` + +用独立客户端确认同一个对象: + +```bash +aws s3 cp s3://my-bucket/dapr/hello.txt - \ + --endpoint-url http://:9000 +``` + +```text +hello from dapr +``` + +![RustFS 控制台中的 Dapr binding 对象](./images/rustfs-dapr-binding.png) + +## 5. 停止或重置 + +保留桶内对象并拆除 sidecar: + +```bash +docker rm -f dapr +``` + +删除演示对象: + +```bash +aws s3 rm s3://my-bucket/dapr/hello.txt \ + --endpoint-url http://:9000 +``` + +## 故障排查 + +### binding 初始化失败并报 S3 错误 + +把四个寻址值放在一起检查:`endpoint` 必须带协议,`region` 对非 AWS 端点也是必填,`forcePathStyle` 必须是 `"true"`。缺少 `forcePathStyle` 时发出的是 RustFS 拒绝的虚拟主机请求。 + +### 调用时报 `no such binding` + +sidecar 没有加载组件——确认文件以 `.yaml` 结尾、位于传给 `--resources-path` 的目录里,且日志中出现第 2 步的初始化行。 + +### `get` 返回空内容 + +`metadata.key` 与创建时的 key 不匹配。key 区分大小写并包含前缀。 + +## 下一步 + +- 在采用 binding 的 `list`、`delete`、`presign` 操作前,先阅读 [S3 兼容性说明](/administration/protocols/s3)。 +- 使用[访问密钥管理](/security-compliance/iam/access-token)创建专用的生产凭证,并通过 Dapr secret store 引用它们而不是组件里的明文值。 +- 按照 [Dapr S3 binding 文档](https://docs.dapr.io/reference/components-reference/supported-bindings/s3/)使用完整操作集与重试行为。 diff --git a/content/zh/developer/integration/cloud-native/images/rustfs-camel-route.png b/content/zh/developer/integration/cloud-native/images/rustfs-camel-route.png new file mode 100644 index 000000000..1151470d1 Binary files /dev/null and b/content/zh/developer/integration/cloud-native/images/rustfs-camel-route.png differ diff --git a/content/zh/developer/integration/cloud-native/images/rustfs-dapr-binding.png b/content/zh/developer/integration/cloud-native/images/rustfs-dapr-binding.png new file mode 100644 index 000000000..54ae9698f Binary files /dev/null and b/content/zh/developer/integration/cloud-native/images/rustfs-dapr-binding.png differ diff --git a/content/zh/developer/integration/cloud-native/meta.json b/content/zh/developer/integration/cloud-native/meta.json index c98a1487c..44be8b4a1 100644 --- a/content/zh/developer/integration/cloud-native/meta.json +++ b/content/zh/developer/integration/cloud-native/meta.json @@ -3,6 +3,8 @@ "pages": [ "cortex", "flux", - "celld" + "celld", + "camel", + "dapr" ] } diff --git a/content/zh/developer/integration/devops/helm.md b/content/zh/developer/integration/devops/helm.md new file mode 100644 index 000000000..55387eb76 --- /dev/null +++ b/content/zh/developer/integration/devops/helm.md @@ -0,0 +1,137 @@ +--- +title: "Helm" +description: "用 helm-s3 插件把私有 Helm Chart 仓库托管在 RustFS 桶中。" +--- + +本指南将 Kubernetes 的 CNCF 包管理器 [Helm](https://github.com/helm/helm) 通过 [helm-s3](https://github.com/hypnoglow/helm-s3) 插件连接到 **RustFS**,让 RustFS 桶成为私有 Chart 仓库。你将在桶中初始化仓库、推送打包好的 Chart,再把仓库添加进 Helm 并拉回 Chart。整个流程使用 `alpine/helm:3.16.4` 和 helm-s3 `v0.17.2` 对 RustFS 桶验证通过。 + +你需要 Docker。本部署用于本地集成测试,不适用于生产环境。 + +## 架构 + +```mermaid +flowchart LR + Dev["helm package"] -->|"helm s3 push"| Repo["s3://my-bucket/charts"] + Repo -->|"index.yaml"| Helm["helm repo add"] + Helm -->|"helm pull / install"| K8s["cluster"] +``` + +桶前缀中保存 `index.yaml` 和每个 Chart 版本一个 `.tgz`。Helm 通过插件读写它们,因此任何能访问 RustFS 的地方都能使用这个仓库。 + +## 1. 安装插件并初始化仓库 + +`alpine/helm` 镜像不带 `git`,因此从插件的发布压缩包安装,而不是仓库 URL: + +```bash +cd /tmp +wget -q https://github.com/hypnoglow/helm-s3/releases/download/v0.17.2/helm-s3_0.17.2_linux_amd64.tar.gz +mkdir -p helm-s3 && tar -xzf helm-s3_0.17.2_linux_amd64.tar.gz -C helm-s3 +helm plugin install helm-s3 +``` + +通过插件自己的环境变量把它指向 RustFS,并初始化仓库前缀: + +```bash +export AWS_ENDPOINT=:9000 +export AWS_DISABLE_SSL=true +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_DEFAULT_REGION=us-east-1 + +helm s3 init s3://my-bucket/charts +``` + +```text +Initialized empty repository at s3://my-bucket/charts +``` + +:::note[helm-s3 用 AWS_ENDPOINT,不是 AWS_ENDPOINT_URL] + +插件用 `AWS_ENDPOINT`(主机与端口,不带协议)加 `AWS_DISABLE_SSL=true` 构造明文 HTTP 的请求 URL。标准的 `AWS_ENDPOINT_URL` 变量不会被识别,SDK 默认的虚拟主机寻址对 RustFS 会以 `403 Forbidden` 失败。 + +::: + +## 2. 推送 Chart + +第一次推送前先注册仓库——插件会读取 Helm 的仓库文件,文件不存在会报错: + +```bash +helm repo add rustfs-charts s3://my-bucket/charts +``` + +创建并打包一个演示 Chart,然后推送。`helm s3 push` 接收的是**仓库名**,不是 `s3://` URI: + +```bash +helm create demo +helm package demo +helm s3 push ./demo-0.1.0.tgz rustfs-charts +``` + +```text +Successfully uploaded the chart to the repository. +``` + +## 3. 从桶安装或拉取 + +刷新仓库索引,确认 Chart 可被发现: + +```bash +helm repo update +helm search repo rustfs-charts/demo +``` + +```text +NAME CHART VERSION APP VERSION DESCRIPTION +rustfs-charts/demo 0.1.0 1.16.0 A Helm chart for Kubernetes +``` + +把 Chart 从 RustFS 拉回来: + +```bash +helm pull rustfs-charts/demo --destination ./pullback +tar -tzf ./pullback/demo-0.1.0.tgz | head -2 +``` + +```text +demo/Chart.yaml +demo/values.yaml +``` + +![RustFS 控制台中的 Helm Chart 仓库对象](./images/rustfs-helm-charts.png) + +## 4. 停止或重置 + +Chart 就是普通的桶对象,清理即删除前缀: + +```bash +aws s3 rm s3://my-bucket/charts --recursive \ + --endpoint-url http://:9000 +``` + +## 故障排查 + +### `helm s3 init` 报 `head s3 object: Forbidden` + +插件在使用虚拟主机寻址——`AWS_ENDPOINT` 和 `AWS_DISABLE_SSL` 没有设置。按第 1 步原样导出它们。 + +### `check if index exists in the storage` 报 git 相关错误 + +镜像里没有 `git`,`helm plugin install ` 无法克隆。按第 1 步从发布压缩包安装。 + +### `The index file already exists under the provided URI` + +仓库前缀之前初始化过。改用 `helm s3 init --ignore-if-exists s3://my-bucket/charts` 重跑,或加 `--force` 替换索引。 + +### push 时报 `repo with name s3://... not found` + +`helm s3 push` 需要的是 `helm repo add` 注册过的仓库名——使用 `helm s3 push ./demo-0.1.0.tgz rustfs-charts`。 + +### push 时报 `couldn't load repositories file` + +当前环境还没有 Helm 的 `repositories.yaml`。先执行 `helm repo add rustfs-charts s3://my-bucket/charts` 再推送。 + +## 下一步 + +- 在托管包含大量 Chart 版本的大型仓库前,先阅读 [S3 兼容性说明](/administration/protocols/s3)。 +- 使用[访问密钥管理](/security-compliance/iam/access-token)创建专用的生产凭证。 +- 按照 [helm-s3 文档](https://github.com/hypnoglow/helm-s3)在同一个桶上使用仓库重建索引、Chart 删除与 CI 集成。 diff --git a/content/zh/developer/integration/devops/images/rustfs-helm-charts.png b/content/zh/developer/integration/devops/images/rustfs-helm-charts.png new file mode 100644 index 000000000..cd0af4957 Binary files /dev/null and b/content/zh/developer/integration/devops/images/rustfs-helm-charts.png differ diff --git a/content/zh/developer/integration/devops/meta.json b/content/zh/developer/integration/devops/meta.json index 55eada6ae..1f41e2a72 100644 --- a/content/zh/developer/integration/devops/meta.json +++ b/content/zh/developer/integration/devops/meta.json @@ -5,6 +5,7 @@ "opensearch", "gitea", "jenkins", - "terraform" + "terraform", + "helm" ] } diff --git a/content/zh/developer/integration/observability/fluent-bit.md b/content/zh/developer/integration/observability/fluent-bit.md new file mode 100644 index 000000000..cca938bc8 --- /dev/null +++ b/content/zh/developer/integration/observability/fluent-bit.md @@ -0,0 +1,154 @@ +--- +title: "Fluent Bit" +description: "通过 S3 输出插件把 Fluent Bit 的日志与事件投递到 RustFS 桶。" +--- + +本指南将 CNCF 的日志处理器与转发器 [Fluent Bit](https://github.com/fluent/fluent-bit) 连接到 **RustFS** 作为日志目的地。你将运行带 `s3` 输出的 Fluent Bit,把记录缓冲到磁盘、上传进 RustFS 桶,并读回一条已投递的日志文件。整个流程使用 `fluent/fluent-bit:3.2.10` 以路径样式寻址写入 RustFS 桶验证通过。 + +你需要 Docker。本部署用于本地集成测试,不适用于生产环境。 + +## 架构 + +```mermaid +flowchart LR + Input["Inputs: tail, dummy, http"] -->|"tagged records"| FB["Fluent Bit"] + FB -->|"buffer (store_dir)"| Disk["Local buffer"] + Disk -->|"PutObject"| RustFS["RustFS :9000"] + Reader["aws cli / Console"] -->|"JSON lines"| RustFS +``` + +S3 输出把带标签的记录缓冲在 `store_dir` 中,文件达到 `total_file_size` 或 `upload_timeout` 到期时上传——以先到者为准。每个上传的对象是一个 JSON Lines 文件。 + +## 1. 创建桶 + +S3 输出从不创建桶,桶不存在时上传会报 `NoSuchBucket`。先创建桶,替换全部连接占位符: + +```bash +aws s3 mb s3://my-bucket \ + --endpoint-url http://:9000 +``` + +## 2. 配置 S3 输出 + +创建 Fluent Bit 配置: + +```text title="fluent-bit.conf" +[SERVICE] + Flush 2 + Log_Level info + +[INPUT] + Name dummy + Tag rustfs.test + Rate 200 + +[OUTPUT] + Name s3 + Match * + endpoint http://:9000 + region us-east-1 + bucket my-bucket + total_file_size 1M + use_put_object On + upload_timeout 30s + store_dir /tmp/flb-store + json_date_key date + json_date_format iso8601 +``` + +`use_put_object On` 让每个缓冲文件用一次 `PutObject` 上传。`upload_timeout` 在缓冲未达到 `total_file_size` 时也会在 30 秒后强制上传——低流量输入必须设置它,否则记录会一直留在本地磁盘。 + +:::note[自定义端点默认就是路径样式] + +Fluent Bit 3.2 没有 `s3_force_path_style` 选项——插件会把它当作未知属性拒绝。当 `endpoint` 指向自定义 S3 服务时,请求按路径样式访问桶,与 RustFS 默认行为一致,无需任何额外开关。 + +::: + +## 3. 运行 Fluent Bit + +启动容器并通过标准 AWS 环境变量传入凭证: + +```bash +docker run -d --name fluent-bit \ + -e AWS_ACCESS_KEY_ID= \ + -e AWS_SECRET_ACCESS_KEY= \ + -v "$PWD/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro" \ + fluent/fluent-bit:3.2.10 +``` + +等待第一次上传,并确认 worker 正常启动: + +```bash +docker logs fluent-bit 2>&1 | grep "worker #0 started" +``` + +```text +[output:s3:s3.0] worker #0 started +``` + +## 4. 验证上传的对象 + +列出桶内容——默认 key 布局是 `fluent-bit-logs//YYYY/MM/DD/HH/MM/`: + +```bash +aws s3 ls s3://my-bucket/ --recursive \ + --endpoint-url http://:9000 +``` + +```text +2026-10-25 01:13:28 1021326 fluent-bit-logs/rustfs.test/2026/10/25/01/13/59-objectbmjH9744 +2026-10-25 01:16:05 333735 fluent-bit-logs/rustfs.test/2026/10/25/01/15/34-objectKfCq13io +``` + +读回一个对象——每一行都是一条 JSON 记录: + +```bash +aws s3 cp s3://my-bucket/fluent-bit-logs/rustfs.test/2026/10/25/01/13/59-objectbmjH9744 - \ + --endpoint-url http://:9000 | head -2 +``` + +```json +{"date":"2026-10-25T01:13:59.204844Z","message":"dummy"} +{"date":"2026-10-25T01:13:59.206396Z","message":"dummy"} +``` + +![Fluent Bit 日志投递到 RustFS 控制台](./images/rustfs-fluentbit-logs.png) + +## 5. 停止或重置 + +保留桶内对象并拆除演示环境: + +```bash +docker rm -f fluent-bit +``` + +删除已上传的日志: + +```bash +aws s3 rm s3://my-bucket/fluent-bit-logs --recursive \ + --endpoint-url http://:9000 +``` + +## 故障排查 + +### `PutObject request failed` 且报 `NoSuchBucket` + +输出桶不存在。按第 1 步创建桶——插件从不自建桶。 + +### `[config] s3: unknown configuration property 's3_force_path_style'` + +Fluent Bit 3.2 没有这个选项。删掉它即可;自定义端点本来就使用路径样式寻址。 + +### 对象一直不出现且日志无报错 + +缓冲未达到 `total_file_size`,且没有设置 `upload_timeout`。加上 `upload_timeout 30s`,低流量输入就会定期上传。 + +### Fluent Bit 日志里出现 `InvalidAccessKeyId` + +容器的凭证环境变量缺失或值有误。S3 输出读取 `AWS_ACCESS_KEY_ID` 和 `AWS_SECRET_ACCESS_KEY`。 + +## 下一步 + +- 在启用分段上传或压缩前,先阅读 [S3 兼容性说明](/administration/protocols/s3)。 +- 使用[访问密钥管理](/security-compliance/iam/access-token)创建专用的生产凭证。 +- 按照 [Fluent Bit S3 输出文档](https://docs.fluentbit.io/manual/pipeline/outputs/s3)在同一个桶上使用 `s3_key_format`、压缩与 `tail` 输入调优。 diff --git a/content/zh/developer/integration/observability/images/rustfs-fluentbit-logs.png b/content/zh/developer/integration/observability/images/rustfs-fluentbit-logs.png new file mode 100644 index 000000000..2a55182ca Binary files /dev/null and b/content/zh/developer/integration/observability/images/rustfs-fluentbit-logs.png differ diff --git a/content/zh/developer/integration/observability/meta.json b/content/zh/developer/integration/observability/meta.json index 7ef6a1a6c..68dd06969 100644 --- a/content/zh/developer/integration/observability/meta.json +++ b/content/zh/developer/integration/observability/meta.json @@ -1,6 +1,7 @@ { "title": "可观测性", "pages": [ + "fluent-bit", "fluentd", "greptimedb", "loki",