Skip to content

Commit a31b49c

Browse files
committed
doc(BA-7272): correct privilege matrix, fixtures mount, and krunner share location per review
1 parent d35acab commit a31b49c

1 file changed

Lines changed: 45 additions & 35 deletions

File tree

docker/README.md

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
The `backend.ai-*` dockerfiles in this directory are built and published to
44
Docker Hub for every release tag by `.github/workflows/docker-images.yml`
55
(matrix from `scripts/list-dockerfiles.sh --service`), multi-arch
6-
(`linux/amd64` + `linux/arm64`). The remaining dockerfiles are internal build
7-
tooling and are not published.
6+
(`linux/amd64` + `linux/arm64`). The remaining dockerfiles are runtime helper
7+
images and are not published.
88

99
## Published images
1010

@@ -23,7 +23,7 @@ tooling and are not published.
2323
| Tag | Meaning |
2424
|---|---|
2525
| `<version>` (e.g. `26.9.0`, `26.9.0rc1`) | The normalized package version of the release tag that built the image |
26-
| `latest` | The most recent **final** release only — never moved by rc/alpha/beta releases |
26+
| `latest` | Applied to **any** final (non-prerelease) release — never moved by rc/alpha/beta releases. This includes hotfixes cut from older release branches, so `latest` can move *backwards*; pin explicit versions in production |
2727

2828
All images take the same build-arg contract: `PYTHON_VERSION` (from
2929
`pants.toml`) and `PKGVER` (normalized `VERSION`), and install the release
@@ -37,6 +37,9 @@ crashes on the `triggered_user` metadata field added in 26.8).
3737

3838
## Infra images (not published)
3939

40+
Runtime helper images loaded on demand by the agent from bundled archives —
41+
not published to Docker Hub.
42+
4043
| Dockerfile | Role |
4144
|---|---|
4245
| `krunner-extractor.dockerfile` | Extracts kernel-runner archives during agent operation |
@@ -50,33 +53,34 @@ path the image's default command reads:
5053

5154
| Service | Config mount target | Notes |
5255
|---|---|---|
53-
| manager | `/etc/backend.ai/manager.toml` | also mount `fixtures/` at `/app/fixtures:ro` (initial DB fixtures) |
56+
| manager | `/etc/backend.ai/manager.toml` | also mount `fixtures/` at `/app/fixtures` **read-write** — manager RPC keypair (auto-generated at first start) + DB fixtures |
5457
| agent | `/etc/backend.ai/agent.toml` | see the privilege and path-parity sections below |
5558
| webserver | `/etc/backend.ai/webserver.conf` | note the `.conf` target name, not `.toml` |
56-
| storage-proxy | `/etc/backend.ai/storage-proxy.toml` | runs unprivilegedset `user:` to the vfroot owner UID:GID; mount TLS material at `/app/ssl:ro` if enabled |
59+
| storage-proxy | `/etc/backend.ai/storage-proxy.toml` | to run unprivileged, set the `user`/`group` knobs in `storage-proxy.toml` (the daemon drops privileges itself) rather than compose `user:` — the chown watcher requires *starting* as root; if TLS is enabled, mount the cert material read-only at whatever path `ssl-cert`/`ssl-privkey` point to |
5760
| appproxy-coordinator | `/etc/backend.ai/proxy-coordinator.toml` | |
58-
| appproxy-worker | `/etc/backend.ai/proxy-worker.toml` | one container per worker: each needs its OWN toml with a unique `authority`, protocol (`http`/`tcp`), `api_bind_addr` port, and non-overlapping `bind_port_range` — and the compose port mappings must match |
61+
| appproxy-worker | `/etc/backend.ai/proxy-worker.toml` | one container per worker: each needs its OWN toml with a unique `authority`, protocol (`http`/`tcp`), `api_bind_addr` port, and a non-overlapping `[proxy_worker.port_proxy] bind_port_range` (port-based frontends only) — and the compose port mappings must match |
5962

6063
Shared prerequisites:
6164

6265
| Item | Used by | Why |
6366
|---|---|---|
6467
| halfstack services (PostgreSQL, Valkey/Redis, etcd) | all | the reference definitions live in `docker-compose.halfstack-main.yml` |
6568
| `supergraph.graphql` + a GraphQL gateway (e.g. `ghcr.io/graphql-hive/gateway`) | GraphQL federation | the supergraph schema is generated per release (`scripts/generate-graphql-schema.sh`); the gateway composes manager subgraphs |
66-
| `/etc/machine-id` bind mount | manager, agent | stable node identity |
67-
| `wheelhouse/` mount at `/app/wheelhouse` (optional) | manager, agent | staging directory for extra plugin wheels (e.g. accelerator plugins) installed into the container on top of the base image |
69+
| RPC auth key distribution | manager, agent | the agent needs the manager's RPC **public** key to authenticate RPC calls — e.g. share the parity-mounted fixtures directory across nodes, or mount a common key directory at `/etc/backend.ai/keys:ro` |
70+
| `wheelhouse/` mount at `/app/wheelhouse` (optional) | manager, agent | an operator convention only — nothing in the images consumes it automatically; to add extra plugin wheels (e.g. accelerator plugins), the operator must `docker exec <container> pip install /app/wheelhouse/*.whl` or build a derived image |
6871

6972
## Container privileges
7073

7174
Most services run fine with compose defaults (bridge network, config file
72-
bind-mounted read-only). The manager and the agent need more; grant each item
73-
consciously — together they amount to root-equivalent control of the host.
75+
bind-mounted read-only). Only the **agent** needs real host privileges; the
76+
manager needs at most the Docker socket. Grant each item consciously —
77+
together they amount to root-equivalent control of the host.
7478

7579
| Requirement | manager | agent | Why |
7680
|---|---|---|---|
77-
| `network_mode: host` | || Kernel↔agent ZMQ/service ports and agent RPC are advertised on host addresses; kernels spawned on the host network must reach them |
78-
| `privileged: true` | || Container/device management against the host daemon; sysfs reads for metrics |
79-
| `/var/run/docker.sock` bind mount | || DooD: containers are created by talking to the **host** Docker daemon |
81+
| `network_mode: host` | optional || Agent: kernel↔agent ZMQ/service ports and agent RPC are advertised on host addresses; kernels spawned on the host network must reach them. Manager: convenience only — the bridge alternative works via the `announce-addr` / `announce-internal-addr` knobs |
82+
| `privileged: true` | || Agent: container/device management against the host daemon; sysfs reads for metrics. The manager does not need it — the Docker socket alone suffices for its (conditional) Docker use |
83+
| `/var/run/docker.sock` bind mount | conditional || DooD: containers are created by talking to the **host** Docker daemon. Manager: only when the `local` container registry is used |
8084
| `pid: host` ||| Host PID namespace visibility: the agent inspects and signals kernel processes by host PID |
8185
| `cgroup: host` (host cgroup namespace) ||| **Required, not optional** — see below |
8286
| Host `/sys` visibility ||| Container resource metrics are read from the host cgroupfs/sysfs (follows automatically from the host cgroup namespace) |
@@ -89,6 +93,7 @@ The agent's host-PID→container-PID translation
8993
(`host_pid_to_container_pid` in `src/ai/backend/agent/utils.py`, via
9094
`src/ai/backend/common/cgroup.py`) parses `/proc/<pid>/cgroup` expecting
9195
**host-rooted** paths (`docker/<id>` or `system.slice/docker-<id>.scope`) and
96+
reads hardcoded `/sys/fs/cgroup/...` paths; the metrics path separately
9297
resolves the cgroupfs mount point from `/proc/mounts`. In a private cgroup
9398
namespace, sibling-container paths are not host-rooted and the mounted cgroupfs
9499
is namespaced — PID translation and sysfs metrics both break.
@@ -108,40 +113,46 @@ the identical location:
108113
| Config knob (`agent.toml`) | Reference value | Used for |
109114
|---|---|---|
110115
| `[container] scratch-root` | `/var/lib/backend.ai/scratches` | Scratch roots of kernel containers |
111-
| `[agent] ipc-base-path` | `/tmp/backend.ai/ipc` | Agent↔kernel IPC sockets |
116+
| `[agent] ipc-base-path` | `/tmp/backend.ai/ipc` (this document's chosen value; `configs/agent/sample.toml` ships `/var/run/backend.ai/ipc`) | Agent↔kernel IPC sockets |
112117
| `[agent] var-base-path` | `/var/lib/backend.ai` | Plugin state bind-mounted into kernels (e.g. accelerator hook caches) |
113-
| — (fixed path) | `/tmp/backend-ai-krunner` | Kernel-runner files: the image entrypoint copies them here so the host daemon can mount them into kernels; **without this mount kernel creation fails** with `bind source path does not exist` (the entrypoint logs a warning at startup) |
118+
| env `BACKENDAI_KRUNNER_SHARED` | `/var/lib/backend.ai/krunner` | Kernel-runner files: the image entrypoint copies them here so the host daemon can mount them into kernels. Covered automatically by the `/var/lib/backend.ai` parity mount; the entrypoint **refuses to start** without it — override the path via the `BACKENDAI_KRUNNER_SHARED` env var |
114119

115-
With the reference values, three parity mounts cover everything:
116-
`/var/lib/backend.ai`, `/tmp/backend.ai`, and `/tmp/backend-ai-krunner`.
120+
With the reference values, two parity mounts cover everything:
121+
`/var/lib/backend.ai` and `/tmp/backend.ai`.
117122

118123
Vfolder roots (e.g. `/vfroot/local/volume1`) follow the same rule on the
119124
**storage-proxy**: mount each volume at the identical absolute path on host and
120125
in the storage-proxy container, so the kernel bind-mount sources it reports
121-
resolve on the host. The agent container itself does not need the vfroot mount.
126+
resolve on the host. The agent container itself does not need the vfroot mount
127+
— unless the agent itself performs the volume mounting
128+
(`cohabiting-storage-proxy = false`), in which case its `mount-path` must be a
129+
**shared-propagation** bind mount (`bind-propagation: rshared`) so host-side
130+
mounts become visible inside the container. Also, `scratch-type = "memory"` is
131+
unsupported in the containerized agent — a tmpfs mounted inside the container's
132+
namespace is invisible to the host daemon — use `hostdir`.
122133

123134
## Reference compose file
124135

125-
`docker-compose.monorepo.yml` at the repository root composes the service
126-
images and assumes the halfstack dependencies from
127-
`docker-compose.halfstack-main.yml`. The fragment below shows the full working
128-
shape of the two elevated services, verified against a live deployment:
136+
`docker-compose.monorepo.yml` at the repository root is a **partial, legacy
137+
example** — it uses different image names, includes no agent or storage-proxy,
138+
and runs on a bridge network. The fragment below is the authoritative
139+
reference for the two elevated services, verified against a live deployment.
140+
Replace `<version>` with a tag from the tagging scheme above. The `cgroup:`
141+
key requires Docker Compose v2.15+.
129142

130143
```yaml
131144
services:
132145
manager:
133-
image: lablup/backend.ai-manager:26.4.4
134-
network_mode: host
135-
privileged: true
146+
image: lablup/backend.ai-manager:<version>
147+
network_mode: host # optional — bridge works via the announce-addr knobs
136148
volumes:
137-
- /var/run/docker.sock:/var/run/docker.sock
138-
- /etc/machine-id:/etc/machine-id
149+
- /var/run/docker.sock:/var/run/docker.sock # only when the `local` container registry is used
139150
- ./manager.toml:/etc/backend.ai/manager.toml:ro
140-
- ./fixtures:/app/fixtures:ro
151+
- ./fixtures:/app/fixtures # read-write: the entrypoint writes the RPC keypair here
141152
restart: unless-stopped
142153

143154
agent:
144-
image: lablup/backend.ai-agent:26.4.4
155+
image: lablup/backend.ai-agent:<version>
145156
network_mode: host
146157
privileged: true
147158
pid: host
@@ -155,16 +166,15 @@ services:
155166
capabilities: [gpu]
156167
volumes:
157168
- /var/run/docker.sock:/var/run/docker.sock
158-
- /etc/machine-id:/etc/machine-id
159169
- ./agent.toml:/etc/backend.ai/agent.toml:ro
160170
# path-parity mounts: host path == container path
171+
# (the krunner share /var/lib/backend.ai/krunner is covered by the /var/lib/backend.ai mount)
161172
- /var/lib/backend.ai:/var/lib/backend.ai
162173
- /tmp/backend.ai:/tmp/backend.ai
163-
- /tmp/backend-ai-krunner:/tmp/backend-ai-krunner
164174
restart: unless-stopped
165175
```
166176
167-
The agent entrypoint builds its krunner symlink farm **at container start**, so
168-
after adding or changing the parity mounts, recreate the container
169-
(`docker compose up -d --force-recreate agent`) — a restart of the old
170-
container is not enough.
177+
Bind mounts are fixed at container **creation**, so after adding or changing
178+
the parity mounts, recreate the container
179+
(`docker compose up -d --force-recreate agent`) — the entrypoint does re-run
180+
on a plain restart, but the old container's mounts cannot change.

0 commit comments

Comments
 (0)