-
Notifications
You must be signed in to change notification settings - Fork 1
docs(dev): container execution backend + ricochet-server in a container #237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9d99cba
863a716
03c554a
abd7355
b5fdd3c
fc3486b
47d75a2
c8ce980
ace2adc
75831f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,6 +4,196 @@ title: Execution Backend | |||||||
|
|
||||||||
| import { Aside } from "@astrojs/starlight/components"; | ||||||||
|
|
||||||||
| <Aside type="note" title="Coming Soon"> | ||||||||
| This documentation is under construction. | ||||||||
| ricochet runs user content — apps and tasks — in an **execution backend**. | ||||||||
| Three backends are supported: | ||||||||
|
|
||||||||
| | Backend | `engine` value | Use when | | ||||||||
| | ------------ | -------------- | ----------------------------------------------------------------------------------------------------------------- | | ||||||||
| | Host | (omitted) | Single host, no container daemon. Content runs as isolated processes on the host via [libcontainer][libcontainer]. | | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| | Container | `container` | Single host with a Docker- or Podman-compatible daemon. Content runs as full containers spawned through the daemon. | | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| | Kubernetes | `k8s` | Multi-node cluster, content scheduled as Pods. | | ||||||||
|
|
||||||||
| [libcontainer]: https://docs.rs/libcontainer/ | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
|
||||||||
| If `[backend]` is omitted, ricochet uses the **host** backend. | ||||||||
| Content runs as isolated processes managed in-process by `libcontainer` (an OCI-runtime implementation, no daemon required), using the language interpreters installed on the host. | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| This page documents the `container` and `k8s` backends; the host backend has no configuration knobs of its own. | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
semi-colons—the new AI tic. |
||||||||
|
|
||||||||
| <Aside type="tip" title="Default exec-env images"> | ||||||||
| The container and Kubernetes backends pull an OCI image per content item. | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I don't think this is necessary |
||||||||
| ricochet publishes a continuously-updated set of production-ready images at [ricochet-rs/exec-envs](https://github.com/ricochet-rs/exec-envs) — R on Alpine, Ubuntu, and AlmaLinux; Python on Ubuntu and AlmaLinux — rebuilt weekly and suitable as defaults for `default_image` and per-content `exec_env` references. | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| Bring your own images when you need additional system libraries or a different language/version combination. | ||||||||
| </Aside> | ||||||||
|
|
||||||||
| ## Host vs container on a single host | ||||||||
|
|
||||||||
| Both backends run content on the same machine as ricochet-server. | ||||||||
| The difference is **how** content is isolated and **where** the runtime environment comes from. | ||||||||
|
|
||||||||
| | Dimension | Host backend (`libcontainer`) | Container backend (`container`) | | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| | ------------------------ | ---------------------------------------------------------------------- | ---------------------------------------------------------------- | | ||||||||
| | Isolation mechanism | `libcontainer` namespaces/cgroups, set up in-process by ricochet-server | OCI containers, spawned by the host's Docker/Podman daemon | | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| | Daemon required | No | Yes (Docker, Podman, or compatible) | | ||||||||
| | Runtime / interpreters | Whatever is installed on the host (system R, Python, Julia) | Whatever the chosen image provides; arbitrary per content item | | ||||||||
| | Per-content exec env | Single shared host environment | Per-item via `exec_env` → image, like the Kubernetes backend | | ||||||||
|
Comment on lines
+37
to
+38
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These seem redundant. The "per-content exec-env" row can probably be deleted?
Comment on lines
+37
to
+38
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| | Observability | Processes are not visible to `docker ps` / container metrics tooling | Each app/task is a real container — visible to standard tooling | | ||||||||
| | Setup cost | None — works on a fresh ricochet-server install | Install and run a container daemon, grant socket access | | ||||||||
|
|
||||||||
| Pick the **host** backend for simple single-tenant deployments where the host already has the interpreters you need and you do not want to operate a container daemon. | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
|
||||||||
| Pick the **container** backend when you need different runtime environments per content item (e.g. R 4.3 vs R 4.5, conflicting system libraries), or when you want each running app and task to be a visible, individually observable container. | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Different runtimes are already supported. |
||||||||
|
|
||||||||
| ## Container backend | ||||||||
|
|
||||||||
| Set `engine = "container"` to spawn content as containers via a local Docker- or Podman-compatible daemon. | ||||||||
|
|
||||||||
| ```toml title="ricochet-config.toml" | ||||||||
| [backend] | ||||||||
| engine = "container" | ||||||||
| default_image = "r-default" | ||||||||
| exec_env_config_path = "ricochet-exec-env.toml" | ||||||||
| content_path = "/var/lib/ricochet/data/content" | ||||||||
| cache_path = "/var/lib/ricochet/data/.cache" | ||||||||
| ``` | ||||||||
|
|
||||||||
| | Field | Required | Description | | ||||||||
| | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ||||||||
| | `engine` | yes | Must be `"container"`. | | ||||||||
| | `default_image` | no | Image category name from `exec_env_config_path`. Used when a content item does not specify its own exec environment. | | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This "category" language needs to be removed 😭
Suggested change
|
||||||||
| | `exec_env_config_path` | yes | Path to the exec-env config TOML. Absolute, or relative to `RICOCHET_HOME`. | | ||||||||
| | `content_path` | yes | Host path bind-mounted into every spawned container at `/opt/ricochet/data/content`. Must contain ricochet's deployment bundles. | | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| | `cache_path` | yes | Host path bind-mounted into every spawned container at `/opt/ricochet/cache`. Stores R, Julia, and Python package caches shared across content. | | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| | `proxy_host` | no | Hostname ricochet's reverse proxy uses to reach spawned content containers. Defaults to `localhost`; override when running ricochet-server in a container (see [Deployment scenarios](#deployment-scenarios)). | | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
|
||||||||
| ricochet refuses to start if `exec_env_config_path`, `content_path`, or `cache_path` is missing. | ||||||||
|
|
||||||||
| ### Image resolution | ||||||||
|
|
||||||||
| `default_image` and per-content `exec_env` values are looked up in the exec-env config. | ||||||||
| A category like `r-default` resolves to a concrete image reference (`docker.io/ricochetrs/r-alpine:4.5`). | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| If the value does not match a category, ricochet treats it as a full image reference and uses it as-is. | ||||||||
|
|
||||||||
| ### Exec-env config | ||||||||
|
|
||||||||
| The exec-env config TOML maps category names to concrete container images and declares the language runtimes inside each image. | ||||||||
| Content items pick a category via their `exec_env` setting; ricochet falls back to `default_image` when none is set. | ||||||||
|
|
||||||||
| The example below references the [official ricochet exec-env images](https://github.com/ricochet-rs/exec-envs) — adjust the image and tag, or substitute your own. | ||||||||
|
|
||||||||
| ```toml title="ricochet-exec-env.toml" | ||||||||
| [image.r-default] | ||||||||
| image = "docker.io/ricochetrs/r-alpine:4.5" | ||||||||
| os = "alpine-3.23" | ||||||||
| arch = ["linux/amd64", "linux/arm64"] | ||||||||
| description = "Default R 4.5 environment (Alpine)" | ||||||||
| r = [ | ||||||||
| { version = "4.5.3", bin = "/usr/bin/R" }, | ||||||||
| ] | ||||||||
|
|
||||||||
| [image.r-44] | ||||||||
| image = "docker.io/ricochetrs/r-alpine:4.4" | ||||||||
| os = "alpine-3.23" | ||||||||
| arch = ["linux/amd64", "linux/arm64"] | ||||||||
| r = [ | ||||||||
| { version = "4.4.3", bin = "/usr/bin/R" }, | ||||||||
| ] | ||||||||
|
|
||||||||
| [image.python-default] | ||||||||
| image = "docker.io/ricochetrs/python-ubuntu:3.13" | ||||||||
| os = "ubuntu-26.04" | ||||||||
| arch = ["linux/amd64", "linux/arm64"] | ||||||||
| description = "Default Python 3.13 environment (Ubuntu)" | ||||||||
| python = [ | ||||||||
| { version = "3.13", bin = "/usr/local/bin/python3" }, | ||||||||
| ] | ||||||||
| ``` | ||||||||
|
|
||||||||
| `r`, `python`, and `julia` arrays declare the interpreter versions present in the image and the path to their binaries — ricochet uses these to match a content item's required version against an available runtime. | ||||||||
|
|
||||||||
| ### Deployment scenarios | ||||||||
|
|
||||||||
| How ricochet-server itself is deployed shapes the container-backend setup. | ||||||||
| The ricochet-server install is covered on its own page — only the container-backend specifics are below. | ||||||||
|
|
||||||||
| #### ricochet-server via systemd (on the host) | ||||||||
|
|
||||||||
| Set up ricochet-server first — see [Host installation](/dev/admin/installation/1-host/). | ||||||||
|
|
||||||||
| Container-backend specifics: | ||||||||
|
|
||||||||
| - The daemon and ricochet-server share the host's filesystem, so `content_path` and `cache_path` resolve consistently on both sides — nothing to translate. | ||||||||
| - The user running ricochet-server needs read/write access to the daemon socket (typically by adding it to the `docker` group, or running as root as the default systemd unit does). | ||||||||
| - `proxy_host` can stay at its default `localhost` since both processes share the host's network namespace. | ||||||||
|
|
||||||||
| #### ricochet-server in a container (sibling containers) | ||||||||
|
|
||||||||
| Set up ricochet-server in a container first — see [Container installation](/dev/admin/installation/2-container/). | ||||||||
|
|
||||||||
| ricochet-server runs as a container and uses the host's daemon to spawn sibling containers for user content — sometimes called "Docker-out-of-Docker" (DooD). | ||||||||
| Three container-backend specifics apply: | ||||||||
|
|
||||||||
| 1. **Bind-mount the daemon socket** (`/var/run/docker.sock`) into the ricochet-server container. | ||||||||
| 2. **Match host and ricochet-server paths for `content_path` and `cache_path`.** The daemon resolves these against the **host** filesystem, not ricochet-server's view, so the simplest correct setup mounts the host data directory at the same path inside the ricochet-server container. | ||||||||
| 3. **Set `proxy_host` to an address ricochet-server can reach.** Sibling content containers publish their app ports to the host, not to ricochet-server's loopback. Either run ricochet-server with `--network host` (default `proxy_host` works), or use `--add-host=host.docker.internal:host-gateway` together with `proxy_host = "host.docker.internal"`, or set `proxy_host` to the host's IP. | ||||||||
|
|
||||||||
| ```sh title="Bridge network with host-gateway alias" | ||||||||
| docker run -d \ | ||||||||
| --name ricochet-server \ | ||||||||
| -p 6188:6188 \ | ||||||||
| --add-host=host.docker.internal:host-gateway \ | ||||||||
| -v /var/run/docker.sock:/var/run/docker.sock \ | ||||||||
| -v /var/lib/ricochet:/var/lib/ricochet \ | ||||||||
| docker.io/ricochetrs/ricochet-server:latest | ||||||||
| ``` | ||||||||
|
|
||||||||
| ```toml title="ricochet-config.toml (DooD)" | ||||||||
| [backend] | ||||||||
| engine = "container" | ||||||||
| exec_env_config_path = "ricochet-exec-env.toml" | ||||||||
| content_path = "/var/lib/ricochet/data/content" | ||||||||
| cache_path = "/var/lib/ricochet/data/.cache" | ||||||||
| proxy_host = "host.docker.internal" | ||||||||
| ``` | ||||||||
|
|
||||||||
| ## Kubernetes backend | ||||||||
|
|
||||||||
| For multi-node clusters, use `engine = "k8s"`. | ||||||||
| ricochet creates a Deployment per app and a Job per task using the cluster's API. | ||||||||
|
|
||||||||
| ```toml title="ricochet-config.toml" | ||||||||
| [backend] | ||||||||
| engine = "k8s" | ||||||||
| namespace = "ricochet" | ||||||||
| default_image = "r-default" | ||||||||
| exec_env_config_path = "/etc/ricochet/exec-env.toml" | ||||||||
| pvc_content_name = "ricochet-content" | ||||||||
| pvc_cache_name = "ricochet-cache" | ||||||||
| image_pull_policy = "IfNotPresent" | ||||||||
| ``` | ||||||||
|
|
||||||||
| The same [`ricochet-exec-env.toml`](#exec-env-config) format applies, and the [official exec-env images](https://github.com/ricochet-rs/exec-envs) work unchanged as Pod images — they're built multi-arch (amd64/arm64) and ship with the same runtimes used by the container backend. | ||||||||
|
|
||||||||
| See [Kubernetes installation](/dev/admin/installation/3-kubernetes/) for cluster prerequisites, Helm chart values, and PVC sizing guidance. | ||||||||
|
|
||||||||
| ## Known issues | ||||||||
|
|
||||||||
| <Aside type="caution" title="Path mismatch is silent"> | ||||||||
| Applies to the [container backend](#container-backend) — most often in the [DooD scenario](#ricochet-server-in-a-container-sibling-containers) where ricochet-server's filesystem view differs from the host's. | ||||||||
|
|
||||||||
| If `content_path` points to a path that does not exist on the host, the daemon still launches the spawned container — but bind-mounts the wrong (or empty) directory. | ||||||||
| Content items will appear to deploy but fail at runtime because their bundles are not visible. | ||||||||
| Verify `ls $content_path` works on the **host**, not just inside the ricochet-server container. | ||||||||
| </Aside> | ||||||||
|
|
||||||||
| <Aside type="caution" title="Wrong proxy_host is silent too"> | ||||||||
| Applies to the [container backend](#container-backend) in the [DooD scenario](#ricochet-server-in-a-container-sibling-containers) only — see [Field reference](#container-backend) for the `proxy_host` setting. | ||||||||
|
|
||||||||
| If `proxy_host` resolves but does not actually reach the host (typo, wrong IP, missing `--add-host`), spawned content containers start and pass health checks at the container layer, but ricochet's reverse proxy gets a connection refused or timeout when it tries to route a request — surfacing as 502s in the UI. | ||||||||
| </Aside> | ||||||||
|
|
||||||||
| <Aside type="note" title="Remote daemons not yet supported"> | ||||||||
| Applies to the [container backend](#container-backend). The [Kubernetes backend](#kubernetes-backend) is the supported path for multi-host execution today. | ||||||||
|
|
||||||||
| ricochet currently only talks to a daemon on the same host as ricochet-server. | ||||||||
| Remote daemon support is on the roadmap. | ||||||||
| </Aside> | ||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,8 +2,104 @@ | |
| title: Container | ||
| --- | ||
|
|
||
| import { Aside } from "@astrojs/starlight/components"; | ||
| import { Aside, Tabs, TabItem } from "@astrojs/starlight/components"; | ||
|
|
||
| <Aside type="note" title="Coming Soon"> | ||
| This documentation is under construction. | ||
| Run ricochet-server itself as a container. | ||
| This is the same `ricochet-server` binary as the [host install](/dev/admin/installation/1-host/), packaged as an image. | ||
|
|
||
| ## Image | ||
|
|
||
| ``` | ||
| docker.io/ricochetrs/ricochet-server:<version> | ||
| ``` | ||
|
|
||
| Tags follow the ricochet release version (e.g. `v0.7.3`). | ||
| A `latest` tag tracks the most recent stable release. | ||
|
|
||
| ## Required volumes | ||
|
|
||
| Two host directories must be persisted across container restarts: | ||
|
|
||
| | Mount target | Purpose | | ||
| | ----------------------- | --------------------------------------------------------------------------- | | ||
| | `/var/lib/ricochet` | Database, deployment bundles, encryption keys, file logs. | | ||
| | `/var/lib/ricochet/data/.cache` | R, Julia, and Python package caches. Share across content runs. | | ||
|
|
||
| The cache directory lives under `RICOCHET_HOME` by default, so mounting `/var/lib/ricochet` covers both. | ||
| Separate them only if you want the cache on a different volume (e.g. faster local disk vs. networked storage). | ||
|
|
||
| ## Ports | ||
|
|
||
| ricochet-server listens on **6188** by default. | ||
|
|
||
| ## Minimal run | ||
|
|
||
| ```sh | ||
| docker run -d \ | ||
| --name ricochet-server \ | ||
| -p 6188:6188 \ | ||
| -v /var/lib/ricochet:/var/lib/ricochet \ | ||
| docker.io/ricochetrs/ricochet-server:latest | ||
| ``` | ||
|
|
||
| Place your `ricochet-config.toml` at `/var/lib/ricochet/ricochet-config.toml` on the host. | ||
| ricochet-server reads it on startup from the bind-mounted location. | ||
|
Comment on lines
+35
to
+46
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, I still can't get ricochet-server to run code in a container. I don't think this should be published yet. |
||
|
|
||
| ## Spawning user content | ||
|
|
||
| The execution backend ricochet uses for spawning user content (apps, tasks) is independent of how ricochet-server itself runs. | ||
|
|
||
| ### Host (default) | ||
|
|
||
| With no `[backend]` block in `ricochet-config.toml`, ricochet-server runs content as [libcontainer](https://docs.rs/libcontainer/)-managed isolated processes **inside** its own container, using the interpreters baked into the ricochet-server image. | ||
| Suitable for small single-tenant setups where every content item can share the same runtime environment. | ||
|
|
||
| See [Host vs container on a single host](/dev/admin/configuration/execution-backend/#host-vs-container-on-a-single-host) for the tradeoff. | ||
|
|
||
| ### Container backend (sibling containers) | ||
|
|
||
| To spawn each piece of user content as its own container on the host, configure the [container backend](/dev/admin/configuration/execution-backend/#container-backend), mount the host's container daemon socket into ricochet-server, and make sure ricochet-server can reach the host network so it can proxy to spawned containers: | ||
|
Comment on lines
+59
to
+61
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this covered by the DooD section? |
||
|
|
||
| ```sh | ||
| docker run -d \ | ||
| --name ricochet-server \ | ||
| -p 6188:6188 \ | ||
| --add-host=host.docker.internal:host-gateway \ | ||
| -v /var/run/docker.sock:/var/run/docker.sock \ | ||
| -v /var/lib/ricochet:/var/lib/ricochet \ | ||
| docker.io/ricochetrs/ricochet-server:latest | ||
| ``` | ||
|
|
||
| Set in `ricochet-config.toml`: | ||
|
|
||
| - `content_path` and `cache_path` to paths that exist on the **host**. | ||
| With the example mount above, `content_path = "/var/lib/ricochet/data/content"` resolves to the same physical directory from both ricochet-server's view and the host daemon's view — sibling containers see the deployment bundles ricochet-server wrote. | ||
| - `proxy_host = "host.docker.internal"` so pingora reaches the host's published ports instead of ricochet-server's own loopback. | ||
|
|
||
| <Aside type="caution" title="Paths are host paths"> | ||
| The daemon resolves `content_path` and `cache_path` against the host filesystem, not the ricochet-server container's view. | ||
| Mount the host data directory into the ricochet-server container at the **same path** used in the config, or sibling containers will mount empty directories and content will fail to start. | ||
| </Aside> | ||
|
|
||
| See the [execution backend reference](/dev/admin/configuration/execution-backend/#ricochet-server-in-a-container-sibling-containers) for `proxy_host` alternatives (`--network host`, explicit host IP) and the full field reference. | ||
|
|
||
| ### Kubernetes backend | ||
|
|
||
| For multi-node setups, do not run ricochet-server as a standalone container — use the [Kubernetes installation](/dev/admin/installation/3-kubernetes/) instead. | ||
|
|
||
| ## Compose example | ||
|
|
||
| ```yaml title="compose.yml" | ||
| services: | ||
| ricochet-server: | ||
| image: docker.io/ricochetrs/ricochet-server:latest | ||
| container_name: ricochet-server | ||
| ports: | ||
| - "6188:6188" | ||
| extra_hosts: | ||
| - "host.docker.internal:host-gateway" # only for container backend | ||
| volumes: | ||
| - /var/lib/ricochet:/var/lib/ricochet | ||
| - /var/run/docker.sock:/var/run/docker.sock # only for container backend | ||
| restart: unless-stopped | ||
| ``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.