deploy: add COPY-into-image tabs, clarify volume ownership, drop mounts
- deploy.md: new 'Getting your files into the image' subsection — a multi-tab annotated block (single file / directory / deps-first) showing COPY <src> <dest> patterns, plus a warning not to COPY secrets. - clarify volume ownership: 'Logs and config are ours; the rest is yours' — we inject/manage the logs + config host mounts; devs put everything else in named volumes Docker owns. - remove all /srv/mounts: dropped the MOUNTS_DIR volume + env var and every mounts reference from the compose block, injected-vars, paths line, and checklist. environments.md + index.md mounts mentions cleaned too. Verified in-browser: COPY tabs switch, volume tip renders, zero mounts refs remain; mkdocs build --strict clean. Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
+54
-12
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
- service key is always **`svc`**
|
- service key is always **`svc`**
|
||||||
- named volumes use **bare names** (`cache`, not `myapp_cache`)
|
- named volumes use **bare names** (`cache`, not `myapp_cache`)
|
||||||
- host paths come from **`${LOGS_DIR}` / `${CONFIG_DIR}` / `${MOUNTS_DIR}`**
|
- host paths come from **`${LOGS_DIR}` / `${CONFIG_DIR}`**
|
||||||
|
|
||||||
Naming things after your repo used to cause **container-name collisions** at
|
Naming things after your repo used to cause **container-name collisions** at
|
||||||
fleet scale — two repos shipping the same name clashed. The generic compose
|
fleet scale — two repos shipping the same name clashed. The generic compose
|
||||||
@@ -79,6 +79,45 @@ CMD ["python", "-m", "yourapp"]
|
|||||||
3. `--system` installs into the image's Python (no venv needed — the container
|
3. `--system` installs into the image's Python (no venv needed — the container
|
||||||
*is* the isolation); reads `pyproject.toml`, no `uv.lock` required.
|
*is* the isolation); reads `pyproject.toml`, no `uv.lock` required.
|
||||||
|
|
||||||
|
### Getting your files into the image
|
||||||
|
|
||||||
|
`COPY . .` grabs the whole repo, but be explicit about anything that needs its own
|
||||||
|
place — assets, templates, a config the app reads at runtime. `COPY <src> <dest>`:
|
||||||
|
`<src>` is relative to the build context (your repo), `<dest>` is a path in the
|
||||||
|
image.
|
||||||
|
|
||||||
|
=== "A single file"
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
COPY ./config.toml /app/config.toml # (1)!
|
||||||
|
```
|
||||||
|
|
||||||
|
1. One file into a specific path. The app reads it at `/app/config.toml`.
|
||||||
|
|
||||||
|
=== "A directory"
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
COPY ./assets /app/assets # (1)!
|
||||||
|
```
|
||||||
|
|
||||||
|
1. A whole tree. Trailing paths are dirs — `/app/assets/` mirrors `./assets/`.
|
||||||
|
|
||||||
|
=== "Deps first (layer caching)"
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
COPY requirements.txt . # (1)!
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
COPY . . # (2)!
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Copy just the deps file first, install, **then** copy the code.
|
||||||
|
2. See [layer caching](#layer-caching) — this ordering is why.
|
||||||
|
|
||||||
|
!!! warning "Don't `COPY` secrets into the image"
|
||||||
|
Anything sensitive stays **out** of the image — no `COPY ./secrets.env`. Secrets
|
||||||
|
live on the host and are injected read-only at runtime (see [Secrets](#secrets)).
|
||||||
|
Add them to `.dockerignore` so a blanket `COPY . .` can't sweep them in.
|
||||||
|
|
||||||
## Your `compose.yaml`
|
## Your `compose.yaml`
|
||||||
|
|
||||||
Copy this verbatim. It names nothing repo-specific — the deploy layer fills in
|
Copy this verbatim. It names nothing repo-specific — the deploy layer fills in
|
||||||
@@ -95,7 +134,6 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ${LOGS_DIR:-./logs}:/app/logs # (4)!
|
- ${LOGS_DIR:-./logs}:/app/logs # (4)!
|
||||||
- ${CONFIG_DIR:-./config}:/app/config # (5)!
|
- ${CONFIG_DIR:-./config}:/app/config # (5)!
|
||||||
- ${MOUNTS_DIR:-./mounts}:/app/mounts
|
|
||||||
- cache:/app/cache # (6)!
|
- cache:/app/cache # (6)!
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
@@ -112,16 +150,21 @@ volumes:
|
|||||||
what recovers a crashed container.
|
what recovers a crashed container.
|
||||||
4. Logs → host, path **injected** by the deploy layer. The `:-./logs` default lets
|
4. Logs → host, path **injected** by the deploy layer. The `:-./logs` default lets
|
||||||
you `docker compose up` locally with nothing set and still work.
|
you `docker compose up` locally with nothing set and still work.
|
||||||
5. Config → host, likewise injected. Same pattern for `${MOUNTS_DIR}`.
|
5. Config → host, likewise injected.
|
||||||
6. Named volume with a **bare name** — ephemeral, auto-namespaced per service at
|
6. Named volume with a **bare name** — ephemeral, auto-namespaced per service at
|
||||||
deploy time so it can't collide.
|
deploy time so it can't collide.
|
||||||
7. Declare bare (`cache`, not `myapp_cache`). Deploy auto-prefixes it.
|
7. Declare bare (`cache`, not `myapp_cache`). Deploy auto-prefixes it.
|
||||||
|
|
||||||
|
!!! tip "Logs and config are ours; the rest is yours"
|
||||||
|
**We handle `logs` and `config`** — the two `${...}` host mounts above are
|
||||||
|
injected and managed at deploy time. **Everything else is yours:** anything
|
||||||
|
your service needs to persist (caches, profiles, scratch) goes in a **named
|
||||||
|
volume**, which Docker owns — no host paths for you to manage.
|
||||||
|
|
||||||
!!! tip "The `${VAR:-./default}` pattern"
|
!!! tip "The `${VAR:-./default}` pattern"
|
||||||
Every host path uses a variable with a local fallback. Locally, `docker compose
|
Both host paths use a variable with a local fallback. Locally, `docker compose
|
||||||
up` needs nothing set — it uses `./logs`, `./config`, `./mounts`. On the fleet,
|
up` needs nothing set — it uses `./logs` and `./config`. On the fleet, the
|
||||||
the deploy layer sets the real values (below), so the same file works both
|
deploy layer sets the real values (below), so the same file works both places.
|
||||||
places.
|
|
||||||
|
|
||||||
## How deploy fills it in
|
## How deploy fills it in
|
||||||
|
|
||||||
@@ -132,7 +175,6 @@ are injected — not baked into your file. You can rely on these being present:
|
|||||||
COMPOSE_PROJECT_NAME=<workspace>-<name>
|
COMPOSE_PROJECT_NAME=<workspace>-<name>
|
||||||
LOGS_DIR=/srv/logs/<workspace>/<name>
|
LOGS_DIR=/srv/logs/<workspace>/<name>
|
||||||
CONFIG_DIR=/srv/config/<workspace>/<name>
|
CONFIG_DIR=/srv/config/<workspace>/<name>
|
||||||
MOUNTS_DIR=/srv/mounts/<workspace>/<name>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Which makes ownership obvious and collisions impossible:
|
Which makes ownership obvious and collisions impossible:
|
||||||
@@ -150,9 +192,9 @@ Which makes ownership obvious and collisions impossible:
|
|||||||
- **`<name>`** — the git repo name, lowercased, by default. Overridable at
|
- **`<name>`** — the git repo name, lowercased, by default. Overridable at
|
||||||
deploy time.
|
deploy time.
|
||||||
|
|
||||||
Host paths live under `/srv/<kind>/<workspace>/<name>/` (config, logs, mounts), all
|
Host paths live under `/srv/<kind>/<workspace>/<name>/` (config, logs), all owned by
|
||||||
owned by the `services` user (uid/gid **1337**). You never write these paths in your
|
the `services` user (uid/gid **1337**). You never write these paths in your compose —
|
||||||
compose — you read the injected `${...}` variables.
|
you read the injected `${...}` variables.
|
||||||
|
|
||||||
## Onboarding a service
|
## Onboarding a service
|
||||||
|
|
||||||
@@ -227,7 +269,7 @@ services:
|
|||||||
- service key `svc` — **no `container_name`**, no repo-specific names
|
- service key `svc` — **no `container_name`**, no repo-specific names
|
||||||
- `user: "1337:1337"` — the shared `services` account
|
- `user: "1337:1337"` — the shared `services` account
|
||||||
- `restart: unless-stopped`
|
- `restart: unless-stopped`
|
||||||
- host paths via `${LOGS_DIR}` / `${CONFIG_DIR}` / `${MOUNTS_DIR}` — never hardcoded
|
- host paths via `${LOGS_DIR}` / `${CONFIG_DIR}` — never hardcoded
|
||||||
- named volumes with **bare names** (`cache`, not `myapp_cache`)
|
- named volumes with **bare names** (`cache`, not `myapp_cache`)
|
||||||
- for browser/subprocess workloads: `init: true`, `shm_size`, `mem_limit`
|
- for browser/subprocess workloads: `init: true`, `shm_size`, `mem_limit`
|
||||||
|
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ happens, depending on where the project runs:
|
|||||||
```
|
```
|
||||||
|
|
||||||
This is how things run in production — see the [Deploy guide](deploy.md) for the
|
This is how things run in production — see the [Deploy guide](deploy.md) for the
|
||||||
full container standard (uid 1337, mounts, layer caching, and the uv image
|
full container standard (uid 1337, the compose convention, layer caching, and the
|
||||||
setup).
|
uv image setup).
|
||||||
|
|
||||||
!!! tip "Which one?"
|
!!! tip "Which one?"
|
||||||
**Local `.venv`** for quick iteration, **Makefile** when you want repeatable
|
**Local `.venv`** for quick iteration, **Makefile** when you want repeatable
|
||||||
|
|||||||
+2
-2
@@ -70,7 +70,7 @@ out of it; examples use placeholders like `<workspace>`, `<project>`, and
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
How to get a project running on **rethink-net** — containers, paths and
|
How to get a project running on **rethink-net** — the compose convention,
|
||||||
mounts, permissions, and secrets.
|
the one-command deploy, and secrets.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user