diff --git a/content/de/developer/integration/cloud-native/celld.md b/content/de/developer/integration/cloud-native/celld.md new file mode 100644 index 000000000..37e2a757c --- /dev/null +++ b/content/de/developer/integration/cloud-native/celld.md @@ -0,0 +1,173 @@ +--- +title: "celld" +description: "Run denoland celld — self-hosted Durable Objects — with fleet state on RustFS." +--- + +This guide connects [celld](https://github.com/denoland/celld) — the self-hosted, distributed Durable Objects runtime from Denoland — to **RustFS** as its fleet bucket. You will run `celld diagnose` against a RustFS bucket (including the conditional-write probe), deploy a Counter worker, mutate Durable Object state over HTTP, restart celld, and confirm the state survives. The workflow was verified with celld v0.6.2 against `rustfs/rustfs-x86-musl:v2.3.1`. + +You need the `celld` binary, `esbuild` (for bundling), and Node.js to drive the deployed worker. + +## Architecture + +```mermaid +flowchart LR + Client["HTTP client"] -->|"Worker fetch :8080"| celld["celld node"] + celld -->|"LTX replicas + ownership + deploy"| RustFS["RustFS :9000"] +``` + +celld keeps only a local WAL. Every committed SQLite write is captured in LTX format and uploaded to the fleet bucket; a conditional bucket write (put-if-not-exists) assigns node ownership. The bucket is the source of truth — nodes can crash or restart without losing Durable Object state. + +## 1. Install celld + +```bash +curl -sLo /tmp/celld.gz \ + https://github.com/denoland/celld/releases/download/v0.6.2/celld-x86_64-unknown-linux-gnu.gz +gunzip /tmp/celld.gz && chmod +x /tmp/celld +mv /tmp/celld /usr/local/bin/celld +celld --version +``` + +```text +celld 0.6.2 +``` + +Install esbuild as well — `celld deploy` bundles workers with it: + +```bash +npm install -g esbuild +``` + +## 2. Diagnose the bucket + +`celld diagnose` probes the bucket including the conditional write that celld requires for ownership fencing: + +```bash +export S3_ENDPOINT=http://:9000 +export AWS_REGION=us-east-1 +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= + +rc mb rustfs/celld-demo +celld diagnose --bucket s3://celld-demo +``` + +```text +ok listen 127.0.0.1:18080: bind check; diagnose does not serve +ok bucket s3://celld-demo +ok bucket conditional write: create, reject-create, update, reject-stale +ok fleet: 0 node lease(s) enumerated +``` + +`ok bucket conditional write` is the important line — RustFS supports put-if-not-exists natively, so no fallback storage is needed. + +## 3. Start a node + +A non-loopback `--listen` requires an explicit `--internal-listen`: + +```bash +nohup celld --bucket s3://celld-demo \ + --listen 0.0.0.0:18080 \ + --internal-listen 0.0.0.0:9099 \ + --advertise 127.0.0.1:9099 > /opt/celld/celld.out 2>&1 & +``` + +```text +INFO celld: host runtime initialized event="host_runtime" worker_count=4 +INFO celld::fleet: no deployment yet; run `celld deploy` ... +``` + +## 4. Deploy the Counter worker + +Use the counter example from the celld repository — a Durable Object that increments `n` in SQLite storage on every request: + +```bash +curl -sLo /tmp/celld.zip https://codeload.github.com/denoland/celld/zip/refs/tags/v0.6.2 +unzip /tmp/celld.zip -d /opt/celld +cd /opt/celld/celld-0.6.2/examples/counter +celld deploy . --bucket s3://celld-demo +``` + +```text +Uploaded counter (0.02 sec) + s3://celld-demo/deploy/counter/bf5806589892a6cc +Current Version ID: bf5806589892a6cc +``` + +## 5. Mutate Durable Object state + +Each request routes to the `Counter` Durable Object, which reads `n` from storage, increments, and writes it back: + +```bash +curl -s "http://127.0.0.1:18080/?name=rustfs" +curl -s "http://127.0.0.1:18080/?name=rustfs" +curl -s "http://127.0.0.1:18080/?name=rustfs" +``` + +```json +{"n":1,"url":"http://127.0.0.1:18080/?name=rustfs"} +{"n":2,"url":"http://127.0.0.1:18080/?name=rustfs"} +{"n":3,"url":"http://127.0.0.1:18080/?name=rustfs"} +``` + +## 6. Restart celld and verify persistence + +Kill the node, start it again, and hit the same URL — the counter continues from where it stopped, because the state was in RustFS the whole time: + +```bash +pkill -f "celld --bucket" +nohup celld --bucket s3://celld-demo \ + --listen 0.0.0.0:18080 --internal-listen 0.0.0.0:9099 \ + --advertise 127.0.0.1:9099 > /opt/celld/celld2.out 2>&1 & +sleep 8 +curl -s "http://127.0.0.1:18080/?name=rustfs" +``` + +```json +{"n":4,"url":"http://127.0.0.1:18080/?name=rustfs"} +``` + +List the bucket — the fleet state is all there: + +```bash +rc ls rustfs/celld-demo/ -r | head -5 +``` + +```text +cells/Counter:f2806128.../ltx/e1/0000/0000000000000001-0000000000000001.ltx +cells/Counter:f2806128.../own.json +deploy/counter/bf5806589892a6cc/index.js +deploy/counter/current.json +``` + +![celld state stored in the RustFS Console](./images/rustfs-celld-state.png) + +## 7. Stop or reset + +```bash +pkill -f "celld --bucket" +rc rm rustfs/celld-demo/ --recursive --force +``` + +## Troubleshooting + +### `bind --listen 127.0.0.0.1:8080 ... Address already in use` + +Port 8080 is commonly taken (a local GitLab registry or dev server). Move the Worker listener: `--listen 0.0.0.0:18080`. + +### `a non-loopback --listen requires an explicit --internal-listen` + +Binding a public interface requires a separate peer listener: add `--internal-listen 0.0.0.0:9099 --advertise 127.0.0.1:9099`. + +### `esbuild not found` + +`celld deploy` bundles the worker with esbuild. Install it globally (`npm install -g esbuild`) or point `CELLD_ESBUILD` at the binary. + +### `celld deploy` on the example prints `unknown command init for argo` at runtime + +That means the executor container/pod is running an `argo` CLI binary instead of argoexec — a sign the wrong image was tagged as the executor in your cluster setup, unrelated to celld itself. On a plain host (no cluster) this does not occur. + +## Next steps + +- Compare with the [Cloudflare Durable Objects documentation](https://developers.cloudflare.com/durable-objects/) — celld runs the same programming model on your own bucket. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token). +- Follow the [celld README](https://github.com/denoland/celld) for multi-node fleets: point a second node at the same bucket and let conditional writes elect the owner. diff --git a/content/de/developer/integration/cloud-native/images/rustfs-celld-state.png b/content/de/developer/integration/cloud-native/images/rustfs-celld-state.png new file mode 100644 index 000000000..692e5d532 Binary files /dev/null and b/content/de/developer/integration/cloud-native/images/rustfs-celld-state.png differ diff --git a/content/de/developer/integration/cloud-native/index.md b/content/de/developer/integration/cloud-native/index.md index 1b514aead..e703d7a73 100644 --- a/content/de/developer/integration/cloud-native/index.md +++ b/content/de/developer/integration/cloud-native/index.md @@ -9,5 +9,6 @@ Use **RustFS** as the object storage layer for cloud native platforms that suppo - [Cortex](./cortex.md) - [Flux](./flux.md) +- [celld](./celld.md) Keep platform state in a dedicated bucket, and use credentials scoped to the required bucket operations. diff --git a/content/de/developer/integration/cloud-native/meta.json b/content/de/developer/integration/cloud-native/meta.json index 30b847753..fbe75f4b9 100644 --- a/content/de/developer/integration/cloud-native/meta.json +++ b/content/de/developer/integration/cloud-native/meta.json @@ -2,6 +2,7 @@ "title": "Cloud Native", "pages": [ "cortex", - "flux" + "flux", + "celld" ] } diff --git a/content/de/developer/integration/index.md b/content/de/developer/integration/index.md index 86312f7b4..db3488bd5 100644 --- a/content/de/developer/integration/index.md +++ b/content/de/developer/integration/index.md @@ -13,7 +13,7 @@ Use this section to connect **RustFS** to infrastructure and application platfor - [Database](./database/index.md) covers ClickHouse, Databend, Doris, DuckDB, InfluxDB, LanceDB, Milvus, Trino, and Vitess. - [Big Data](./big-data/index.md) covers Airflow, AutoMQ, Delta Lake, DolphinScheduler, Flink, Hive, Hudi, Iceberg, Kafka, Paimon, PyIceberg, SeaTunnel, Spark, and Zeppelin. - [Storage](./storage/index.md) covers Alluxio, lakeFS, OpenDAL, SFTPGo, s3fs, and ZeroFS. -- [Cloud Native](./cloud-native/index.md) covers Cortex and Flux. +- [Cloud Native](./cloud-native/index.md) covers Cortex, Flux, and celld. - [Observability](./observability/index.md) covers telemetry systems including Fluentd, GreptimeDB, Loki, OpenObserve, OpenTelemetry, Tempo, Thanos, and VictoriaMetrics. - [Others](./others/index.md) covers the capo SDK, rclone, JuiceFS, Nextcloud, and tusd. - [Registry](./registry/index.md) covers Docker Registry and Harbor. diff --git a/content/en/developer/integration/cloud-native/celld.md b/content/en/developer/integration/cloud-native/celld.md new file mode 100644 index 000000000..37e2a757c --- /dev/null +++ b/content/en/developer/integration/cloud-native/celld.md @@ -0,0 +1,173 @@ +--- +title: "celld" +description: "Run denoland celld — self-hosted Durable Objects — with fleet state on RustFS." +--- + +This guide connects [celld](https://github.com/denoland/celld) — the self-hosted, distributed Durable Objects runtime from Denoland — to **RustFS** as its fleet bucket. You will run `celld diagnose` against a RustFS bucket (including the conditional-write probe), deploy a Counter worker, mutate Durable Object state over HTTP, restart celld, and confirm the state survives. The workflow was verified with celld v0.6.2 against `rustfs/rustfs-x86-musl:v2.3.1`. + +You need the `celld` binary, `esbuild` (for bundling), and Node.js to drive the deployed worker. + +## Architecture + +```mermaid +flowchart LR + Client["HTTP client"] -->|"Worker fetch :8080"| celld["celld node"] + celld -->|"LTX replicas + ownership + deploy"| RustFS["RustFS :9000"] +``` + +celld keeps only a local WAL. Every committed SQLite write is captured in LTX format and uploaded to the fleet bucket; a conditional bucket write (put-if-not-exists) assigns node ownership. The bucket is the source of truth — nodes can crash or restart without losing Durable Object state. + +## 1. Install celld + +```bash +curl -sLo /tmp/celld.gz \ + https://github.com/denoland/celld/releases/download/v0.6.2/celld-x86_64-unknown-linux-gnu.gz +gunzip /tmp/celld.gz && chmod +x /tmp/celld +mv /tmp/celld /usr/local/bin/celld +celld --version +``` + +```text +celld 0.6.2 +``` + +Install esbuild as well — `celld deploy` bundles workers with it: + +```bash +npm install -g esbuild +``` + +## 2. Diagnose the bucket + +`celld diagnose` probes the bucket including the conditional write that celld requires for ownership fencing: + +```bash +export S3_ENDPOINT=http://:9000 +export AWS_REGION=us-east-1 +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= + +rc mb rustfs/celld-demo +celld diagnose --bucket s3://celld-demo +``` + +```text +ok listen 127.0.0.1:18080: bind check; diagnose does not serve +ok bucket s3://celld-demo +ok bucket conditional write: create, reject-create, update, reject-stale +ok fleet: 0 node lease(s) enumerated +``` + +`ok bucket conditional write` is the important line — RustFS supports put-if-not-exists natively, so no fallback storage is needed. + +## 3. Start a node + +A non-loopback `--listen` requires an explicit `--internal-listen`: + +```bash +nohup celld --bucket s3://celld-demo \ + --listen 0.0.0.0:18080 \ + --internal-listen 0.0.0.0:9099 \ + --advertise 127.0.0.1:9099 > /opt/celld/celld.out 2>&1 & +``` + +```text +INFO celld: host runtime initialized event="host_runtime" worker_count=4 +INFO celld::fleet: no deployment yet; run `celld deploy` ... +``` + +## 4. Deploy the Counter worker + +Use the counter example from the celld repository — a Durable Object that increments `n` in SQLite storage on every request: + +```bash +curl -sLo /tmp/celld.zip https://codeload.github.com/denoland/celld/zip/refs/tags/v0.6.2 +unzip /tmp/celld.zip -d /opt/celld +cd /opt/celld/celld-0.6.2/examples/counter +celld deploy . --bucket s3://celld-demo +``` + +```text +Uploaded counter (0.02 sec) + s3://celld-demo/deploy/counter/bf5806589892a6cc +Current Version ID: bf5806589892a6cc +``` + +## 5. Mutate Durable Object state + +Each request routes to the `Counter` Durable Object, which reads `n` from storage, increments, and writes it back: + +```bash +curl -s "http://127.0.0.1:18080/?name=rustfs" +curl -s "http://127.0.0.1:18080/?name=rustfs" +curl -s "http://127.0.0.1:18080/?name=rustfs" +``` + +```json +{"n":1,"url":"http://127.0.0.1:18080/?name=rustfs"} +{"n":2,"url":"http://127.0.0.1:18080/?name=rustfs"} +{"n":3,"url":"http://127.0.0.1:18080/?name=rustfs"} +``` + +## 6. Restart celld and verify persistence + +Kill the node, start it again, and hit the same URL — the counter continues from where it stopped, because the state was in RustFS the whole time: + +```bash +pkill -f "celld --bucket" +nohup celld --bucket s3://celld-demo \ + --listen 0.0.0.0:18080 --internal-listen 0.0.0.0:9099 \ + --advertise 127.0.0.1:9099 > /opt/celld/celld2.out 2>&1 & +sleep 8 +curl -s "http://127.0.0.1:18080/?name=rustfs" +``` + +```json +{"n":4,"url":"http://127.0.0.1:18080/?name=rustfs"} +``` + +List the bucket — the fleet state is all there: + +```bash +rc ls rustfs/celld-demo/ -r | head -5 +``` + +```text +cells/Counter:f2806128.../ltx/e1/0000/0000000000000001-0000000000000001.ltx +cells/Counter:f2806128.../own.json +deploy/counter/bf5806589892a6cc/index.js +deploy/counter/current.json +``` + +![celld state stored in the RustFS Console](./images/rustfs-celld-state.png) + +## 7. Stop or reset + +```bash +pkill -f "celld --bucket" +rc rm rustfs/celld-demo/ --recursive --force +``` + +## Troubleshooting + +### `bind --listen 127.0.0.0.1:8080 ... Address already in use` + +Port 8080 is commonly taken (a local GitLab registry or dev server). Move the Worker listener: `--listen 0.0.0.0:18080`. + +### `a non-loopback --listen requires an explicit --internal-listen` + +Binding a public interface requires a separate peer listener: add `--internal-listen 0.0.0.0:9099 --advertise 127.0.0.1:9099`. + +### `esbuild not found` + +`celld deploy` bundles the worker with esbuild. Install it globally (`npm install -g esbuild`) or point `CELLD_ESBUILD` at the binary. + +### `celld deploy` on the example prints `unknown command init for argo` at runtime + +That means the executor container/pod is running an `argo` CLI binary instead of argoexec — a sign the wrong image was tagged as the executor in your cluster setup, unrelated to celld itself. On a plain host (no cluster) this does not occur. + +## Next steps + +- Compare with the [Cloudflare Durable Objects documentation](https://developers.cloudflare.com/durable-objects/) — celld runs the same programming model on your own bucket. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token). +- Follow the [celld README](https://github.com/denoland/celld) for multi-node fleets: point a second node at the same bucket and let conditional writes elect the owner. diff --git a/content/en/developer/integration/cloud-native/images/rustfs-celld-state.png b/content/en/developer/integration/cloud-native/images/rustfs-celld-state.png new file mode 100644 index 000000000..692e5d532 Binary files /dev/null and b/content/en/developer/integration/cloud-native/images/rustfs-celld-state.png differ diff --git a/content/en/developer/integration/cloud-native/index.md b/content/en/developer/integration/cloud-native/index.md index 1b514aead..e703d7a73 100644 --- a/content/en/developer/integration/cloud-native/index.md +++ b/content/en/developer/integration/cloud-native/index.md @@ -9,5 +9,6 @@ Use **RustFS** as the object storage layer for cloud native platforms that suppo - [Cortex](./cortex.md) - [Flux](./flux.md) +- [celld](./celld.md) Keep platform state in a dedicated bucket, and use credentials scoped to the required bucket operations. diff --git a/content/en/developer/integration/cloud-native/meta.json b/content/en/developer/integration/cloud-native/meta.json index 30b847753..fbe75f4b9 100644 --- a/content/en/developer/integration/cloud-native/meta.json +++ b/content/en/developer/integration/cloud-native/meta.json @@ -2,6 +2,7 @@ "title": "Cloud Native", "pages": [ "cortex", - "flux" + "flux", + "celld" ] } diff --git a/content/en/developer/integration/index.md b/content/en/developer/integration/index.md index 3e09c1698..f4bda60f1 100644 --- a/content/en/developer/integration/index.md +++ b/content/en/developer/integration/index.md @@ -13,7 +13,7 @@ Use this section to connect **RustFS** to infrastructure and application platfor - [Database](./database/index.md) covers ClickHouse, Databend, Doris, DuckDB, InfluxDB, LanceDB, Milvus, Trino, and Vitess. - [Big Data](./big-data/index.md) covers Airflow, AutoMQ, Delta Lake, DolphinScheduler, Flink, Hive, Hudi, Iceberg, Kafka, Paimon, PyIceberg, SeaTunnel, Spark, and Zeppelin. - [Storage](./storage/index.md) covers Alluxio, lakeFS, OpenDAL, SFTPGo, s3fs, and ZeroFS. -- [Cloud Native](./cloud-native/index.md) covers Cortex and Flux. +- [Cloud Native](./cloud-native/index.md) covers Cortex, Flux, and celld. - [Observability](./observability/index.md) covers telemetry systems including Fluentd, GreptimeDB, Loki, OpenObserve, OpenTelemetry, Tempo, Thanos, and VictoriaMetrics. - [Others](./others/index.md) covers the capo SDK, rclone, JuiceFS, Nextcloud, and tusd. - [Registry](./registry/index.md) covers Docker Registry and Harbor. diff --git a/content/fr/developer/integration/cloud-native/celld.md b/content/fr/developer/integration/cloud-native/celld.md new file mode 100644 index 000000000..37e2a757c --- /dev/null +++ b/content/fr/developer/integration/cloud-native/celld.md @@ -0,0 +1,173 @@ +--- +title: "celld" +description: "Run denoland celld — self-hosted Durable Objects — with fleet state on RustFS." +--- + +This guide connects [celld](https://github.com/denoland/celld) — the self-hosted, distributed Durable Objects runtime from Denoland — to **RustFS** as its fleet bucket. You will run `celld diagnose` against a RustFS bucket (including the conditional-write probe), deploy a Counter worker, mutate Durable Object state over HTTP, restart celld, and confirm the state survives. The workflow was verified with celld v0.6.2 against `rustfs/rustfs-x86-musl:v2.3.1`. + +You need the `celld` binary, `esbuild` (for bundling), and Node.js to drive the deployed worker. + +## Architecture + +```mermaid +flowchart LR + Client["HTTP client"] -->|"Worker fetch :8080"| celld["celld node"] + celld -->|"LTX replicas + ownership + deploy"| RustFS["RustFS :9000"] +``` + +celld keeps only a local WAL. Every committed SQLite write is captured in LTX format and uploaded to the fleet bucket; a conditional bucket write (put-if-not-exists) assigns node ownership. The bucket is the source of truth — nodes can crash or restart without losing Durable Object state. + +## 1. Install celld + +```bash +curl -sLo /tmp/celld.gz \ + https://github.com/denoland/celld/releases/download/v0.6.2/celld-x86_64-unknown-linux-gnu.gz +gunzip /tmp/celld.gz && chmod +x /tmp/celld +mv /tmp/celld /usr/local/bin/celld +celld --version +``` + +```text +celld 0.6.2 +``` + +Install esbuild as well — `celld deploy` bundles workers with it: + +```bash +npm install -g esbuild +``` + +## 2. Diagnose the bucket + +`celld diagnose` probes the bucket including the conditional write that celld requires for ownership fencing: + +```bash +export S3_ENDPOINT=http://:9000 +export AWS_REGION=us-east-1 +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= + +rc mb rustfs/celld-demo +celld diagnose --bucket s3://celld-demo +``` + +```text +ok listen 127.0.0.1:18080: bind check; diagnose does not serve +ok bucket s3://celld-demo +ok bucket conditional write: create, reject-create, update, reject-stale +ok fleet: 0 node lease(s) enumerated +``` + +`ok bucket conditional write` is the important line — RustFS supports put-if-not-exists natively, so no fallback storage is needed. + +## 3. Start a node + +A non-loopback `--listen` requires an explicit `--internal-listen`: + +```bash +nohup celld --bucket s3://celld-demo \ + --listen 0.0.0.0:18080 \ + --internal-listen 0.0.0.0:9099 \ + --advertise 127.0.0.1:9099 > /opt/celld/celld.out 2>&1 & +``` + +```text +INFO celld: host runtime initialized event="host_runtime" worker_count=4 +INFO celld::fleet: no deployment yet; run `celld deploy` ... +``` + +## 4. Deploy the Counter worker + +Use the counter example from the celld repository — a Durable Object that increments `n` in SQLite storage on every request: + +```bash +curl -sLo /tmp/celld.zip https://codeload.github.com/denoland/celld/zip/refs/tags/v0.6.2 +unzip /tmp/celld.zip -d /opt/celld +cd /opt/celld/celld-0.6.2/examples/counter +celld deploy . --bucket s3://celld-demo +``` + +```text +Uploaded counter (0.02 sec) + s3://celld-demo/deploy/counter/bf5806589892a6cc +Current Version ID: bf5806589892a6cc +``` + +## 5. Mutate Durable Object state + +Each request routes to the `Counter` Durable Object, which reads `n` from storage, increments, and writes it back: + +```bash +curl -s "http://127.0.0.1:18080/?name=rustfs" +curl -s "http://127.0.0.1:18080/?name=rustfs" +curl -s "http://127.0.0.1:18080/?name=rustfs" +``` + +```json +{"n":1,"url":"http://127.0.0.1:18080/?name=rustfs"} +{"n":2,"url":"http://127.0.0.1:18080/?name=rustfs"} +{"n":3,"url":"http://127.0.0.1:18080/?name=rustfs"} +``` + +## 6. Restart celld and verify persistence + +Kill the node, start it again, and hit the same URL — the counter continues from where it stopped, because the state was in RustFS the whole time: + +```bash +pkill -f "celld --bucket" +nohup celld --bucket s3://celld-demo \ + --listen 0.0.0.0:18080 --internal-listen 0.0.0.0:9099 \ + --advertise 127.0.0.1:9099 > /opt/celld/celld2.out 2>&1 & +sleep 8 +curl -s "http://127.0.0.1:18080/?name=rustfs" +``` + +```json +{"n":4,"url":"http://127.0.0.1:18080/?name=rustfs"} +``` + +List the bucket — the fleet state is all there: + +```bash +rc ls rustfs/celld-demo/ -r | head -5 +``` + +```text +cells/Counter:f2806128.../ltx/e1/0000/0000000000000001-0000000000000001.ltx +cells/Counter:f2806128.../own.json +deploy/counter/bf5806589892a6cc/index.js +deploy/counter/current.json +``` + +![celld state stored in the RustFS Console](./images/rustfs-celld-state.png) + +## 7. Stop or reset + +```bash +pkill -f "celld --bucket" +rc rm rustfs/celld-demo/ --recursive --force +``` + +## Troubleshooting + +### `bind --listen 127.0.0.0.1:8080 ... Address already in use` + +Port 8080 is commonly taken (a local GitLab registry or dev server). Move the Worker listener: `--listen 0.0.0.0:18080`. + +### `a non-loopback --listen requires an explicit --internal-listen` + +Binding a public interface requires a separate peer listener: add `--internal-listen 0.0.0.0:9099 --advertise 127.0.0.1:9099`. + +### `esbuild not found` + +`celld deploy` bundles the worker with esbuild. Install it globally (`npm install -g esbuild`) or point `CELLD_ESBUILD` at the binary. + +### `celld deploy` on the example prints `unknown command init for argo` at runtime + +That means the executor container/pod is running an `argo` CLI binary instead of argoexec — a sign the wrong image was tagged as the executor in your cluster setup, unrelated to celld itself. On a plain host (no cluster) this does not occur. + +## Next steps + +- Compare with the [Cloudflare Durable Objects documentation](https://developers.cloudflare.com/durable-objects/) — celld runs the same programming model on your own bucket. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token). +- Follow the [celld README](https://github.com/denoland/celld) for multi-node fleets: point a second node at the same bucket and let conditional writes elect the owner. diff --git a/content/fr/developer/integration/cloud-native/images/rustfs-celld-state.png b/content/fr/developer/integration/cloud-native/images/rustfs-celld-state.png new file mode 100644 index 000000000..692e5d532 Binary files /dev/null and b/content/fr/developer/integration/cloud-native/images/rustfs-celld-state.png differ diff --git a/content/fr/developer/integration/cloud-native/index.md b/content/fr/developer/integration/cloud-native/index.md index 1b514aead..e703d7a73 100644 --- a/content/fr/developer/integration/cloud-native/index.md +++ b/content/fr/developer/integration/cloud-native/index.md @@ -9,5 +9,6 @@ Use **RustFS** as the object storage layer for cloud native platforms that suppo - [Cortex](./cortex.md) - [Flux](./flux.md) +- [celld](./celld.md) Keep platform state in a dedicated bucket, and use credentials scoped to the required bucket operations. diff --git a/content/fr/developer/integration/cloud-native/meta.json b/content/fr/developer/integration/cloud-native/meta.json index 30b847753..fbe75f4b9 100644 --- a/content/fr/developer/integration/cloud-native/meta.json +++ b/content/fr/developer/integration/cloud-native/meta.json @@ -2,6 +2,7 @@ "title": "Cloud Native", "pages": [ "cortex", - "flux" + "flux", + "celld" ] } diff --git a/content/fr/developer/integration/index.md b/content/fr/developer/integration/index.md index bb895625d..303dfb7fb 100644 --- a/content/fr/developer/integration/index.md +++ b/content/fr/developer/integration/index.md @@ -13,7 +13,7 @@ Utilisez cette section pour connecter **RustFS** à des plateformes d'infrastruc - [Database](./database/index.md) covers ClickHouse, Databend, Doris, DuckDB, InfluxDB, LanceDB, Milvus, Trino, and Vitess. - [Big Data](./big-data/index.md) covers Airflow, AutoMQ, Delta Lake, DolphinScheduler, Flink, Hive, Hudi, Iceberg, Kafka, Paimon, PyIceberg, SeaTunnel, Spark, and Zeppelin. - [Storage](./storage/index.md) covers Alluxio, lakeFS, OpenDAL, SFTPGo, s3fs, and ZeroFS. -- [Cloud Native](./cloud-native/index.md) couvre Cortex et Flux. +- [Cloud Native](./cloud-native/index.md) couvre Cortex, Flux et celld. - [Observabilité](./observability/index.md) couvre les systèmes de télémétrie incluant Fluentd, GreptimeDB, Loki, OpenObserve, OpenTelemetry, Tempo, Thanos et VictoriaMetrics. - [Autres](./others/index.md) couvre le SDK capo, rclone, JuiceFS, Nextcloud et tusd. - [Registre](./registry/index.md) couvre Docker Registry et Harbor. diff --git a/content/ja/developer/integration/cloud-native/celld.md b/content/ja/developer/integration/cloud-native/celld.md new file mode 100644 index 000000000..37e2a757c --- /dev/null +++ b/content/ja/developer/integration/cloud-native/celld.md @@ -0,0 +1,173 @@ +--- +title: "celld" +description: "Run denoland celld — self-hosted Durable Objects — with fleet state on RustFS." +--- + +This guide connects [celld](https://github.com/denoland/celld) — the self-hosted, distributed Durable Objects runtime from Denoland — to **RustFS** as its fleet bucket. You will run `celld diagnose` against a RustFS bucket (including the conditional-write probe), deploy a Counter worker, mutate Durable Object state over HTTP, restart celld, and confirm the state survives. The workflow was verified with celld v0.6.2 against `rustfs/rustfs-x86-musl:v2.3.1`. + +You need the `celld` binary, `esbuild` (for bundling), and Node.js to drive the deployed worker. + +## Architecture + +```mermaid +flowchart LR + Client["HTTP client"] -->|"Worker fetch :8080"| celld["celld node"] + celld -->|"LTX replicas + ownership + deploy"| RustFS["RustFS :9000"] +``` + +celld keeps only a local WAL. Every committed SQLite write is captured in LTX format and uploaded to the fleet bucket; a conditional bucket write (put-if-not-exists) assigns node ownership. The bucket is the source of truth — nodes can crash or restart without losing Durable Object state. + +## 1. Install celld + +```bash +curl -sLo /tmp/celld.gz \ + https://github.com/denoland/celld/releases/download/v0.6.2/celld-x86_64-unknown-linux-gnu.gz +gunzip /tmp/celld.gz && chmod +x /tmp/celld +mv /tmp/celld /usr/local/bin/celld +celld --version +``` + +```text +celld 0.6.2 +``` + +Install esbuild as well — `celld deploy` bundles workers with it: + +```bash +npm install -g esbuild +``` + +## 2. Diagnose the bucket + +`celld diagnose` probes the bucket including the conditional write that celld requires for ownership fencing: + +```bash +export S3_ENDPOINT=http://:9000 +export AWS_REGION=us-east-1 +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= + +rc mb rustfs/celld-demo +celld diagnose --bucket s3://celld-demo +``` + +```text +ok listen 127.0.0.1:18080: bind check; diagnose does not serve +ok bucket s3://celld-demo +ok bucket conditional write: create, reject-create, update, reject-stale +ok fleet: 0 node lease(s) enumerated +``` + +`ok bucket conditional write` is the important line — RustFS supports put-if-not-exists natively, so no fallback storage is needed. + +## 3. Start a node + +A non-loopback `--listen` requires an explicit `--internal-listen`: + +```bash +nohup celld --bucket s3://celld-demo \ + --listen 0.0.0.0:18080 \ + --internal-listen 0.0.0.0:9099 \ + --advertise 127.0.0.1:9099 > /opt/celld/celld.out 2>&1 & +``` + +```text +INFO celld: host runtime initialized event="host_runtime" worker_count=4 +INFO celld::fleet: no deployment yet; run `celld deploy` ... +``` + +## 4. Deploy the Counter worker + +Use the counter example from the celld repository — a Durable Object that increments `n` in SQLite storage on every request: + +```bash +curl -sLo /tmp/celld.zip https://codeload.github.com/denoland/celld/zip/refs/tags/v0.6.2 +unzip /tmp/celld.zip -d /opt/celld +cd /opt/celld/celld-0.6.2/examples/counter +celld deploy . --bucket s3://celld-demo +``` + +```text +Uploaded counter (0.02 sec) + s3://celld-demo/deploy/counter/bf5806589892a6cc +Current Version ID: bf5806589892a6cc +``` + +## 5. Mutate Durable Object state + +Each request routes to the `Counter` Durable Object, which reads `n` from storage, increments, and writes it back: + +```bash +curl -s "http://127.0.0.1:18080/?name=rustfs" +curl -s "http://127.0.0.1:18080/?name=rustfs" +curl -s "http://127.0.0.1:18080/?name=rustfs" +``` + +```json +{"n":1,"url":"http://127.0.0.1:18080/?name=rustfs"} +{"n":2,"url":"http://127.0.0.1:18080/?name=rustfs"} +{"n":3,"url":"http://127.0.0.1:18080/?name=rustfs"} +``` + +## 6. Restart celld and verify persistence + +Kill the node, start it again, and hit the same URL — the counter continues from where it stopped, because the state was in RustFS the whole time: + +```bash +pkill -f "celld --bucket" +nohup celld --bucket s3://celld-demo \ + --listen 0.0.0.0:18080 --internal-listen 0.0.0.0:9099 \ + --advertise 127.0.0.1:9099 > /opt/celld/celld2.out 2>&1 & +sleep 8 +curl -s "http://127.0.0.1:18080/?name=rustfs" +``` + +```json +{"n":4,"url":"http://127.0.0.1:18080/?name=rustfs"} +``` + +List the bucket — the fleet state is all there: + +```bash +rc ls rustfs/celld-demo/ -r | head -5 +``` + +```text +cells/Counter:f2806128.../ltx/e1/0000/0000000000000001-0000000000000001.ltx +cells/Counter:f2806128.../own.json +deploy/counter/bf5806589892a6cc/index.js +deploy/counter/current.json +``` + +![celld state stored in the RustFS Console](./images/rustfs-celld-state.png) + +## 7. Stop or reset + +```bash +pkill -f "celld --bucket" +rc rm rustfs/celld-demo/ --recursive --force +``` + +## Troubleshooting + +### `bind --listen 127.0.0.0.1:8080 ... Address already in use` + +Port 8080 is commonly taken (a local GitLab registry or dev server). Move the Worker listener: `--listen 0.0.0.0:18080`. + +### `a non-loopback --listen requires an explicit --internal-listen` + +Binding a public interface requires a separate peer listener: add `--internal-listen 0.0.0.0:9099 --advertise 127.0.0.1:9099`. + +### `esbuild not found` + +`celld deploy` bundles the worker with esbuild. Install it globally (`npm install -g esbuild`) or point `CELLD_ESBUILD` at the binary. + +### `celld deploy` on the example prints `unknown command init for argo` at runtime + +That means the executor container/pod is running an `argo` CLI binary instead of argoexec — a sign the wrong image was tagged as the executor in your cluster setup, unrelated to celld itself. On a plain host (no cluster) this does not occur. + +## Next steps + +- Compare with the [Cloudflare Durable Objects documentation](https://developers.cloudflare.com/durable-objects/) — celld runs the same programming model on your own bucket. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token). +- Follow the [celld README](https://github.com/denoland/celld) for multi-node fleets: point a second node at the same bucket and let conditional writes elect the owner. diff --git a/content/ja/developer/integration/cloud-native/images/rustfs-celld-state.png b/content/ja/developer/integration/cloud-native/images/rustfs-celld-state.png new file mode 100644 index 000000000..692e5d532 Binary files /dev/null and b/content/ja/developer/integration/cloud-native/images/rustfs-celld-state.png differ diff --git a/content/ja/developer/integration/cloud-native/index.md b/content/ja/developer/integration/cloud-native/index.md index 4c079efe3..159f953c4 100644 --- a/content/ja/developer/integration/cloud-native/index.md +++ b/content/ja/developer/integration/cloud-native/index.md @@ -9,5 +9,6 @@ Use **RustFS** as the object storage layer for cloud native platforms that suppo - [Cortex](./cortex.md) - [Flux](./flux.md) +- [celld](./celld.md) Keep platform state in a dedicated bucket, and use credentials scoped to the required bucket operations. diff --git a/content/ja/developer/integration/cloud-native/meta.json b/content/ja/developer/integration/cloud-native/meta.json index 92c564ac4..6c0c94066 100644 --- a/content/ja/developer/integration/cloud-native/meta.json +++ b/content/ja/developer/integration/cloud-native/meta.json @@ -2,6 +2,7 @@ "title": "クラウドネイティブ", "pages": [ "cortex", - "flux" + "flux", + "celld" ] } diff --git a/content/ja/developer/integration/index.md b/content/ja/developer/integration/index.md index 41854b38a..2ecea1587 100644 --- a/content/ja/developer/integration/index.md +++ b/content/ja/developer/integration/index.md @@ -13,7 +13,7 @@ description: "RustFS をリバースプロキシ、バックアップツール - [Database](./database/index.md) covers ClickHouse, Databend, Doris, DuckDB, InfluxDB, LanceDB, Milvus, Trino, and Vitess. - [Big Data](./big-data/index.md) covers Airflow, AutoMQ, Delta Lake, DolphinScheduler, Flink, Hive, Hudi, Iceberg, Kafka, Paimon, PyIceberg, SeaTunnel, Spark, and Zeppelin. - [Storage](./storage/index.md) covers Alluxio, lakeFS, OpenDAL, SFTPGo, s3fs, and ZeroFS. -- [クラウドネイティブ](./cloud-native/index.md) は Cortex、Flux を扱います。 +- [クラウドネイティブ](./cloud-native/index.md) は Cortex、Flux、celld を扱います。 - [オブザーバビリティ](./observability/index.md) は Fluentd、GreptimeDB、Loki、OpenObserve、OpenTelemetry、Tempo、Thanos、VictoriaMetrics などのテレメトリシステムを扱います。 - [その他](./others/index.md) は capo SDK、rclone、JuiceFS、Nextcloud、tusd などのツールを扱います。 - [コンテナレジストリ](./registry/index.md) は Docker Registry、Harbor を扱います。 diff --git a/content/zh/developer/integration/cloud-native/celld.md b/content/zh/developer/integration/cloud-native/celld.md new file mode 100644 index 000000000..966e10aa3 --- /dev/null +++ b/content/zh/developer/integration/cloud-native/celld.md @@ -0,0 +1,173 @@ +--- +title: "celld" +description: "以 RustFS 作为 denoland celld——自托管 Durable Objects——的舰队状态桶。" +--- + +本指南将 Denoland 出品的自托管分布式 Durable Objects 运行时 [celld](https://github.com/denoland/celld) 连接到 **RustFS** 作为其舰队桶(fleet bucket)。你将先对 RustFS 桶跑 `celld diagnose`(含条件写探测),部署一个 Counter Worker,经 HTTP 修改 Durable Object 状态,重启 celld 后确认状态仍在。整个流程使用 celld v0.6.2 对 `rustfs/rustfs-x86-musl:v2.3.1` 验证通过。 + +你需要 `celld` 二进制、`esbuild`(打包用)以及驱动已部署 Worker 的 Node.js。 + +## 架构 + +```mermaid +flowchart LR + Client["HTTP client"] -->|"Worker fetch :8080"| celld["celld node"] + celld -->|"LTX replicas + ownership + deploy"| RustFS["RustFS :9000"] +``` + +celld 本地只留 WAL。每笔已提交的 SQLite 写入都会捕获为 LTX 格式并上传到舰队桶;桶上的条件写(put-if-not-exists)负责节点 ownership 分配。桶即事实来源——节点崩溃或重启都不会丢 Durable Object 状态。 + +## 1. 安装 celld + +```bash +curl -sLo /tmp/celld.gz \ + https://github.com/denoland/celld/releases/download/v0.6.2/celld-x86_64-unknown-linux-gnu.gz +gunzip /tmp/celld.gz && chmod +x /tmp/celld +mv /tmp/celld /usr/local/bin/celld +celld --version +``` + +```text +celld 0.6.2 +``` + +同时安装 esbuild——`celld deploy` 用它打包 Worker: + +```bash +npm install -g esbuild +``` + +## 2. 诊断桶 + +`celld diagnose` 会对桶做探测,包括 celld 分配 ownership 所需的条件写: + +```bash +export S3_ENDPOINT=http://:9000 +export AWS_REGION=us-east-1 +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= + +rc mb rustfs/celld-demo +celld diagnose --bucket s3://celld-demo +``` + +```text +ok listen 127.0.0.1:18080: bind check; diagnose does not serve +ok bucket s3://celld-demo +ok bucket conditional write: create, reject-create, update, reject-stale +ok fleet: 0 node lease(s) enumerated +``` + +`ok bucket conditional write` 是关键一行——RustFS 原生支持 put-if-not-exists,无需任何兜底存储。 + +## 3. 启动节点 + +非环回 `--listen` 必须显式指定 `--internal-listen`: + +```bash +nohup celld --bucket s3://celld-demo \ + --listen 0.0.0.0:18080 \ + --internal-listen 0.0.0.0:9099 \ + --advertise 127.0.0.1:9099 > /opt/celld/celld.out 2>&1 & +``` + +```text +INFO celld: host runtime initialized event="host_runtime" worker_count=4 +INFO celld::fleet: no deployment yet; run `celld deploy` ... +``` + +## 4. 部署 Counter Worker + +使用 celld 仓库里的 counter 示例——一个 Durable Object,每次请求都从 SQLite 存储读 `n`、加一并写回: + +```bash +curl -sLo /tmp/celld.zip https://codeload.github.com/denoland/celld/zip/refs/tags/v0.6.2 +unzip /tmp/celld.zip -d /opt/celld +cd /opt/celld/celld-0.6.2/examples/counter +celld deploy . --bucket s3://celld-demo +``` + +```text +Uploaded counter (0.02 sec) + s3://celld-demo/deploy/counter/bf5806589892a6cc +Current Version ID: bf5806589892a6cc +``` + +## 5. 修改 Durable Object 状态 + +每个请求都会路由到 `Counter` Durable Object:从存储读 `n`、自增、写回: + +```bash +curl -s "http://127.0.0.1:18080/?name=rustfs" +curl -s "http://127.0.0.1:18080/?name=rustfs" +curl -s "http://127.0.0.1:18080/?name=rustfs" +``` + +```json +{"n":1,"url":"http://127.0.0.1:18080/?name=rustfs"} +{"n":2,"url":"http://127.0.0.1:18080/?name=rustfs"} +{"n":3,"url":"http://127.0.0.1:18080/?name=rustfs"} +``` + +## 6. 重启 celld 验证持久化 + +杀掉节点、重新启动、再打同一 URL——计数器从上次停的地方继续,因为状态一直都在 RustFS 里: + +```bash +pkill -f "celld --bucket" +nohup celld --bucket s3://celld-demo \ + --listen 0.0.0.0:18080 --internal-listen 0.0.0.0:9099 \ + --advertise 127.0.0.1:9099 > /opt/celld/celld2.out 2>&1 & +sleep 8 +curl -s "http://127.0.0.1:18080/?name=rustfs" +``` + +```json +{"n":4,"url":"http://127.0.0.1:18080/?name=rustfs"} +``` + +列举桶——舰队状态全在里面: + +```bash +rc ls rustfs/celld-demo/ -r | head -5 +``` + +```text +cells/Counter:f2806128.../ltx/e1/0000/0000000000000001-0000000000000001.ltx +cells/Counter:f2806128.../own.json +deploy/counter/bf5806589892a6cc/index.js +deploy/counter/current.json +``` + +![存储在 RustFS 控制台中的 celld 状态](./images/rustfs-celld-state.png) + +## 7. 停止或重置 + +```bash +pkill -f "celld --bucket" +rc rm rustfs/celld-demo/ --recursive --force +``` + +## 故障排查 + +### `bind --listen 127.0.0.0.1:8080 ... Address already in use` + +8080 端口很常见被占(本地 GitLab registry 或 dev server)。把 Worker 监听挪走:`--listen 0.0.0.0:18080`。 + +### `a non-loopback --listen requires an explicit --internal-listen` + +绑定公网接口时需要单独的对等监听:加 `--internal-listen 0.0.0.0:9099 --advertise 127.0.0.1:9099`。 + +### `esbuild not found` + +`celld deploy` 用 esbuild 打包 Worker。全局安装(`npm install -g esbuild`)或把 `CELLD_ESBUILD` 指向二进制路径。 + +### `celld deploy` 部署的示例运行时打印 `unknown command init for argo` + +这说明执行器容器/ Pod 里跑的是 `argo` CLI 而非 argoexec——通常是集群环境中把错误镜像打成了执行器镜像,与 celld 本身无关。纯主机(无集群)部署不会出现。 + +## 下一步 + +- 与 [Cloudflare Durable Objects 文档](https://developers.cloudflare.com/durable-objects/)对照——celld 在你自己的桶上运行同样的编程模型。 +- 使用[访问密钥管理](/security-compliance/iam/access-token)创建专用的生产凭证。 +- 按照 [celld README](https://github.com/denoland/celld) 搭建多节点舰队:让第二个节点指向同一桶,由条件写选出 owner。 diff --git a/content/zh/developer/integration/cloud-native/images/rustfs-celld-state.png b/content/zh/developer/integration/cloud-native/images/rustfs-celld-state.png new file mode 100644 index 000000000..9028bdc07 Binary files /dev/null and b/content/zh/developer/integration/cloud-native/images/rustfs-celld-state.png differ diff --git a/content/zh/developer/integration/cloud-native/index.md b/content/zh/developer/integration/cloud-native/index.md index 3cb7947bc..75cea1f96 100644 --- a/content/zh/developer/integration/cloud-native/index.md +++ b/content/zh/developer/integration/cloud-native/index.md @@ -9,5 +9,6 @@ description: "通过 S3 兼容的对象存储接口,将云原生平台连接 - [Cortex](./cortex.md) - [Flux](./flux.md) +- [celld](./celld.md) 请将平台状态保存在专用的存储桶中,并为凭证仅授予所需桶操作的权限。 diff --git a/content/zh/developer/integration/cloud-native/meta.json b/content/zh/developer/integration/cloud-native/meta.json index 4fd911631..c98a1487c 100644 --- a/content/zh/developer/integration/cloud-native/meta.json +++ b/content/zh/developer/integration/cloud-native/meta.json @@ -2,6 +2,7 @@ "title": "云原生", "pages": [ "cortex", - "flux" + "flux", + "celld" ] } diff --git a/content/zh/developer/integration/index.md b/content/zh/developer/integration/index.md index 8b6dd5311..82700ac5e 100644 --- a/content/zh/developer/integration/index.md +++ b/content/zh/developer/integration/index.md @@ -13,7 +13,7 @@ description: "将 RustFS 与反向代理、备份工具、数据库、大数据 - [数据库](./database/index.md)涵盖 ClickHouse、Databend、Doris、DuckDB、InfluxDB、LanceDB、Milvus、Trino 和 Vitess 等数据库。 - [大数据](./big-data/index.md)涵盖 Airflow、AutoMQ、Delta Lake、DolphinScheduler、Flink、Hive、Hudi、Iceberg、Kafka、Paimon、PyIceberg、SeaTunnel、Spark 和 Zeppelin 等大数据系统。 - [存储](./storage/index.md)涵盖 Alluxio、lakeFS、OpenDAL、SFTPGo、s3fs 和 ZeroFS 等存储系统。 -- [云原生](./cloud-native/index.md)涵盖 Cortex 与 Flux。 +- [云原生](./cloud-native/index.md)涵盖 Cortex、Flux 和 celld。 - [可观测性](./observability/index.md)涵盖 Fluentd、GreptimeDB、Loki、OpenObserve、OpenTelemetry、Tempo、Thanos 和 VictoriaMetrics 等遥测系统。 - [其他](./others/index.md)涵盖 capo SDK、rclone、JuiceFS、Nextcloud 和 tusd 等工具。 - [镜像仓库](./registry/index.md)涵盖 Docker Registry 和 Harbor。