fivenines
22/40

Guided Problem

MiniDock Build 17: Preserve Logs While Operators Attach and Exec

Time
25m
Level
intermediate
Artifacts
not specified
Progress0%
Lesson 22 · Runtime

exec, attach, and logs

Design claim: Process creation, live stream attachment, and durable log capture are related paths with different lifetimes.

Starting model

  • You can restart eligible crashes without reviving manual stops or creating a tight loop.
  • The useful vocabulary at this point is deliberately small: stdio, durable output driver, attach, follow.

Three commands touch streams for different reasons

Operators must inspect a running container, follow its output, disconnect safely, and recover past output after clients leave. Runtime behavior unfolds over time, so state and ownership must remain valid after the initiating request has returned.

The tempting shortcut is straightforward: treat the original CLI connection as the owner of the process and its only output stream. The shortcut confuses one command or connection with the longer-lived process and resources it happens to touch. Containers outlive clients, exec creates additional processes, attach is live and transient, and logs must remain available after disconnection. Asynchronous exit, retry, disconnect, or timeout exposes that mismatch immediately.

Separate custody, subscription, and persistence

The container writes to stdout/stderr exactly once; the shim fans it out. One permanent consumer — the log driver (file, journald, remote shipper), always on, which is why logs can show you output from before you asked. And zero or more live consumers: attach streams multiplex the same bytes to CLI clients over the API. Detaching just unsubscribes; the container never knows. The producer and consumers are fully decoupled — a slow attached client can't block the app.

The shim retains process streams, attach multiplexes a live connection, the logging path persists output, and exec creates a separately tracked process in existing isolation. The mechanism records the durable fact separately from transient control and lets events update the model when reality changes.

Read the static view as custody and the dynamic view as evidence crossing that custody boundary.

Architecture — stream plumbing through the shim
  flowchart LR
    C["container process
stdout / stderr"] --> SH["shim
(fan-out point)"] SH --> LD["log driver
always on → files / shipper"] SH --> AT["attach multiplexer"] AT --> U1["client 1 (live)"] AT --> U2["client 2 (live)"] LD --> LOGS[("stored logs
serves 'minidock logs'")] U3["stdin from attached client"] --> SH --> C

It separates the main process, exec processes, shim streams, logger, and clients. Its central claim is that client connection lifetime never becomes process or log lifetime; the labels therefore describe authority rather than decorative grouping.

Join a running container without replacing it

exec answers "let me poke around inside." The engine asks the runtime to start a new process that joins the container's existing namespaces and cgroup — same fake world, same budgets — but it is not the app's child and its death doesn't touch the container's state machine:

Attach subscribes to the main process streams, logs read the configured durable sink, and exec asks the runtime to join the container's namespaces for a new process. The sequence matters because lifecycle decisions made from stale evidence become illegal or destructive operations.

The second figure tests the same model in motion: it traces attach, disconnect, continued logging, and a later log read.

Sequence — minidock exec c-42 sh
  sequenceDiagram
    participant U as Client
    participant E as Engine
    participant R as Runtime
    participant NS as c-42's namespaces
    U->>E: exec c-42 sh (interactive)
    E->>R: start process "sh" joining c-42's ns + cgroup
    R->>NS: enter PID, MNT, NET … then exec sh
    NS-->>E: exec process running (own stdio pipes)
    E-->>U: bidirectional stream (your keystrokes ↔ sh)
    U->>E: exit
    note over NS: sh dies while container app never noticed —
exec exit ≠ container exit

It traces attach, disconnect, continued logging, and a later log read. The ordering is valid only when it continues to preserve the stated invariant under retries and interruption.

Disconnect is not exit

An attached terminal disconnects while the application continues writing, so the container must live and logging must continue. The failure case is authoritative input to the state model, not an exception to be hidden behind a successful command response.

Client connection lifetime never becomes process or log lifetime. The reusable rule keeps process reality, operator intent, and retained resources from collapsing into one status flag.

Give MiniDock independent stream lifetimes

MiniDock exposes separate control paths for live observation, historical output, and additional processes. MiniDock must preserve the lifecycle guarantee while leaving the current integration move to the learner.

What carries forward

  • Client connection lifetime never becomes process or log lifetime.
  • Support stored logs, live attachment, and diagnostics without transferring primary lifecycle ownership.
  • The rejected shortcut remains a diagnostic: if the design starts depending on it again, the original constraint has probably been lost.
Next step

See what actually stuck.

Take the practice scenarios now.