There is a simple server application in the ~/app
directory.
It is containerized with an extensible entrypoint.sh
script:
upon startup, the script checks for extra files in the container's /entrypoint.d
directory and executes them before starting the server itself.
This entrypoint script is a handy mechanism for running custom initialization logic,
and the team wants to preserve it.
However, something seems to be wrong with the container's termination process.
The docker stop
command hangs for a few seconds,
and eventually, the container gets forcibly killed.
Your task is to make the container exit gracefully after the docker stop
command is received,
while keeping the extensible entrypoint mechanism intact.
Key Points:
- You may modify the
Dockerfile
and theentrypoint.sh
script. - You can rebuild the
app:latest
image as many times as needed. - The server code itself must not be modified.
- A simple
docker run app
command (without any extra arguments) should start the container. - A simple
docker stop
command should stop the container gracefully. - The container name
test
is reserved for the verification process — make sure you use a different container name while debugging.
Good luck!
Hint 1 💡
While debugging, it's usually a good idea to isolate the problematic part of the system as much as possible.
Will the container stop gracefully if the entrypoint script is removed?
Will it stop gracefully if the entrypoint script is kept as-is,
but the server is replaced with a simple CLI command (e.g. sleep 999
)?
Hint 2 💡
When the docker stop
command is executed, it sends a signal to the container (SIGTERM
by default).
Does the application (main.go
) know how to handle this signal gracefully?
Hint 3 💡
The docker stop
command sends a signal to the container's main process.
Can you identify the main process of the app:latest
container?
Is it the server itself or the entrypoint script?
Hint 4 💡
When a shell sits in the process tree, signals can easily get lost. Can you find a way to make the container's main process the server itself? But make sure that the entrypoint script is still executed!
Hint 5 💡
By default, when a shell script executes another script of a binary,
a good old fork+exec
trick is performed.
The fork()
system call creates a copy of the current (i.e. the shell) process,
and then the exec()
call replaces the copied process with an image of the new executable.
However, this is not the only option!
Shell scripts can also use the exec
built-in command directly.
It allows replacing the current shell process with the new executable without creating a "forked" process.
For our case, this means that the entrypoint process can be replaced with the server process "in-place" as the very last step of the entrypoint script,
making the server the main process of the container.