J'essaie d'exécuter une application de console .net core 1.0.0 à l'intérieur d'un conteneur Docker.
Lorsque j'exécute la commande dotnet run
à partir du dossier de démonstration de ma machine, tout fonctionne correctement. Mais lorsqu'il est exécuté avec docker run -d --name demo Demo
, le conteneur se ferme immédiatement.
J'ai essayé docker logs demo
pour vérifier les journaux et il ne montre que le texte de la Console.WriteLine:
Application de démonstration en cours d'exécution ...
et rien d'autre.
J'ai téléchargé le projet sur https://github.com/learningdockerandnetcore/Demo
Le projet contient Programs.cs
, Dockerfile
utilisé pour créer une image de démonstration et le fichier project.json
.
Vous devez soit exécuter votre conteneur en mode interactif (avec l'option -i
). mais notez que les processus en arrière-plan seront fermés immédiatement lorsque vous exécuterez le conteneur, assurez-vous donc que votre script est exécuté au premier plan, sinon il ne fonctionnera tout simplement pas.
Si vous basculez votre application vers le noyau 2.0. Cible, vous pouvez utiliser Microsoft.Extensions.Hosting pacakge pour héberger une application console .net en utilisant l'API HostBuilder pour démarrer/arrêter votre application. Sa ConsoleLifetime class traiterait la méthode générale de démarrage/arrêt des applications.
Pour exécuter votre application, vous devez implémenter votre propre interface IHostedService
ou hériter de la classe BackgroundService
class, puis l'ajouter au contexte de l'hôte dans ConfigureServices
.
namespace Microsoft.Extensions.Hosting
{
//
// Summary:
// Defines methods for objects that are managed by the Host.
public interface IHostedService
{
// Summary:
// Triggered when the application Host is ready to start the service.
Task StartAsync(CancellationToken cancellationToken);
// Summary:
// Triggered when the application Host is performing a graceful shutdown.
Task StopAsync(CancellationToken cancellationToken);
}
}
Voici un exemple de service hébergé:
public class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
Ensuite, créez HostBuilder et ajoutez le service et d’autres composants (journalisation, configuration).
public class Program
{
public static async Task Main(string[] args)
{
var hostBuilder = new HostBuilder()
// Add configuration, logging, ...
.ConfigureServices((hostContext, services) =>
{
// Add your services with depedency injection.
});
await hostBuilder.RunConsoleAsync();
}
}
La seule façon pour moi de garder Docker/Linux pour garder mon application .NET Core vivante était de corrompre ASP.NET pour l’héberger pour moi… C’est un tel bidouillage!
Cette procédure s'exécutera dans Docker à l'aide de l'option docker run -d
, de sorte que vous n'avez pas besoin d'une connexion en direct pour maintenir le flux STDIN actif.
J'ai créé une application console .NET Core (pas une application ASP.NET) et ma classe de programme ressemble à ceci:
public class Program
{
public static ManualResetEventSlim Done = new ManualResetEventSlim(false);
public static void Main(string[] args)
{
//This is unbelievably complex because .NET Core Console.ReadLine() does not block in a docker container...!
var Host = new WebHostBuilder().UseStartup(typeof(Startup)).Build();
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;
};
Host.Run(cts.Token);
Done.Set();
}
}
}
La classe de démarrage:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IServer, ConsoleAppRunner>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
}
}
La classe ConsoleAppRunner
public class ConsoleAppRunner : IServer
{
/// <summary>A collection of HTTP features of the server.</summary>
public IFeatureCollection Features { get; }
public ConsoleAppRunner(ILoggerFactory loggerFactory)
{
Features = new FeatureCollection();
}
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
}
/// <summary>Start the server with an application.</summary>
/// <param name="application">An instance of <see cref="T:Microsoft.AspNetCore.Hosting.Server.IHttpApplication`1" />.</param>
/// <typeparam name="TContext">The context associated with the application.</typeparam>
public void Start<TContext>(IHttpApplication<TContext> application)
{
//Actual program code starts here...
Console.WriteLine("Demo app running...");
Program.Done.Wait(); // <-- Keeps the program running - The Done property is a ManualResetEventSlim instance which gets set if someone terminates the program.
}
}
La seule chose agréable à ce sujet est que vous pouvez utiliser DI (si vous le souhaitez) dans votre application. Par conséquent, dans mon cas d'utilisation, j'utilise ILoggingFactory pour gérer ma journalisation.
Edit 30th Oct 2018 Ce message semble toujours être populaire - Je voudrais simplement signaler à tous ceux qui lisent mon ancien message qu’il est maintenant très ancien. Je me basais sur le noyau .NET 1.1 (qui était nouveau à l'époque). Il est probable que si vous utilisez une version plus récente de .NET Core (2.0/2.1 ou supérieure), il existe probablement un moyen beaucoup plus efficace de résoudre ce problème maintenant. Prenez le temps de regarder quelques-uns des autres articles de ce fil qui ne sont peut-être pas aussi bien classés que celui-ci, mais qui pourraient être plus récents et plus récents.
Je ne suis pas sûr de savoir pourquoi Console.ReadLine();
ne bloque pas le thread principal lors de l'exécution d'une application de console centrale dotnet dans un conteneur de menu fixe détaché, mais la meilleure solution consiste à enregistrer un ConsoleCancelEventHandler
avec l'événement Console.CancelKeyPress
.
Ensuite, vous pouvez à la place bloquer le thread principal avec un type de Threading WaitHandle
et signaler la libération du thread principal lorsque Console.CancelKeyPress
est déclenché.
Vous trouverez un bon exemple de code ici: https://Gist.github.com/kuznero/73acdadd8328383ea7d5
une autre "méthode sale" consiste à démarrer votre programme à l’écran en
screen -dmS yourprogramm