Contrôler la durée de vie de l’application de console .NET Core hébergée dans le docker

Déni de responsabilité – il s’agit presque de la même question, car le conteneur docker se ferme immédiatement même avec Console.ReadLine () dans une application de console .net core – mais je ne pense pas que la réponse acceptée sur cette question soit satisfaisante.

Ce que j’essaie de réaliser
Je construis une application console (c’est un service HTTP utilisant ServiceStack) qui est construite avec .NET core (dnxcore50 – il s’agit d’une application console, pas d’une application ASP.NET). Je lance cette application dans un conteneur Docker sur une machine Linux. C’est ce que j’ai fait et le service HTTP fonctionne.

Mon problème
Ayant déclaré que «mon service fonctionne» – et cela se produit, il existe un problème d’hébergement du service dans un conteneur de docker. J’utilise Console.ReadLine() après avoir démarré mon écouteur HTTP, mais ce code ne bloque pas dans le conteneur docker et le conteneur se ferme immédiatement après le démarrage. Je peux démarrer le conteneur docker en mode “interactif”, et le service restra là à écouter jusqu’à ce que je tue la session interactive et que le conteneur se ferme.

Code pour Repo
Le code ci-dessous est une liste complète de codes pour la création de mon application de test .NET core servicestack console.

 public class Program { public static void Main(ssortingng[] args) { new AppHost().Init().Start("http://*:8088/"); Console.WriteLine("listening on port 8088"); Console.ReadLine(); } } public class AppHost : AppSelfHostBase { // Initializes your AppHost Instance, with the Service Name and assembly containing the Services public AppHost() : base("My Test Service", typeof(MyTestService).GetAssembly()) { } // Configure your AppHost with the necessary configuration and dependencies your App needs public override void Configure(Container container) { } } public class MyTestService: Service { public TestResponse Any(TestRequest request) { ssortingng message = ssortingng.Format("Hello {0}", request.Name); Console.WriteLine(message); return new TestResponse {Message = message}; } } [Api("Test method")] [Route("/test/{Name}", "GET", Summary = "Get Message", Notes = "Gets a message incorporating the passed in name")] public class TestRequest : IReturn { [ApiMember(Name = "Name", Description = "Your Name", ParameterType = "path", DataType = "ssortingng")] public ssortingng Name { get; set; } } public class TestResponse { [ApiMember(Name = "Message", Description = "A Message", ParameterType = "path", DataType = "ssortingng")] public ssortingng Message { get; set; } } 

L’ancienne façon de résoudre ce problème
Donc, ayant précédemment hébergé en utilisant Mono (Mono avait de sérieux problèmes de performances – d’où le passage à .NET core) – la façon de résoudre ce problème était d’utiliser Mono.Posix pour écouter un signal de Mono.Posix comme celui-ci:

 using Mono.Unix; using Mono.Unix.Native; ... static void Main(ssortingng[] args) { //Start your service here... // check if we're running on mono if (Type.GetType("Mono.Runtime") != null) { // on mono, processes will usually run as daemons - this allows you to listen // for termination signals (ctrl+c, shutdown, etc) and finalize correctly UnixSignal.WaitAny(new[] { new UnixSignal(Signum.SIGINT), new UnixSignal(Signum.SIGTERM), new UnixSignal(Signum.SIGQUIT), new UnixSignal(Signum.SIGHUP) }); } else { Console.ReadLine(); } } 

Maintenant – je comprends que cela ne fonctionnera pas pour .NET Core (évidemment parce que Mono.Posix est pour Mono!)

La solution décrite dans l’article connexe (en haut de ce billet) ne me sert à rien – dans un environnement de production, je ne peux pas espérer conserver un conteneur Docker en veillant à ce qu’une session interactive soit disponible pour conserver Console.ReadLine. travailler parce qu’il y a un stream STD-IN là-bas …

Existe-t-il un autre moyen de conserver mon conteneur Docker en vie (en utilisant l’option -d (détaché) lors de l’appel de docker run ) lors de l’hébergement d’une application .NET Core?

Refactor de code dans le cadre de la suggestion Mythz

  public static void Main(ssortingng[] args) { Run(new AppHost().Init(), "http://*:8088/"); } public static void Run(ServiceStackHost host, params ssortingng[] uris) { AppSelfHostBase appSelfHostBase = (AppSelfHostBase)host; using (IWebHost webHost = appSelfHostBase.ConfigureHost(new WebHostBuilder(), uris).Build()) { ManualResetEventSlim done = new ManualResetEventSlim(false); using (CancellationTokenSource cts = new CancellationTokenSource()) { Action shutdown = () => { if (!cts.IsCancellationRequested) { Console.WriteLine("Application is shutting down..."); cts.Cancel(); } done.Wait(); }; Console.CancelKeyPress += (sender, eventArgs) => { shutdown(); // Don't terminate the process immediately, wait for the Main thread to exit gracefully. eventArgs.Cancel = true; }; Console.WriteLine("Application started. Press Ctrl+C to shut down."); webHost.Run(cts.Token); done.Set(); } } } 

Solution finale!

Pour Posterity – la solution que j’ai utilisée est le code qui peut être trouvé ici (Merci à Myths pour des précisions): https://github.com/NetCoreApps/Hello/blob/master/src/SelfHost/Program.cs

Repo du code correspondant:

 public static void Main(ssortingng[] args) { var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup() .UseUrls("http://*:8088/") .Build(); host.Run(); } } public class Startup { // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // app.UseStaticFiles(); app.UseServiceStack(new AppHost()); app.Run(context => { context.Response.Redirect("/metadata"); return Task.FromResult(0); }); } 

Dans NuGet, j’ai installé Microsoft.NETCore.App, ServiceStack.Core et ServiceStack.Kestrel.

Si vous allez héberger des applications .NET Core dans Docker, je vous recommande de suivre simplement l’ API d’hébergement .NET Core standard où il appelle IWebHost.Run() pour bloquer le thread principal et conserver l’application console.

AppHostSelfBase est juste une enveloppe autour de l’API d’hébergement de .NET Core mais appelle à la place l’ IWebHost.Start() non bloquant. Pour obtenir le comportement de IWebHost.Run() vous devriez pouvoir réutiliser la même approche de ManualResetEventSlim et Console.CancelKeyPress que l’ implémentation de WebHost.Run () utilise , mais personnellement, il est plus simple d’utiliser l’API d’hébergement de .NET Core et d’appeler Run() et enregistrez simplement votre ServiceStack AppHost en tant que module .NET Core .