Докер-контейнер сразу же выходит из консоли Console.ReadLine() в консольном приложении .net
Я пытаюсь запустить консольное приложение .net core 1.0.0 внутри контейнера докеров.
Когда я запускаю команду dotnet run
изнутри папки Demo на моей машине, она работает нормально; Но при запуске с помощью docker run -d --name demo Demo
контейнер немедленно выйдет.
Я попробовал docker logs demo
проверить журналы, и он просто показывает текст из Console.WriteLine:
Демо-приложение работает...
и ничего больше.
Я загрузил проект в https://github.com/learningdockerandnetcore/Demo
В проекте содержатся Programs.cs
, Dockerfile
, используемые для создания демонстрационного образа и project.json
файла.
Ответы
Ответ 1
Вы должны либо запустить свой контейнер в интерактивном режиме (с опцией -i
). но обратите внимание, что фоновые процессы будут немедленно закрыты при запуске контейнера, поэтому убедитесь, что ваш script запущен на переднем плане или просто не будет работать.
Ответ 2
Если вы переключите свое приложение на целевой .net core 2.0, то вы можете использовать пакет Microsoft.Extensions.Hosting для размещения приложения основной консоли .net с помощью API HostBuilder для запуска/остановки вашего приложения. Его класс ConsoleLifetime будет обрабатывать общий метод запуска/остановки приложения.
Чтобы запустить приложение, вы должны реализовать свой собственный интерфейс IHostedService
или наследовать от класса BackgroundService
, а затем добавить его в контекст хоста в 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);
}
}
Вот пример размещенного сервиса:
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();
}
}
Затем создайте HostBuilder и добавьте сервис и другие компоненты (регистрация, настройка).
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();
}
}
Ответ 3
Единственный способ заставить Docker/Linux сохранить приложение .NET Core - это подделать ASP.NET для размещения его для меня... Это такой уродливый взлом!
Это будет выполняться в Docker с использованием опции docker run -d
, поэтому вам не нужно иметь действующее соединение, чтобы поддерживать поток STDIN живым.
Я создал консольное приложение .NET Core (не приложение ASP.NET), и мой класс Program выглядит следующим образом:
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();
}
}
}
Класс Startup:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IServer, ConsoleAppRunner>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
}
}
Класс 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.
}
}
Единственная приятная вещь в этом - это то, что вы можете использовать DI в вашем приложении (если хотите), поэтому в моем случае я использую ILoggingFactory для обработки моего журналирования.
Редактировать 30 октября 2018 г. Эта публикация все еще кажется популярной - я хотел бы просто указать всем, кто читает мой старый пост, что он сейчас довольно древний. Я основывал его на .NET core 1.1 (который был новым в то время). Вполне вероятно, что если вы используете более новую версию ядра .NET(2.0/2.1 или выше), то, вероятно, существует гораздо лучший способ решения этой проблемы сейчас. Пожалуйста, уделите время просмотру некоторых других постов в этой теме, которые могут быть не столь высоко оценены, как эта, но могут быть более новыми и более свежими -d.
Ответ 4
Я не уверен, почему Console.ReadLine();
не блокирует основной поток при запуске приложения консоли консоли dotnet в отдельном контейнере докеров, но лучшим решением является регистрация ConsoleCancelEventHandler
с событием Console.CancelKeyPress
.
Затем вместо этого вы можете блокировать основной поток с типом Threading WaitHandle
и сигнализировать о выпуске основного потока, когда стрелка Console.CancelKeyPress
запущена.
Хороший пример кода можно найти здесь: https://gist.github.com/kuznero/73acdadd8328383ea7d5
Ответ 5
Вы можете использовать:
Thread.Sleep(Timeout.Infinite);
Смотрите этот ответ:
Это Thread.Sleep(Timeout.Infinite); более эффективен, чем while (true) {}?
Ответ 6
еще один "грязный способ" - запустить вашу программу на экране с помощью
screen -dmS yourprogramm
Ответ 7
Я использую этот подход:
static async Task Main(string[] args)
{
// run code ..
await Task.Run(() => Thread.Sleep(Timeout.Infinite));
}
Ответ 8
Для тех, кто хочет запустить ваше консольное приложение .net 4.x в linux docker без необходимости -i
и хотеть запускать его в фоновом режиме, лучшим решением является пакет mono.posix
, который делает именно то, что мы ищем, слушайте сигналы Linux.
это также относится к WebApi2
с проектами Owin
или к любому console app
для большинства из нас работают контейнеры в фоновом режиме, используя console.read
или ManualResetEventSlim
или AutoResetEvent
обыкновения работали из - за отдельным режим по грузчику.
Лучшим решением является установка Install-Package Mono.Posix
вот пример:
using System;
using Microsoft.Owin.Hosting;
using Mono.Unix;
using Mono.Unix.Native;
public class Program
{
public static void Main(string[] args)
{
string baseAddress = "http://localhost:9000/";
// Start OWIN host
using (WebApp.Start<Startup>(url: baseAddress))
{
Console.ReadLine();
}
if (IsRunningOnMono())
{
var terminationSignals = GetUnixTerminationSignals();
UnixSignal.WaitAny(terminationSignals);
}
else
{
Console.ReadLine();
}
host.Stop();
}
public static bool IsRunningOnMono()
{
return Type.GetType("Mono.Runtime") != null;
}
public static UnixSignal[] GetUnixTerminationSignals()
{
return new[]
{
new UnixSignal(Signum.SIGINT),
new UnixSignal(Signum.SIGTERM),
new UnixSignal(Signum.SIGQUIT),
new UnixSignal(Signum.SIGHUP)
};
}
}
сообщение в блоге с полным исходным кодом: https://dusted.codes/running-nancyfx-ina-docker-container-a-beginners-guide-to-build-and-run-dotnet-applications-in-docker
Ответ 9
Использование Console.ReadLine
вместо этого, похоже, работает.
С#:
do
{
Console.WriteLine($"Type: quit<Enter> to end {Process.GetCurrentProcess().ProcessName}");
}
while (!Console.ReadLine().Trim().Equals("quit",StringComparison.OrdinalIgnoreCase));
F #:
while not (Console.ReadLine().Trim().Equals("quit",StringComparison.OrdinalIgnoreCase)) do
printfn "Type: quit<Enter> to end"