Challenge, Medium,  on  ContainersLinux

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 the entrypoint.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!

Container process tree with and without the entrypoint script.
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.

Categories: ContainersLinux
Discussion:  Discord

Level up your Server Side game — Join 9,000 engineers who receive insightful learning materials straight to their inbox