Create, Start, Exit — and Exit Codes
This lab runs on a real VM
This box is not a Pod but a virtual machine launched by KubeVirt. A separate Linux kernel runs in it, systemd actually manages services, and docker is a real Docker engine, not an imitation. A container started with docker run becomes a real process, and both docker exec and docker logs work as usual.
This lab used to run inside a Pod. That box had dropped every kernel capability, so the step that starts a container was blocked, and you learned by working around it and unpacking image archives by hand. The workaround is no longer needed.
There are two things to know.
- The first start takes a little over a minute. The VM boots and installs Docker, so it is slower than a Pod lab (usually 40 seconds).
- There is no browser preview. Only one grading port is open for connections into the VM. If you start a web server, check it with
curlfrom inside the VM.
Goal
You create container state transitions by hand and deliberately reproduce exit codes 143, 137 and 42, so that you learn their meaning by doing.
Why it matters
The report we get most often in operations is "the container died". But there are at least three ways to die, and each needs a completely different response. If the app ended by itself, you need to look at the code; if it ended after receiving SIGTERM, it was a normal deployment; and if it was killed by SIGKILL, nothing is left in the app log. The exit code is the cheapest signal for telling these three apart. Once you know one rule, that a value above 128 is 128 + 신호번호 (128 plus the signal number), you can read 143 = 128+15 = SIGTERM and 137 = 128+9 = SIGKILL right away.
Steps
- Create
/root/dk2, and, usingalpine:3.20, only create (do not start) a container nameddk-life. The command issleep 600. - Start
dk-lifeand put it in the running state. - Using
alpine:3.20, rundk-trapin the background so that it printscaught-termwhen it receives SIGTERM and ends with exit code 143. Then stop it normally. - Using
alpine:3.20, rundk-notrapin the background (the command issleep 600) and stop it with a grace period of only 2 seconds. The exit code must be 137. - Using
alpine:3.20, rundk-failso that it ends with exit code 42, and write only that number to/root/dk2/exit42.txt. - Start the
dk-restartcontainer with the restart policy on-failure, at most 3 times. - Take the process list from inside a container and save it to
/root/dk2/pid1.txt. The PID column must be visible. This lab box is itself a container. If you runps -e -o pid,ppid,commhere, you can confirm both that PID 1 is this container's main command and that only a handful of processes are visible. - In
/root/dk2/lifecycle.md, write the names and exit codes of the three containersdk-trap,dk-notrapanddk-fail, one per line.
Notes
- You can adjust the grace period with
docker stop -t <초>(the number after the option is in seconds). - Register a shell signal handler in the form
trap '명령' TERM(the quoted placeholder stands for the command to run). - Read the exit code with
docker inspect <이름> | jq -r '.[0].State.ExitCode'(the placeholder stands for the container name). - Common mistake 1: in step 3, if
sleepruns before the handler is registered, the signal is not received. Build it as a loop that repeats a short sleep. - Common mistake 2: if you run step 7 on the host, dozens of processes appear. Inside a container there should be only a handful.
Create without running
Create /root/dk2, and, using alpine:3.20, only create (do not start) a container named dk-life. The command is sleep 600.
run creates and starts right away. There is a separate subcommand that only creates. In this state there is no process yet.
Start the container you created
Start dk-life and put it in the running state.
Think about why creating and starting are separate. Once it starts, State.Pid gets an actual process number.
Exit cleanly on SIGTERM
Using alpine:3.20, run dk-trap in the background so that it prints caught-term when it receives SIGTERM and ends with exit code 143. Then stop it normally.
The shell has a builtin command for registering a signal handler. If the handler ends with the exit code you want, that value becomes the container exit code as is.
What happens without a handler
Using alpine:3.20, run dk-notrap in the background (the command is sleep 600) and stop it with a grace period of only 2 seconds. The exit code must be 137.
A PID 1 that has not registered a handler ignores SIGTERM. If you give a short grace period, you can see the result without waiting.
The exit code the application produced
Using alpine:3.20, run dk-fail so that it ends with exit code 42, and write only that number to /root/dk2/exit42.txt.
Dying from a signal and ending by the app's own choice have different exit code ranges. A value below 128 is one the app produced directly.
Restart only on failure
Start the dk-restart container with the restart policy on-failure, at most 3 times.
There are four restart policies. Choose the value that restarts only on failure, and with a limit on the count.
The process list inside a container
Take the process list from inside a container and save it to /root/dk2/pid1.txt. The PID column must be visible.
This lab box is itself a container. If you run ps -e -o pid,ppid,comm here, you can confirm both that PID 1 is this container's main command and that only a handful of processes are visible.
If you list processes inside a container, the list is very short and you can immediately see what number 1 is. Do not run it on the host.
Build the exit code table
In /root/dk2/lifecycle.md, write the names and exit codes of the three containers dk-trap, dk-notrap and dk-fail, one per line.
Check the exit codes of the three containers with a real inspect and write them in the table. If you also write down why each value is what it is, it will help you later.