0

I've been trying different approaches to gracefully shutdown my application by reacting to the SIGTERM signal, however I can't seem to make it work.

I've implemented the following service:

public class ShutdownService : IHostedService
{
    private readonly IHostApplicationLifetime _appLifetime;
    private readonly ILogger<ShutdownService> _logger;

    public ShutdownService(IHostApplicationLifetime appLifetime, ILogger<ShutdownService> logger)
    {
        _appLifetime = appLifetime;
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Initializing shutdown service.");
        Console.CancelKeyPress += (sender, eventArgs) =>
        {
            _logger.LogInformation("Ctrl-C detected. Initiating shutdown...");
            _appLifetime.StopApplication();
            eventArgs.Cancel = true;
        };
        _logger.LogInformation("Now listening to Ctrl-C.");
        cancellationToken.Register(() =>
        {
            _logger.LogInformation("SIGTERM detected. Initiating shutdown...");
            _appLifetime.StopApplication();
        });
        AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
        {
           _logger.LogInformation("SIGTERM detected. Initiating shutdown...");
           _appLifetime.StopApplication();
        };

        _logger.LogInformation("Now listening to SIGTERM.");
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Executing graceful shutdown.");
        return Task.CompletedTask;
    }
}

I've registered it in my services:

Host.CreateDefaultBuilder(args)
    .UseDefaultServiceProvider(o => o.ValidateScopes = false)
    .UseSystemd()
    .ConfigureServices((hostContext, services) =>
    {
        services.AddHostedService<ShutdownService>();
    }

In my dockerfile, I'm using a shell script as entrypoint since I have to start multiple applications when the container starts. The script looks like this:

#!/bin/bash
dotnet /App/myGracefulShutdownApp.dll &
pid=$!
dotnet /App2/myOtherApp.dll &
trap 'kill -SIGTERM $pid' SIGTERM SIGINT
wait $pid

I've verified, that the ShutdownService is running, when the container is started. I've verified, that the correct process id gets killed with the trap command when I stop the container.

The container is run as a non-root user, if that matters. I've also tried using the kill command from within the container (to see if the user has the permissions to execute the kill command and it does), but the application still does not execute StopAsync.

I'm happy to provide more information if needed. Thank you for your help already!

cmos
  • 482
  • 4
  • 14
  • To shutdown you need to process ID (PID). Where are you getting the PID? – jdweng Jan 04 '23 at 13:41
  • @jdweng: In the bash script I set the 'pid=$!' wich is the PID of my application. I then wait until the process ends with 'wait $pid' – cmos Jan 04 '23 at 13:43
  • How is that value returned back to the c# code? – jdweng Jan 04 '23 at 13:55
  • It is not. I'm not trying to kill a proces in my application, I want to react to my application being stopped (docker container is stopped) so I can delay the shutdown and run some cleanup code before the container is completely stopped :) – cmos Jan 04 '23 at 14:02
  • Did you see following : https://stackoverflow.com/questions/6546509/detect-when-console-application-is-closing-killed – jdweng Jan 04 '23 at 14:15
  • I have not, however the implementation suggested there needs to use mono which I do not want to use if possible. – cmos Jan 04 '23 at 14:29

1 Answers1

0

Since I still couldn't figure out why my app did not receive the SIGTERM command, I opted to expand my web API with a "Shutdown" method which will then be called with a cURL command when the start script traps the SIGTERM signal.

cmos
  • 482
  • 4
  • 14