Step 1 of 5 · Reading · ~3 min
Read
Container Lifecycle
exec & attach
A running container is a process in six namespaces. There are two ways to reach it, they are not variations on each other, and choosing the wrong one is how people accidentally kill production.
attach: the same terminal
docker attach connects your terminal to the streams of PID 1 itself — the process the container was started with. Nothing new is created.
$ docker attach mybox
nginx logs streaming...
^C # SIGINT goes to PID 1, nginx exits, the container is down
That last line is the trap. Ctrl-C is not "leave"; it is a signal to the container's main process. Detaching without killing anything is the escape sequence Ctrl-P Ctrl-Q. And because there is exactly one PID 1, two people attached to one container share one stream and one keyboard.
exec: a new process in the same walls
docker exec starts a new process inside the container's namespaces:
$ docker exec -it mybox sh
/ # ls
Internally it is four steps and one syscall you have not met:
1. read the container's namespace handles from /proc/<pid-of-init>/ns/
2. setns() into each one: pid, mount, net, uts, ipc, (user)
3. fork; the child execs the requested command, now inside those namespaces
4. the CLI wires your terminal to the child's stdin/stdout/stderr
Two consequences that explain otherwise baffling output. The exec'd process is not a child of the container's PID 1 — it was forked by a process outside the container, so ps inside shows it with a parent that does not exist in this namespace. And by default the process joins the container's namespaces but not all of its cgroups, so a shell you exec in to debug a memory limit may not itself be subject to that limit. Measuring a container from a shell you exec'd into it can therefore mislead you in both directions.
The practical rule: attach to watch the thing that is running, exec to do anything else. Note too that killing an exec'd process leaves the container running, while PID 1 exiting stops the container and takes everything else in it down — which is the exercise's STOP.
Why the exercise is a simulator
setns() requires CAP_SYS_ADMIN, and there is no container in the grader to enter. The bookkeeping is the lesson: which processes exist, which one is special, and what dies with what.
Your exercise: exec & attach Simulator
| Docker does | Your simulator does |
|---|---|
| start the container; the entrypoint becomes PID 1 | RUN <id> — PID 1 is init |
docker exec: a new process in the same namespaces | EXEC <id> <name> — pids from 100 upward, per container |
| signal a process that is not the entrypoint | KILL <id> <pid> — KILLED, or a refusal for PID 1, or ERR no such pid |
docker attach: connect to PID 1's streams | ATTACH <id> |
| PID 1 exits: everything in the container goes with it | STOP <id> — reports how many processes died, PID 1 included |
ps inside the container | PROCS <id> — 1 init first, then the exec'd processes by pid |
Three rules carry the tests. PID 1 is not killable through KILL — stopping a container is a different operation with different consequences, and the simulator says so rather than pretending. The exec pid counter is per container and never rewinds within a run, so killing pid 100 does not let the next EXEC reuse it. And once stopped, every command against that container answers ERR not running — the namespaces are gone, so there is nothing to enter, attach to, or list. Starting the same id again is a fresh container: PID 1, an empty process list, and the counter back at 100.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…