Loading...
Willow Media

DotNet Core achtergrond service onder Linux

Dat Microsoft zijn zinnen gezet heeft op crossplatform voor .Net Core, blijkt ook weer uit de ondersteuning van systemd (Linux) in Dotnet Core 3.0. Zo was het voorheen wel mogelijk om een Windows Service te maken en deze via Mono onder Linux aan de praat te krijgen, nu wordt het wel heel makkelijk gemaakt om een achtergrond service te maken.

Via systemd kunnen services gemaakt worden die in de achtergrond draaien en die opstarten als het OS opstart. De output van deze services kan vervolgens redelijk makkelijk bekeken worden. Het 'installeren' van zo'n service gaat via een 'unit file', dat niet meer is dan een tekst bestand is met de benodigde parameters voor de service. Redelijk overwacht lijkt dit erg op de bekende .ini files die veelal onder Windows voorkomen.

Om als een service te kunnen draaien via deze methode, pas ik een commandline applicatie aan. Hiervoor maak ik een CreateHostBuilder aan, waarbij het type bij AddHostedService<T> mijn klasse bevat. Let vooral op de regel UseSystemd, die zorgt voor terugmeldingen aan systemd.

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSystemd()
        .ConfigureServices((hostContext, services) =>
        {
            services.AddHostedService<WatchWorker>();
        });

UseSystemd begrijp ook wanneer een applicatie niet als service gestart wordt, zodat tijdens het onwikkelen ook gewoon gedebugged kan worden. Via Main() wordt de host gestart en zal de 'Worker' opgestart worden:

class Program
{
    static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

De klasse 'WatchWorker' moet hiervoor wel overerven van BackgroundService. Voor de test initieer ik een andere klasse, die het achtergrond proces afhandelt. Uiteindelijk zal ik deze in de worker integreren.

public class WatchWorker : BackgroundService
{
    private readonly ILogger<WatchWorker> _logger;
 
    public WatchWorker(ILogger<WatchWorker> logger)
    {
        _logger = logger;
    }
 
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        Watcher watcher = new Watcher();
        watcher.Watch(stoppingToken);
    }
}

Om dit werkend te krijgen, is het wel nodig om 2 nuget packages toe te voegen:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>netcoreapp3.0</TargetFramework>
<!--        <RuntimeIdentifier>linux-x64</RuntimeIdentifier>-->
<!--        <PublishSingleFile>true</PublishSingleFile>-->
<!--        <PublishTrimmed>true</PublishTrimmed>-->
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.0.1" />
        <PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="3.0.1" />
        <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
    </ItemGroup>
</Project>

Vervolgens publiceer ik de applicatie, upload hem naar de Linux server. Daar maak ik vervolgend de unit file aan:

#nano watcher.service 

[Unit]
Description=my service

[Service]
Type=simple
ExecStart={volledig pad naar executable of script}

[Install]
WantedBy=multi-user.target

Ik maak de unit file aan in hetzelfde pad als waar de executable staat. Zo is hij makkelijk terug te vinden, en eventueel te backuppen. Om de service werkend te krijgen, maak ik een symlink naar de juiste lokatie. Stel dat de unit file "watcher.service" heet, dan zorgen de volgende commando's dat hij gestart wordt (start), de status bekeken wordt (status) en via "enable" zal de service automatisch opstarten als de machine opstart:

chmod 644 watcher.service
ln -s /my/path/to/insight-ci/watcher.service /etc/systemd/system/watcher.service
systemctl start watcher.service
systemctl status watcher.service
systemctl enable watcher

Om de service te monitoren, kan via journalctl de logs ingekeken worden. Via -f (zie tail) kan je de logfile blijven volgen:

journalctl -f -u watcher

Als alternatief kan je ook de syslog log bekijken:

tail /var/log/syslog -f -n 999 |grep -i watcher

Self contained

Aansluitend aan dit verhaal, is dat een DotNet Core applicatie als "self contained" gepubliceerd kan worden. Dit betekend niks meer dan dat het als een eigen executable wordt gepubliceerd, die de DotNet runtime niet nodig heeft. Deze zit er namelijk bij in. Een voordeel is dat, in mijn geval, deze direct onder Linux uit te voeren is, op een machine zonder de aanwezigheid van 'dotnet'. Let wel dat de applicatie niet echt klein is. In mijn geval is hij al 48Mb, terwijl hij niet heel veel code bevat.

dotnet publish -c release -r linux-x64 --self-contained  

Bronnen:

  • dotnet
  • Linux
  • systemd