共用方式為


Docker 部署

提示

即使您熟悉 Docker 或 Orleans,還是建議您閱讀本文,以避免因應措施可能遇到的問題。

本文及其範例是進行中的工作。 歡迎使用任何意見反應、提取要求或建議。

將 Orleans 解決方案部署至 Docker

考慮到 Docker 協調器和叢集堆疊的設計方式,將 Orleans 部署至 Docker 可能會很棘手。 最複雜的是了解 Docker Swarm 和 Kubernetes 網路模型重疊網路的概念。

Docker 容器和網路模型的設計目的是要執行大部分無狀態和不可變動的容器。 因此,啟動執行 node.js 或 Nginx 應用程式的叢集相當簡單。 不過,如果您嘗試使用更詳細的項目,例如實際的叢集或分散式應用程式 (例如 Orleans 型應用程式),您最終將無法加以設定。 可以,但不如 Web 型應用程式簡單。

Docker 叢集包含將多個主機組合在一起,以做為單一資源集區,並使用 Container Orchestrator 進行管理。 Docker Inc. 提供 Swarm 做為容器協調流程的選項,而 Google 則是採用 Kubernetes (也稱為 K8s)。 還有其他協調器,例如 DC/OSMesos,但在本文件中,我們將討論 Swarm 和 K8s,因為這些較為廣泛使用。

在 Orleans 已支援的任何位置中執行的相同粒紋介面和實作,也會在 Docker 容器上執行。 不需要任何特殊考量,即可在 Docker 容器中執行您的應用程式。

這裡討論的概念可用於 Orleans 的 .NET Core 和 .NET4.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。 Docker4X 安裝程式已經包含 Swarm,因此不需要額外的安裝即可使用。

注意

在 Windows 上,Docker 安裝程式會在安裝程序中啟用 Hyper-V。 由於本文及其範例是使用 .NET Core,因此所使用的容器映像是以 Windows Server NanoServer 為基礎。 如果您不打算使用 .NET Core,並將目標設為 .NET 4.6.1 完整架構,則所使用的映像應該是 Windows Server Core,以及 Orleans 的 1.4+ 版 (僅支援 .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 封裝,我們將使用 dotnet CLI 強制從 MyGet 取得來源摘要和版本:

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:已選取 Program.cs 的 Explorer。

我們還分別向介面和精細度專案新增了 Program.csOrleansHostWrapper.csIGreetingGrain.cs GreetingGrain.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 (定址接收器):

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 (用戶端):

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 映像。 根據目標和開發平台,您必須挑選適當的映像。 在本文中,我們會使用 microsoft/dotnet:1.1.2-sdk (以 Linux 為基礎的映像)。 例如,您可以針對 Windows 使用 microsoft/dotnet:1.1.2-sdk-nanoserver。 挑選符合您需求的項目。

Windows 使用者的注意事項:如先前所述,針對跨平台,我們將在本文中使用 .NET Core 和 Orleans Technical Preview 2.0。 如果您想要在 Windows 上使用 Docker 搭配完整發行 Orleans 的 1.4+,則必須使用以 Windows Server Core 為基礎的映像,因為 NanoServer 和 Linux 映像僅支援 .NET 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 上選取的映像為基礎。 如需關於 docker-compose 的詳細資料,請參閱 docker-compose 文件

針對 Orleans 部署,常見的使用案例是具有包含兩個服務的 docker-compose.yml。 一個用於 Orleans 定址接收器,另一個則用於 Orleans 用戶端。 用戶端會相依於定址接收器,這表示僅在 Silo 服務啟動之後才會啟動。 另一個案例是新增儲存體/資料庫服務/容器 (例如 SQL Server),這應該在用戶端和定址接收器之前就先啟動,因此這兩個服務都應該與其相依。

注意

進一步閱讀之前,請注意在 docker-compose 檔案中「縮排」很重要。 因此,如果您有任何問題,請加以留意。

以下是我們將說明本文中服務的方式:

docker-compose.override.yml (Debug):

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 (production):

version: '3.1'

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

在生產環境中,我們不會對應本機目錄,也沒有 build: 動作。 原因是在生產環境中,映像應該建置並推送至您自己的 Docker 登錄。

將所有項目放在一起

現在,我們已擁有執行 Orleans 應用程式所需的所有移動元件,我們會將其放在一起,以便可在 Docker 內執行我們的 Orleans 解決方案 (終於!)。

重要

您應該從解決方案目錄執行下列命令。

首先,讓我們確定從解決方案還原所有 NuGet 封裝。 您只需要做一次這個動作。 僅在變更專案的任何封裝相依性時,才需要再次執行此動作。

dotnet restore

現在,讓我們如往常一樣使用 dotnet CLI 來建置解決方案,並將其發佈至輸出目錄:

dotnet publish -o ./bin/PublishOutput

提示

我們在這裡使用 publish 而非組建,以避免在 Orleans 中動態載入的組件發生問題。 我們仍在為其尋找更好的解決方案。

建置並發佈應用程式後,您必須建置 Dockerfile 映像。 此步驟只需針對每個專案執行一次,且僅在您變更 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 映像做為基底,則 [命令] 資料行會顯示 PowerShell 相對於 *NIX 系統上的 tail 命令,使容器保持相同的方式。

現在您已啟動容器,不需要在每次想要啟動 Orleans 應用程式時將其停止。 您只需要整合 IDE,以偵錯先前在 docker-compose.yml 中所對應容器內的應用程式。

調整大小

一旦您的撰寫專案開始執行後,您就可以使用 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 時,其會根據設定的規則來排程容器。 這同樣適用於 Azure ACS (在 Swarm 模式中) 和 AWS ECS 容器服務等其他 Swarm 型服務。 您只需要先部署 Swarm 叢集,然後再部署 Docker 化Orleans應用程式。

注意

如果您使用 Docker 引擎搭配已支援 stackdeploycompose v3 的 Swarm 模式,則部署解決方案的較佳方法是 docker stack deploy -c docker-compose.yml <name>。 請記住,其需要 v3 撰寫檔案來支援您的 Docker 引擎,而 Azure 和 AWS 等大部分的託管服務仍會使用 v2 和較舊的引擎。

Google Kubernetes (K8s)

如果您計劃使用 Kubernetes 裝載 Orleans,在 OrleansContrib\Orleans.Clustering.Kubernetes 上提供了社群維護的叢集提供者。 您可以在該處找到如何使用提供者順暢地在 Kubernetes 中裝載 Orleans 的文件和範例。

在容器內偵錯 Orleans

現在您已了解如何從頭開始在容器中執行 Orleans,最好在 Docker 中運用其中一個最重要的原則。 容器是不可變動的。 且其應該 (幾乎) 與生產環境中的開發具有相同的映像、相依性和執行階段。 這可確保良好的舊陳述式「這可在我的電腦上運作!」永遠不會再發生。 若要這麼做,您必須有辦法在容器「內部」進行開發,而這包含將偵錯工具附加至容器內部的應用程式。

您有多種方式可以使用多種工來達成此目的。 在評估過數個方式之後,當我撰寫本文之際,我最終選擇一個看起來更簡單且在應用程式中較不具干擾的方式。

如本文稍早所述,我們會使用 VSCode 來開發範例,因此以下說明如何取得附加至您容器內 Orleans 應用程式的偵錯工具。

首先,在您的解決方案中變更 .vscode 目錄內的兩個檔案:

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"
        }
    ]
}

此檔案基本上每當您建置專案時都會告知 VSCode,其就會執行 publish 命令,如同我們稍早手動執行的一樣。

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 (將會發佈) 建置解決方案,並啟動「定址接收器」和「用戶端」。 其會將 docker exec 命令傳送至執行中的 docker-compose 服務執行個體/容器,以對應用程式啟動偵錯工具,這樣就可以了。 您已將偵錯工具附加至容器,並加以使用,如同其是在本機執行的 Orleans 應用程式一樣。 現在的差異在於其位於容器內,一旦完成,您就可以將容器發佈至您的登錄,並在生產環境中,在 Docker 主機上提取容器。