web-dev-qa-db-ja.com

Dockerでホストされている.NETCoreコンソールアプリケーションの存続期間を制御する

免責事項-これは 。netコアコンソールアプリケーションでConsole.ReadLine()を使用してもdockerコンテナーはすぐに終了します -とほぼ同じ質問ですが、この質問に対する受け入れられた回答は満足のいくものではないと思います。

私が達成しようとしていること
。NETコア(dnxcore50-これはコンソールアプリであり、ASP.NETアプリケーションではありません)で構築されたコンソールアプリケーション(ServiceStackを使用したHTTPサービス)を構築しています。 LinuxマシンのDockerコンテナでこのアプリケーションを実行しています。これを実行すると、HTTPサービスが機能します。

私の問題
「私のサービスは機能します」と言っても、それでも、Dockerコンテナでサービスをホストする際に問題が発生します。 HTTPリスナーの起動後にConsole.ReadLine()を使用していますが、このコードはDockerコンテナー内でブロックされず、コンテナーは起動直後に終了します。 Dockerコンテナを「インタラクティブ」モードで開始できます。インタラクティブセッションを強制終了してコンテナが終了するまで、サービスはそこでリッスンします。

レポのコード
以下のコードは、テスト用の.NETコアサービススタックコンソールアプリケーションを作成するための完全なコードリストです。

public class Program
{
    public static void Main(string[] 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)
    {
        string message = string.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<TestResponse>
{
    [ApiMember(Name = "Name", Description = "Your Name", ParameterType = "path", DataType = "string")]
    public string Name { get; set; }
}

public class TestResponse 
{
    [ApiMember(Name = "Message", Description = "A Message", ParameterType = "path", DataType = "string")]
    public string Message { get; set; }
}

この問題を解決する古い方法
以前にMonoを使用してホストしていた(Monoには重大なパフォーマンスの問題があったため、.NET Coreへの切り替え)-この動作を修正する方法は、Mono.Posixを使用して次のようなkillシグナルをリッスンすることでした。

using Mono.Unix;
using Mono.Unix.Native;

...

static void Main(string[] 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();
        }
    }

さて、これは.NET Coreでは機能しないことを理解しています(明らかに、Mono.PosixはMono用です!)

関連記事(この投稿の上部)で概説されている解決策は私には役に立ちません-実稼働環境では、Console.ReadLineを維持するインタラクティブセッションを利用できるようにすることで、Dockerコンテナーを存続させることは期待できませんそこにSTD-INストリームがあるので動作しています...

.NET CoreアプリケーションをホストするときにDockerコンテナを存続させる別の方法はありますか(-dを呼び出すときにdocker run(デタッチ)オプションを使用)?

Mythz提案の一部としてのコードリファクタリング

 public static void Main(string[] args)
    {
        Run(new AppHost().Init(), "http://*:8088/");
    }

    public static void Run(ServiceStackHost Host, params string[] 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();
            }
        }
    }

最終解決策!

後世のために-私が行った解決策はここにあるコードです(明確化のための神話に感謝します): https://github.com/NetCoreApps/Hello/blob/master/src/SelfHost/Program .cs

関連するコードのリポジトリ:

public static void Main(string[] args)
    {
        var Host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .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);
        });
    }

NuGetには、Microsoft.NETCore.App、ServiceStack.Core、およびServiceStack.Kestrelがインストールされています。

13
Jay

Dockerで.NETCoreアプリをホストする場合は、通常の 。NET Core Hosting API に従って、IWebHost.Run()を呼び出してメインスレッドをブロックし、維持することをお勧めします。コンソールアプリケーションは生きています。

AppHostSelfBaseは単なる 。NET CoreのホスティングAPIのラッパー ですが、代わりに非ブロッキングIWebHost.Start()を呼び出します。 IWebHost.Run()の動作を取得するには、ManualResetEventSlimと_Console.CancelKeyPress_の同じアプローチを再利用できる必要があります WebHost.Run()の実装が使用する =ですが、個人的には、.NET CoreのホスティングAPIを使用して、Run()を呼び出し、 ServiceStackAppHostを.NETCoreモジュールとして登録する の方が簡単です。

10
mythz