.netコア1.0.0コンソールアプリケーションをdockerコンテナー内で実行しようとしています。
自分のマシンのDemoフォルダ内からdotnet run
コマンドを実行すると、正常に動作します。ただし、docker run -d --name demo Demo
を使用して実行すると、コンテナはすぐに終了します。
ログを確認するためにdocker logs demo
を試したところ、Console.WriteLineからのテキストが表示されるだけです。
デモアプリを実行しています...
何もありません。
https://github.com/learningdockerandnetcore/Demo にプロジェクトをアップロードしました
プロジェクトには、デモ画像の作成に使用されるPrograms.cs
、Dockerfile
、およびproject.json
ファイルが含まれています。
コンテナをインタラクティブモードで実行する必要があります(-i
オプション)。ただし、コンテナを実行するとすぐにバックグラウンドプロセスが閉じられるため、スクリプトがフォアグラウンドで実行されていることを確認してください。そうしないと、スクリプトが機能しません。
アプリを.netコア2.0を対象とするように切り替える場合は、 Microsoft.Extensions.Hosting パッケージを使用して、HostBuilder APIを使用して.netコアコンソールアプリケーションをホストし、アプリケーションを起動/停止できます。その 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();
}
}
_
.NET Coreアプリケーションを存続させるためにDocker/Linuxを入手できる唯一の方法は、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();
}
}
}
スタートアップクラス:
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を使用してログを処理しています。
2018年10月30日編集この投稿はまだ人気があるようです-私の古い投稿を読んでいる人には、かなり古いものであることを指摘したいと思います。私はそれを.NETコア1.1(当時は新しい)に基づいていました。新しいバージョンの.NETコア(2.0/2.1以降)を使用している場合は、この問題を解決するためのはるかに優れた方法があると考えられます。このスレッドほど高くはないかもしれませんが、より新しく、より最新である可能性がある、このスレッドの他の投稿をいくつか見てください。
以下を使用できます。
Thread.Sleep(Timeout.Infinite);
この答えを見てください:
Is Thread.Sleep(Timeout.Infinite); while while true(true){}?
分離されたDockerコンテナーでdotnetコアコンソールアプリを実行しているときにConsole.ReadLine();
がメインスレッドをブロックしない理由はわかりませんが、ConsoleCancelEventHandler
を_Console.CancelKeyPress
_イベント。
次に、代わりに、タイプThreading WaitHandle
でメインスレッドをブロックし、_Console.CancelKeyPress
_が発生したときにメインスレッドの解放を通知できます。
良いサンプルコードはここにあります: https://Gist.github.com/kuznero/73acdadd8328383ea7d5
私はこのアプローチを使用しています:
static async Task Main(string[] args)
{
// run code ..
await Task.Run(() => Thread.Sleep(Timeout.Infinite));
}
-i
を指定せずに.net 4.xコンソールアプリをLinux dockerで実行し、バックグラウンドで実行したい場合は、mono.posix
パッケージが最適です。私たちは探して、Linuxのシグナルを聞きます。
これは、Owin
プロジェクトを含むWebApi2
、または基本的にすべてのconsole app
にも適用されます
私たちのほとんどにとって、console.read
またはManualResetEventSlim
またはAutoResetEvent
を使用してバックグラウンドでコンテナーを実行することは、Dockerによる分離モードのため機能しません。
最善の解決策は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)
};
}
}
もう1つの「汚い方法」は、画面でプログラムを起動することです
screen -dmS yourprogramm