Properly terminate crystal-lang service in docker


Production-ready crystal application in docker must process correctly several signals to shutdown properly. There is two commands in docker can be used to stop running container: docker stop and docker kill. First one, docker stop stops a running container by sending it SIGTERM signal, let the main process process it, and after a grace period uses SIGKILL to terminate the application.

In this article I’ll show how to process SIGING (Ctrl-C), SIGTERM and SIGKILL with Crystal-lang.

Download

It’s a part of my sandbox repository, download it here.

Not familiar with crystal-lang? This is the main file with business logic.

Installation

Everything is dockerised, just get docker installed and download git repository.

Usage

For development-mode application run this:

docker build --pull --force-rm \
  -t crystal-signal-trap \
  --file ./Dockerfile . \
&& docker run -ti --rm --name=trap crystal-signal-trap

But most important is to get signals trap working in production-ready docker, which is scratch, busybox or alpine-based. To test it, run following snippet:

docker build --pull --force-rm \
  -t crystal-signal-trap \
  --file ./Dockerfile-production . \
&& docker run -ti --rm --name=trap crystal-signal-trap

Demo

asciicast

I want to process signals separately!

Instead of grouping signals trap as it’s made in the demo app, it is possible just to process any of them:

 Signal::INT.trap do
   # Processing SIGING
 end

More signals?

It is possible to send other signals with docker:

docker kill --signal=SIGHUP  my_container

In this case, Crystal-lang app shall catch that signal: Signal::INT.trap { ... }. Here is the list of known by Crystal-lang signals.


References: