You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docker/README.md
+45-35Lines changed: 45 additions & 35 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,8 +3,8 @@
3
3
The `backend.ai-*` dockerfiles in this directory are built and published to
4
4
Docker Hub for every release tag by `.github/workflows/docker-images.yml`
5
5
(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.
8
8
9
9
## Published images
10
10
@@ -23,7 +23,7 @@ tooling and are not published.
23
23
| Tag | Meaning |
24
24
|---|---|
25
25
|`<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|
27
27
28
28
All images take the same build-arg contract: `PYTHON_VERSION` (from
29
29
`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).
37
37
38
38
## Infra images (not published)
39
39
40
+
Runtime helper images loaded on demand by the agent from bundled archives —
41
+
not published to Docker Hub.
42
+
40
43
| Dockerfile | Role |
41
44
|---|---|
42
45
|`krunner-extractor.dockerfile`| Extracts kernel-runner archives during agent operation |
@@ -50,33 +53,34 @@ path the image's default command reads:
50
53
51
54
| Service | Config mount target | Notes |
52
55
|---|---|---|
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 |
54
57
| agent |`/etc/backend.ai/agent.toml`| see the privilege and path-parity sections below |
55
58
| webserver |`/etc/backend.ai/webserver.conf`| note the `.conf` target name, not `.toml`|
56
-
| storage-proxy |`/etc/backend.ai/storage-proxy.toml`|runs unprivileged — set `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|
| 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 |
59
62
60
63
Shared prerequisites:
61
64
62
65
| Item | Used by | Why |
63
66
|---|---|---|
64
67
| halfstack services (PostgreSQL, Valkey/Redis, etcd) | all | the reference definitions live in `docker-compose.halfstack-main.yml`|
65
68
|`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 |
68
71
69
72
## Container privileges
70
73
71
74
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.
74
78
75
79
| Requirement | manager | agent | Why |
76
80
|---|---|---|---|
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|
80
84
|`pid: host`| — | ✅ | Host PID namespace visibility: the agent inspects and signals kernel processes by host PID |
81
85
|`cgroup: host` (host cgroup namespace) | — | ✅ |**Required, not optional** — see below |
82
86
| 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
89
93
(`host_pid_to_container_pid` in `src/ai/backend/agent/utils.py`, via
|`[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|
114
119
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`.
117
122
118
123
Vfolder roots (e.g. `/vfroot/local/volume1`) follow the same rule on the
119
124
**storage-proxy**: mount each volume at the identical absolute path on host and
120
125
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`.
122
133
123
134
## Reference compose file
124
135
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+.
129
142
130
143
```yaml
131
144
services:
132
145
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
136
148
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
139
150
- ./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
141
152
restart: unless-stopped
142
153
143
154
agent:
144
-
image: lablup/backend.ai-agent:26.4.4
155
+
image: lablup/backend.ai-agent:<version>
145
156
network_mode: host
146
157
privileged: true
147
158
pid: host
@@ -155,16 +166,15 @@ services:
155
166
capabilities: [gpu]
156
167
volumes:
157
168
- /var/run/docker.sock:/var/run/docker.sock
158
-
- /etc/machine-id:/etc/machine-id
159
169
- ./agent.toml:/etc/backend.ai/agent.toml:ro
160
170
# 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)
161
172
- /var/lib/backend.ai:/var/lib/backend.ai
162
173
- /tmp/backend.ai:/tmp/backend.ai
163
-
- /tmp/backend-ai-krunner:/tmp/backend-ai-krunner
164
174
restart: unless-stopped
165
175
```
166
176
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