but I'm a bit curious about how the official images solves the problem....
Don't forget that Docker 1.13 comes with tini
, originally from krallin/tini
.
Any image run with docker run --init
would include an init
inside the container that forwards signals and reaps processes.
As mentioned here, tini
works transparently, Dockerfiles don't need to be modified in any way.
So, In Unix systems, when you press CTRL+C
in a terminal, a SIGINT
(Signal Interrupt) is sent to the foreground process group, which in this case is the Docker container. If you use CTRL+\
, it sends SIGQUIT
, and if you use CTRL+Z
, it sends SIGTSTP
(Signal Stop).
Docker containers run a single main process, and this process runs in PID 1.
PID 1 is special on Linux: it is the first process that runs and is the ancestor of all other processes.
It also has a special relationship with Unix signals: it is the only process that can choose to ignore SIGINT
and SIGTERM
. Other processes cannot ignore these signals, but they can have handlers that execute when they receive them.
In Docker, when you use CMD
to specify the process to run, Docker will wrap that process with a small init
system (like tini
), which properly handles Unix signals and forwards them to the main process (I mentioned it originally here).
This is why your caddy
image, which does not have an ENTRYPOINT
and only has CMD
, can handle the CTRL+C
.
However, when you use ENTRYPOINT
, Docker does not wrap the process with this small init
system, and the process runs as PID 1.
If the process does not have built-in handling for SIGINT
or SIGTERM
, it will not respond to CTRL+C
. This is why your mariadb
image, which has an ENTRYPOINT
, does not stop when you press CTRL+C
.
The handling of a kill signal is an important part of a graceful shutdown. When a process receives a SIGTERM
(or SIGINT
), it should stop accepting new work, finish its current work, and then exit. This allows it to clean up any resources it is using and ensure data consistency.
If the process does not handle these signals and is simply killed (with SIGKILL
), it does not have a chance to clean up and may leave resources in an inconsistent state. This could be harmful in the case of a database like MariaDB, which could have uncommitted transactions or partially written data.
To support the handling of a kill signal in a Docker entrypoint, you can:
Add signal handling to the entrypoint script itself. This requires modifying the script and might not be feasible if the entrypoint is a binary.
Use an init
system that can handle signals and forward them to the main process. There are small init
systems like tini
that are designed for this purpose. You can use them in your Dockerfile like this:
FROM your-base-image
RUN apt-get update && apt-get install -y tini
ENTRYPOINT ["/usr/bin/tini", "--", "your-command"]
This will run tini
as PID 1, which will handle signals and forward them to your command.
Use Docker's built-in init, which is a minimal tini
implementation. You can enable it with the --init
option when you run your container:
docker run --init -it --rm -p 3306:3306 -e MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=true mariadb:latest
This will run Docker's built-in init as PID 1, which will handle signals and forward them to MariaDB.
These methods ensure that your Docker entrypoint can handle kill signals and perform a graceful shutdown when necessary.
Is there any reason NOT to support the handling of a kill signal?
Not sure, unless you want your image to be really:
- simple, for a very simple or short-lived processes, with no need of signal handling
- fast, to avoid any shutdown signals during critical sections of their code
- resilient, when you want to prevent unwanted or unauthorized shutdown.
But generally, ignoring termination signals can lead to issues like data loss, resource leakage, or zombie processes, and should be avoided.
The only thing left is: if not supporting the shutdown signal is somehow dangerous for service like MariaDB, why MariaDB itself is not supporting it directly in the entrypoint?
This could be for a legacy reason, inherited from MySQL, which MariaDB is a fork of. MySQL was developed before Docker existed, and in a traditional server environment, it's less common for a process to receive a SIGINT
. It's more common for a process to receive a SIGTERM
when the system is shutting down, which MySQL and MariaDB do handle.
Its entrypoint reflects that, and you run it with --init
to ensure a basic SIGINT
support.
But keep in mind it is not the only issue. I made the mistake of running a MariaDB instance on an Azure ACI (container) instead of AKS (Kubernetes).
When an ACI closes (at least when I used it in 2021), it sends... a SIGKILL (a signal which cannot be caught, blocked, or ignored). When the kernel sends a SIGKILL to a process, that process is immediately terminated, and it doesn't get a chance to clean up or do any other work before it's killed.
So, independently of whether your image supports graceful shutdown, be mindful of your execution environment, which might not allow any graceful shutdown in the first place.