Docker のデプロイ

ヒント

Docker や Orleans についてよくご存じでも、Microsoft により対処済みの問題に直面した場合に回避できるように、他の Orleans ドキュメントと同様に最後までお読みになることをお勧めします。

この記事とそのサンプルは進行中の作業です。 どのようなフィードバック、PR、提案でも大歓迎です。

Orleans ソリューションを Docker にデプロイする

Docker オーケストレーターとクラスタリング スタックの設計方法を考えると、Orleans を Docker にデプロイする処理は注意を要する場合があります。 最もわかりにくいのは、Docker Swarm と Kubernetes ネットワーク モデルからの "オーバーレイ ネットワーク" の概念を理解することです。

Docker コンテナーとネットワーク モデルは、主にステートレスで不変のコンテナーを実行するように設計されています。 そのため、node.js または Nginx アプリケーションを実行しているクラスターのスピンアップは非常に簡単です。 ところが、もう少し複雑で、実際のクラスター化または分散アプリケーション (Orleans ベースのアプリケーションなど) のようなものを使用しようとすると、設定に悩むことになります。 可能ではありますが、Web ベースのアプリケーションほど簡単ではありません。

Docker クラスタリングは、複数のホストを、"コンテナー オーケストレーター" を使用して管理されるリソースの単一プールとして動作するようにまとめることで構成されます。 Docker Inc. からは、コンテナー オーケストレーションのオプションとして Swarm が提供され、Google には Kubernetes (別名K8s) があります。 他にも、DC/OSMesos などのオーケストレーターがありますが、このドキュメントでは、より広く使用されている Swarm と K8s について説明します。

Orleans が既にサポートされている任意の場所で実行されるのと同じグレイン インターフェイスと実装が、Docker コンテナーでも実行されます。 Docker コンテナーでアプリケーションを実行できるようにするために特別な考慮事項は必要ありません。

ここで説明する概念は、Orleans の .NET Core と .NET 4.6.1 の両方のフレーバーで使用できますが、Docker と .NET Core のクロスプラットフォームの性質を示すために、.NET Core を使用していることを考慮した例に焦点を当てます。 この記事では、場合により、プラットフォーム固有 (Windows/Linux/OSX) の詳細について言及します。

必須コンポーネント

この記事では、次の前提条件がインストールされていることを前提としています。

  • Docker - Docker4X は、サポートされている主要なプラットフォーム用に使いやすいインストーラを備えています。 Docker エンジンと Docker Swarm も含まれています。
  • Kubernetes (K8s) - コンテナー オーケストレーションの Google のオファー。 Minikube (K8s のローカル デプロイ) とそのすべての依存関係と共に kubectl をインストールするためのガイダンスが含まれています。
  • .NET - .NET のクロスプラットフォーム フレーバー
  • Visual Studio Code (VSCode) - 任意の IDE を使用できます。 VSCode はクロスプラットフォームであるため、すべてのプラットフォームで確実に動作するように使用しています。 VSCode をインストールしたら、C# 拡張機能をインストールします。

重要

Kubernetes を使用しない場合は、インストールする必要はありません。 Swarm を使用する場合は、追加のインストールは必要ありません。Docker4X インストーラに既に含まれています。

注意

Windows では、Docker インストーラによって、Hyper-V がインストール プロセスで有効にされます。 この記事とその例では .NET Core を使用しています。使用されるコンテナー イメージは Windows Server NanoServer に基づいています。 .NET Core を使用する予定がなく、.NET 4.6.1 のフル フレームワークを対象とする場合、使用するイメージは Windows Server Core とバージョン 1.4 以上の Orleans (これは .NET フル フレームワークのみをサポートしています) にする必要があります。

Orleans ソリューションを作成する

次の手順は、新しい dotnet ツールを使用して通常の Orleans ソリューションを作成する方法を示しています。

コマンドは、お使いのプラットフォームの該当するものに変更してください。 また、ディレクトリ構造もあくまで提案です。 ニーズに合わせて変更してください。

mkdir Orleans-Docker
cd Orleans-Docker
dotnet new sln
mkdir -p src/OrleansSilo
mkdir -p src/OrleansClient
mkdir -p src/OrleansGrains
mkdir -p src/OrleansGrainInterfaces
dotnet new console -o src/OrleansSilo --framework netcoreapp1.1
dotnet new console -o src/OrleansClient --framework netcoreapp1.1
dotnet new classlib -o src/OrleansGrains --framework netstandard1.5
dotnet new classlib -o src/OrleansGrainInterfaces --framework netstandard1.5
dotnet sln add src/OrleansSilo/OrleansSilo.csproj
dotnet sln add src/OrleansClient/OrleansClient.csproj
dotnet sln add src/OrleansGrains/OrleansGrains.csproj
dotnet sln add src/OrleansGrainInterfaces/OrleansGrainInterfaces.csproj
dotnet add src/OrleansClient/OrleansClient.csproj reference src/OrleansGrainInterfaces/OrleansGrainInterfaces.csproj
dotnet add src/OrleansSilo/OrleansSilo.csproj reference src/OrleansGrainInterfaces/OrleansGrainInterfaces.csproj
dotnet add src/OrleansGrains/OrleansGrains.csproj reference src/OrleansGrainInterfaces/OrleansGrainInterfaces.csproj
dotnet add src/OrleansSilo/OrleansSilo.csproj reference src/OrleansGrains/OrleansGrains.csproj

ここまで行ったことは、ソリューション構造とプロジェクトを作成し、プロジェクト間に参照を追加するための単なる定型コードです。 通常の Orleans プロジェクトと違いはありません。

この記事の執筆時点では、Orleans 2.0 (.NET Core とクロスプラットフォームをサポートする唯一のバージョン) はテクノロジ プレビュー段階であるため、NuGet パッケージは MyGet フィードでホストされ、Nuget.org 公式フィードには公開されていません。 プレビュー NuGet パッケージをインストールするには、MyGet からソース フィードとバージョンを強制する dotnet CLI を使用します。

dotnet add src/OrleansClient/OrleansClient.csproj package Microsoft.Orleans.Core -s https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json -v 2.0.0-preview2-201705020000
dotnet add src/OrleansGrainInterfaces/OrleansGrainInterfaces.csproj package Microsoft.Orleans.Core -s https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json -v 2.0.0-preview2-201705020000
dotnet add src/OrleansGrains/OrleansGrains.csproj package Microsoft.Orleans.Core -s https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json -v 2.0.0-preview2-201705020000
dotnet add src/OrleansSilo/OrleansSilo.csproj package Microsoft.Orleans.Core -s https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json -v 2.0.0-preview2-201705020000
dotnet add src/OrleansSilo/OrleansSilo.csproj package Microsoft.Orleans.OrleansRuntime -s https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json -v 2.0.0-preview2-201705020000
dotnet restore

これで、シンプルな Orleans アプリケーションを実行するための基本の依存関係がすべて整いました。 これまでのところ、通常の Orleans アプリケーションから何も変更されていないことに注目してください。 次は、それを使って何かを実行できるようにコードを追加しましょう。

Orleans アプリケーションを実装する

VSCode を使用していると仮定して、ソリューション ディレクトリから code . を実行します。 このディレクトリが VSCode で開き、ソリューションが読み込まれます。

これは、先ほど作成したソリューション構造です。

Visual Studio Code: Explorer with Program.cs selected.

また、Program.csOrleansHostWrapper.csIGreetingGrain.csGreetingGrain.cs ファイルをそれぞれインターフェイスおよびグレイン プロジェクトに追加しました。これらのファイルのコードを次に示します。

IGreetingGrain.cs:

using System;
using System.Threading.Tasks;
using Orleans;

namespace OrleansGrainInterfaces
{
    public interface IGreetingGrain : IGrainWithGuidKey
    {
        Task<string> SayHello(string name);
    }
}

GreetingGrain.cs:

using System;
using System.Threading.Tasks;
using OrleansGrainInterfaces;

namespace OrleansGrains
{
    public class GreetingGrain : Grain, IGreetingGrain
    {
        public Task<string> SayHello(string name)
        {
            return Task.FromResult($"Hello from Orleans, {name}");
        }
    }
}

OrleansHostWrapper.cs:

using System;
using System.NET;
using Orleans.Runtime;
using Orleans.Runtime.Configuration;
using Orleans.Runtime.Host;

namespace OrleansSilo;

public class OrleansHostWrapper
{
    private readonly SiloHost _siloHost;

    public OrleansHostWrapper(ClusterConfiguration config)
    {
        _siloHost = new SiloHost(Dns.GetHostName(), config);
        _siloHost.LoadOrleansConfig();
    }

    public int Run()
    {
        if (_siloHost is null)
        {
            return 1;
        }

        try
        {
            _siloHost.InitializeOrleansSilo();

            if (_siloHost.StartOrleansSilo())
            {
                Console.WriteLine(
                    $"Successfully started Orleans silo '{_siloHost.Name}' as a {_siloHost.Type} node.");
                return 0;
            }
            else
            {
                throw new OrleansException(
                    $"Failed to start Orleans silo '{_siloHost.Name}' as a {_siloHost.Type} node.");
            }
        }
        catch (Exception exc)
        {
            _siloHost.ReportStartupError(exc);
            Console.Error.WriteLine(exc);

            return 1;
        }
    }

    public int Stop()
    {
        if (_siloHost is not null)
        {
            try
            {
                _siloHost.StopOrleansSilo();
                _siloHost.Dispose();
                Console.WriteLine($"Orleans silo '{_siloHost.Name}' shutdown.");
            }
            catch (Exception exc)
            {
                siloHost.ReportStartupError(exc);
                Console.Error.WriteLine(exc);

                return 1;
            }
        }
        return 0;
    }
}

Program.cs (Silo):

using System;
using System.Collections.Generic;
using System.Linq;
using System.NET;
using System.Threading.Tasks;
using Orleans.Runtime.Configuration;

namespace OrleansSilo
{
    public class Program
    {
        private static OrleansHostWrapper s_hostWrapper;

        static async Task<int> Main(string[] args)
        {
            int exitCode = await InitializeOrleansAsync();

            Console.WriteLine("Press Enter to terminate...");
            Console.ReadLine();

            exitCode += ShutdownSilo();

            return exitCode;
        }

        private static int InitializeOrleansAsync()
        {
            var config = new ClusterConfiguration();
            config.Globals.DataConnectionString =
                "[AZURE STORAGE CONNECTION STRING HERE]";
            config.Globals.DeploymentId = "Orleans-Docker";
            config.Globals.LivenessType =
                GlobalConfiguration.LivenessProviderType.AzureTable;
            config.Globals.ReminderServiceType =
                GlobalConfiguration.ReminderServiceProviderType.AzureTable;
            config.Defaults.PropagateActivityId = true;
            config.Defaults.ProxyGatewayEndpoint =
                new IPEndPoint(IPAddress.Any, 10400);
            config.Defaults.Port = 10300;
            var ips = await Dns.GetHostAddressesAsync(Dns.GetHostName());
            config.Defaults.HostNameOrIPAddress =
                ips.FirstOrDefault()?.ToString();

            s_hostWrapper = new OrleansHostWrapper(config);
            return hostWrapper.Run();
        }

        static int ShutdownSilo() =>
            s_hostWrapper?.Stop() ?? 0;
    }
}

Program.cs (Client):

using System;
using System.NET;
using System.Threading;
using System.Threading.Tasks;
using Orleans;
using Orleans.Runtime.Configuration;
using OrleansGrainInterfaces;

namespace OrleansClient
{
    class Program
    {
        private static IClusterClient s_client;
        private static bool s_running;

        static async Task Main(string[] args)
        {
            await InitializeOrleansAsync();

            Console.ReadLine();

            s_running = false;
        }

        static async Task InitializeOrleansAsync()
        {
            var config = new ClientConfiguration
            {
                DeploymentId = "Orleans-Docker";
                PropagateActivityId = true;
            };
            var hostEntry =
                await Dns.GetHostEntryAsync("orleans-silo");
            var ip = hostEntry.AddressList[0];
            config.Gateways.Add(new IPEndPoint(ip, 10400));

            Console.WriteLine("Initializing...");

            using client = new ClientBuilder().UseConfiguration(config).Build();
            await client.Connect();
            s_running = true;
            Console.WriteLine("Initialized!");

            var grain = client.GetGrain<IGreetingGrain>(Guid.Empty);

            while (s_running)
            {
                var response = await grain.SayHello("Gutemberg");
                Console.WriteLine($"[{DateTime.UtcNow}] - {response}");

                await Task.Delay(1000);
            }
        }
    }
}

粒度の実装の詳細はこの記事の範囲外であるため、ここでは説明しません。 その他の関連ドキュメントを確認してください。 これらのファイルは基本的に最小の Orleans アプリケーションであり、この記事の残りを進めるために、これから始めることにします。

この記事では、OrleansAzureUtils メンバーシップ プロバイダーを使用していますが、Orleans で既にサポートされているその他のプロバイダーを使用することもできます。

Dockerfile

コンテナーを作成するために、Docker ではイメージが使用されます。 独自のものを作成する方法の詳細については、Docker のドキュメントを参照してください。 この記事では、公式の Microsoft イメージを使用します。 ターゲット プラットフォームと開発プラットフォームに基づいて、適切なイメージを選択する必要があります。 この記事では、Linux ベースのイメージである microsoft/dotnet:1.1.2-sdk を使用しています。 たとえば、Windows には microsoft/dotnet:1.1.2-sdk-nanoserver を使用できます。 ニーズに合ったものを選びます。

注 (Windows ユーザー向け): 前述したように、クロスプラットフォームであるために、この記事では .NET Core と Orleans Technical Preview 2.0 を使用しています。 フル リリースされた Orleans1.4 以降で、Windows 上で Docker を使用する場合は、NanoServer と Linux ベースのイメージが .NET Core のみをサポートしているため、Windows Server Core に基づいたイメージを使用する必要があります。

Dockerfile.debug:

FROM microsoft/dotnet:1.1.2-sdk
ENV NUGET_XMLDOC_MODE skip
WORKDIR /vsdbg
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        unzip \
    && rm -rf /var/lib/apt/lists/* \
    && curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg
WORKDIR /app
ENTRYPOINT ["tail", "-f", "/dev/null"]

基本的には、この Dockerfile を使用して、VSdbg デバッガーをダウンロードしてインストールし、空のコンテナーを起動して、永久に動作させることで、デバッグ中にばらしたり、破棄したりする必要がないようにします。

運用環境では、イメージには .NET Core ランタイムのみが含まれ、SDK 全体は含まないため、イメージが小さくなり、dockerfile も少しシンプルになります。

Dockerfile:

FROM microsoft/dotnet:1.1.2-runtime
WORKDIR /app
ENTRYPOINT ["dotnet", "OrleansSilo.dll"]
COPY . /app

docker-compose ファイル

docker-compose.yml ファイルには基本的に (プロジェクト内で) 一連のサービスとその依存関係をサービス レベルで定義します。 各サービスには、Dockerfile で選択したイメージに基づいて、特定のコンテナーの 1 つ以上のインスタンスを含めます。 docker-compose の詳細については、docker-compose のドキュメントを参照してください。

Orleans デプロイの場合、2 つのサービスが含まれる docker-compose.yml を用意するのが、一般的なユース ケースです。 1 つは Orleans サイロ用、もう 1 つは Orleans Client 用です。 Client は Silo に依存します。つまり、Silo サービスが起動した後にのみ開始されます。 もう 1 つのケースは、たとえば SQL Server のようなストレージ、データベース サービス、コンテナーを追加することです。クライアントとサイロの前に最初に開始する必要があり、両方のサービスがそれに依存する必要があります。

注意

読み進める前に、docker-compose ファイル内の "インデント" が重要であることに注目してください。 何か問題がある場合は、それに注意します。

ここでは、この記事で使用するサービスについて説明します。

docker-compose.override.yml (デバッグ):

version: '3.1'

services:
  orleans-client:
    image: orleans-client:debug
    build:
      context: ./src/OrleansClient/bin/PublishOutput/
      dockerfile: Dockerfile.Debug
    volumes:
      - ./src/OrleansClient/bin/PublishOutput/:/app
      - ~/.nuget/packages:/root/.nuget/packages:ro
    depends_on:
      - orleans-silo
  orleans-silo:
    image: orleans-silo:debug
    build:
      context: ./src/OrleansSilo/bin/PublishOutput/
      dockerfile: Dockerfile.Debug
    volumes:
      - ./src/OrleansSilo/bin/PublishOutput/:/app
      - ~/.nuget/packages:/root/.nuget/packages:ro

docker-compose.yml (運用環境):

version: '3.1'

services:
  orleans-client:
    image: orleans-client
    depends_on:
      - orleans-silo
  orleans-silo:
    image: orleans-silo

運用環境では、ローカル ディレクトリをマップせず、build: アクションもありません。 その理由は、運用環境では、イメージをビルドして、独自の Docker レジストリにプッシュする必要があるからです。

すべてをまとめる

Orleans アプリケーションの実行に必要な可動部分がすべて完成したので、Docker 内でこの Orleans ソリューションを実行できるようにまとめます。

重要

次のコマンドは、ソリューション ディレクトリから実行される必要があります。

まず、このソリューションからすべての NuGet パッケージを復元することを確認しましょう。 これは 1 回だけ行う必要があります。 プロジェクトでパッケージの依存関係を変更する場合にのみ、再実行する必要があります。

dotnet restore

次に、dotnet CLI を通常どおり使用してソリューションをビルドして、出力ディレクトリに公開しましょう。

dotnet publish -o ./bin/PublishOutput

ヒント

Orleans で動的に読み込まれるアセンブリの問題を回避するために、ここでは、ビルドではなく、publish を使用しています。 より良い解決策を引き続き探しております。

アプリケーションをビルドして発行したら、Dockerfile イメージをビルドする必要があります。 この手順はプロジェクトごとに 1 回だけ実行する必要があり、Dockerfile、docker-compose を変更した場合や、何らかの理由でローカル イメージ レジストリをクリーンアップした場合にのみ再度実行する必要があります。

docker-compose build

Dockerfiledocker-compose.yml の両方で使用されるイメージはすべて、レジストリからプルされ、開発用コンピューターにキャッシュされます。 イメージがビルドされ、実行の準備が整いました。

それでは、実行してみましょう。

# docker-compose up -d
Creating network "orleansdocker_default" with the default driver
Creating orleansdocker_orleans-silo_1 ...
Creating orleansdocker_orleans-silo_1 ... done
Creating orleansdocker_orleans-client_1 ...
Creating orleansdocker_orleans-client_1 ... done
#

ここで、docker-compose ps を実行すると、orleansdocker プロジェクトに対して 2 つのコンテナーが実行されていることがわかります。

# docker-compose ps
             Name                     Command        State   Ports
------------------------------------------------------------------
orleansdocker_orleans-client_1   tail -f /dev/null   Up
orleansdocker_orleans-silo_1     tail -f /dev/null   Up

注意

Windows を使用していて、コンテナーがベースとして Windows イメージを使用している場合、Command 列には、*NIX システムの tail に対して相対の PowerShell コマンドが表示されるため、コンテナーは同じ方法で維持されます。

これでコンテナーが起動したので、Orleans アプリケーションを起動するたびにコンテナーを停止する必要はありません。 必要なのは、IDE を統合して、docker-compose.yml で事前にマップされコンテナー内でアプリケーションをデバッグすることです。

Scaling

作成プロジェクトを実行したら、docker-compose scale コマンドを使用してアプリケーションを簡単にスケールアップまたはスケールダウンできます。

# docker-compose scale orleans-silo=15
Starting orleansdocker_orleans-silo_1 ... done
Creating orleansdocker_orleans-silo_2 ...
Creating orleansdocker_orleans-silo_3 ...
Creating orleansdocker_orleans-silo_4 ...
Creating orleansdocker_orleans-silo_5 ...
Creating orleansdocker_orleans-silo_6 ...
Creating orleansdocker_orleans-silo_7 ...
Creating orleansdocker_orleans-silo_8 ...
Creating orleansdocker_orleans-silo_9 ...
Creating orleansdocker_orleans-silo_10 ...
Creating orleansdocker_orleans-silo_11 ...
Creating orleansdocker_orleans-silo_12 ...
Creating orleansdocker_orleans-silo_13 ...
Creating orleansdocker_orleans-silo_14 ...
Creating orleansdocker_orleans-silo_15 ...
Creating orleansdocker_orleans-silo_6
Creating orleansdocker_orleans-silo_5
Creating orleansdocker_orleans-silo_3
Creating orleansdocker_orleans-silo_2
Creating orleansdocker_orleans-silo_4
Creating orleansdocker_orleans-silo_9
Creating orleansdocker_orleans-silo_7
Creating orleansdocker_orleans-silo_8
Creating orleansdocker_orleans-silo_10
Creating orleansdocker_orleans-silo_11
Creating orleansdocker_orleans-silo_15
Creating orleansdocker_orleans-silo_12
Creating orleansdocker_orleans-silo_14
Creating orleansdocker_orleans-silo_13

数秒後、要求した特定のインスタンス数にスケーリングされたサービスが表示されます。

# docker-compose ps
             Name                     Command        State   Ports
------------------------------------------------------------------
orleansdocker_orleans-client_1   tail -f /dev/null   Up
orleansdocker_orleans-silo_1     tail -f /dev/null   Up
orleansdocker_orleans-silo_10    tail -f /dev/null   Up
orleansdocker_orleans-silo_11    tail -f /dev/null   Up
orleansdocker_orleans-silo_12    tail -f /dev/null   Up
orleansdocker_orleans-silo_13    tail -f /dev/null   Up
orleansdocker_orleans-silo_14    tail -f /dev/null   Up
orleansdocker_orleans-silo_15    tail -f /dev/null   Up
orleansdocker_orleans-silo_2     tail -f /dev/null   Up
orleansdocker_orleans-silo_3     tail -f /dev/null   Up
orleansdocker_orleans-silo_4     tail -f /dev/null   Up
orleansdocker_orleans-silo_5     tail -f /dev/null   Up
orleansdocker_orleans-silo_6     tail -f /dev/null   Up
orleansdocker_orleans-silo_7     tail -f /dev/null   Up
orleansdocker_orleans-silo_8     tail -f /dev/null   Up
orleansdocker_orleans-silo_9     tail -f /dev/null   Up

重要

これらの例の Command 列には、デバッガー コンテナーを使用しているため、tail コマンドが表示されています。 運用環境の場合は、たとえば dotnet OrleansSilo.dll が表示されます。

Docker Swarm

Docker クラスタリング スタックは Swarm と呼ばれます。詳細については、Docker Swarm を参照してください。

Swarm クラスターでこの記事を実行するために、追加の作業はありません。 Swarm ノードで docker-compose up -d を実行すると、構成されたルールに基づいてコンテナーがスケジュールされます。 同じことが、Docker DatacenterAzure ACS (Swarm モード)、AWS ECS Container Service など、Swarm ベースの他のサービスにも適用されます。 必要なのは、Docker 化されたOrleans アプリケーションをデプロイする前に Swarm クラスターをデプロイすることだけです。

注意

stackdeploycompose v3 を既にサポートしている Swarm モードで Docker エンジンを使用している場合、ソリューションをデプロイするためのより良い方法は docker stack deploy -c docker-compose.yml <name> になります。 Docker エンジンをサポートするには v3 compose ファイルが必要であり、Azure や AWS などのホストされるサービスの大半ではまだ v2 以前のエンジンが使用されていることに留意してください。

Google Kubernetes (K8s)

Kubernetes を使用して Orleans をホストする予定の場合、OrleansContrib\Orleans.Clustering.Kubernetes にコミュニティが管理するクラスタリング プロバイダーがあり、そこで、プロバイダーを使用して Kubernetes で Orleans をシームレスにホストする方法に関するドキュメントとサンプルを見つけることができます。

コンテナー内の Orleans をデバッグする

コンテナーで Orleans を最初から実行する方法がわかったので、Docker で最も重要な原則の 1 つを活用することをお勧めします。 コンテナーは不変です。 そして、開発環境と運用環境に (ほぼ) 同じイメージ、依存関係、ランタイムを持つ必要があります。 こうすることで、"私のコンピューターでは動くのに!" という懐かしい台詞を二度と口にすることがなくなります。 それを可能にするには、コンテナーの "内側" で開発する方法が必要で、これにはコンテナー内のアプリケーションにデバッガーをアタッチすることが含まれます。

複数のツールを使用してこれを実現するには、複数の方法があります。 いくつか評価した結果、この記事の執筆時点で、見た目がよりシンプルで、アプリケーションの邪魔にならないものを選ぶことにしました。

この記事で前述したように、サンプルの開発に VSCode を使用しているため、ここでは、コンテナー内の Orleans アプリケーションにデバッガーをアタッチする方法を示します。

まず、ソリューションの .vscode ディレクトリ内の 2 つのファイルを変更します。

tasks.json:

{
    "version": "0.1.0",
    "command": "dotnet",
    "isShellCommand": true,
    "args": [],
    "tasks": [
        {
            "taskName": "publish",
            "args": [
                "${workspaceRoot}/Orleans-Docker.sln", "-c", "Debug", "-o", "./bin/PublishOutput"
            ],
            "isBuildCommand": true,
            "problemMatcher": "$msCompile"
        }
    ]
}

基本的に、このファイルを使用して、プロジェクトをビルドするたびに、先ほど手動で行ったように publish コマンドを実行するように VSCode に指示します。

launch.json:

{
   "version": "0.2.0",
   "configurations": [
        {
            "name": "Silo",
            "type": "coreclr",
            "request": "launch",
            "cwd": "/app",
            "program": "/app/OrleansSilo.dll",
            "sourceFileMap": {
                "/app": "${workspaceRoot}/src/OrleansSilo"
            },
            "pipeTransport": {
                "debuggerPath": "/vsdbg/vsdbg",
                "pipeProgram": "/bin/bash",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "-c",
                    "docker exec -i orleansdocker_orleans-silo_1 /vsdbg/vsdbg --interpreter=vscode"
                ]
            }
        },
        {
            "name": "Client",
            "type": "coreclr",
            "request": "launch",
            "cwd": "/app",
            "program": "/app/OrleansClient.dll",
            "sourceFileMap": {
                "/app": "${workspaceRoot}/src/OrleansClient"
            },
            "pipeTransport": {
                "debuggerPath": "/vsdbg/vsdbg",
                "pipeProgram": "/bin/bash",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "-c",
                    "docker exec -i orleansdocker_orleans-client_1 /vsdbg/vsdbg --interpreter=vscode"
                ]
            }
        }
    ]
}

これで、VSCode からソリューションをビルドするだけで (発行され)、Silo と Client の両方を開始できます。 実行中の docker-compose サービス インスタンスまたはコンテナーに docker exec コマンドが送信され、アプリケーションにデバッガーが起動されます。それだけです。 デバッガーをコンテナーにアタッチし、それをローカルで実行されている Orleans アプリケーションのように使用します。 ここで異なるのは、コンテナー内にあることです。完了したら、コンテナーをレジストリに発行し、運用環境の Docker ホストにプルするだけで済みます。