Teilen über


Docker-Bereitstellung

Tipp

Selbst wenn Sie sehr mit Docker und/oder Orleans vertraut sind, empfiehlt es sich, wie bei allen anderen Orleans-Dokumentationen, bis zum Ende zu lesen, um möglicherweise auftretende Probleme zu vermeiden, die wir bereits behoben oder umgangen haben.

Dieser Artikel und sein Beispiel sind keine endgültige Fassung. Jegliche/s Feedback, PR oder Vorschläge sind herzlich willkommen.

Bereitstellen von Orleans-Lösungen in Docker

Die Bereitstellung von Orleans in Docker kann aufgrund des Designs von Docker-Orchestratoren und -Clusteringstapeln schwierig sein. Am kompliziertesten ist das Verständnis des Konzepts des Überlagerungsnetzwerks (Overlay) aus dem Docker Swarm und Kubernetes-Netzwerkmodell.

Docker-Container und -Netzwerkmodelle wurden so entwickelt, dass sie größtenteils zustandslose und unveränderliche Container ausführen. Somit lässt sich ein Cluster, auf dem node.js- oder Nginx-Anwendungen ausgeführt werden, ziemlich einfach hochfahren. Wenn Sie jedoch versuchen, etwas komplexeres zu verwenden, z. B. eine echte gruppierte oder verteilte (z. B Orleans-basierte) Anwendung, werden Sie letztendlich Probleme beim Einrichten bekommen. Es ist möglich, aber nicht so einfach, wie bei webbasierten Anwendungen.

Docker-Clustering besteht aus der Zusammenstellung mehrerer Hosts, die als einzelner Ressourcenpool mit einem Containerorchestrator verwaltet werden. Docker Inc. stellt Swarm als eigene Option für die Containerorchestrierung bereit, während Google über Kubernetes (auch bekannt als K8s) verfügt. Es gibt andere Orchestratoren wie DC/OS, Mesos, aber in diesem Dokument werden wir über Swarm und K8s sprechen, da deren Verwendung stärker verbreitet ist.

Die gleichen Grain-Schnittstellen und -Implementierungen, die überall ausgeführt werden, wo Orleans bereits unterstützt wird, werden auch in Docker-Containern laufen. Es sind keine besonderen Überlegungen erforderlich, um Ihre Anwendung in Docker-Containern ausführen zu können.

Die hier erläuterten Konzepte können sowohl für .NET Core- als auch für .NET 4.6.1-Varianten von Orleans verwendet werden, aber um die plattformübergreifende Natur von Docker und .NET Core zu veranschaulichen, konzentrieren wir uns auf ein Beispiel, das davon ausgeht, dass Sie .NET Core verwenden. Plattformspezifische Details (Windows/Linux/OSX) können Sie in diesem Artikel finden.

Voraussetzungen

In diesem Artikel wird davon ausgegangen, dass die folgenden erforderlichen Komponenten installiert sind:

  • Docker: Docker4X verfügt über ein benutzerfreundliches Installationsprogramm für die wichtigsten unterstützten Plattformen. Es enthält die Docker-Engine sowie Docker Swarm.
  • Kubernetes (K8s): Googles Angebot für die Containerorchestrierung. Es enthält Anleitungen zum Installieren von Minikube (einer lokalen Bereitstellung von K8s) sowie von kubectl, zusammen mit allen zugehörigen Abhängigkeiten.
  • .NET – Plattformübergreifende Variante von .NET
  • Visual Studio Code (VSCode): Sie können jede beliebige IDE verwenden. VSCode ist plattformübergreifend, daher verwenden wir es, um sicherzustellen, dass es auf allen Plattformen funktioniert. Nachdem Sie VSCode installiert haben, installieren Sie die C#-Erweiterung.

Wichtig

Sie müssen Kubernetes nicht installiert haben, wenn Sie es nicht verwenden möchten. Das Docker4X-Installationsprogramm enthält bereits Swarm, sodass für die Verwendung keine zusätzliche Installation erforderlich ist.

Hinweis

Unter Windows aktiviert das Docker-Installationsprogramm Hyper-V während des Installationsvorgangs. Da dieser Artikel und seine Beispiele .NET Core verwenden, basieren die verwendeten Containerimages auf Windows Server NanoServer. Wenn Sie nicht planen, .NET Core zu verwenden, und als Ziel das vollständige.NET 4.6.1 Framework verwenden möchten, sollte das verwendete Image Windows Server Core und die Version 1.4 und höher von Orleans sein (die nur das vollständige .NET Framework unterstützt).

Erstellen einer Orleans-Lösung

Die folgenden Anweisungen zeigen, wie Sie mithilfe der neuen dotnet-Tools eine reguläre Orleans-Lösung erstellen.

Passen Sie die Befehle so an, dass sie für Ihre Plattform geeignet sind. Außerdem ist die Verzeichnisstruktur lediglich ein Vorschlag. Passen Sie diese an Ihre Anforderungen an.

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

Bisher haben wir lediglich Codebausteine erstellt, um die Projektmappenstruktur und Projekte zu erstellen sowie um Verweise zwischen Projekten hinzuzufügen. Nichts anderes als ein normales Orleans-Projekt.

Zum Zeitpunkt der Erstellung dieses Artikels befindet sich Orleans 2.0 (die einzige Version, die .NET Core und unterstützt und plattformübergreifend ist) in der Technology Preview, sodass die zugehörigen NuGet-Pakete in einem MyGet-Feed gehostet und nicht im offiziellen Nuget.org-Feed veröffentlicht werden. Um die NuGet-Vorschaupakete zu installieren, verwenden wir die dotnet CLI, um den Quellfeed und die Version von MyGet zu erzwingen:

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

Gut, jetzt verfügen Sie über alle grundlegenden Abhängigkeiten, um eine einfache Orleans-Anwendung auszuführen. Beachten Sie, dass sich bisher nichts gegenüber Ihrer regulären Orleans-Anwendung geändert hat. Fügen wir nun Code hinzu, damit wir etwas damit machen können.

Implementieren Ihrer Orleans-Anwendung

Vorausgesetzt, Sie verwenden VSCode, führen Sie im Projektmappenverzeichnis code . aus. Dadurch wird das Verzeichnis in VSCode geöffnet und die Projektmappe geladen.

Dies ist die Projektmappenstruktur, die wir zuvor erstellt haben.

Visual Studio Code: Explorer with Program.cs selected.

Wir haben außerdem die Dateien Program.cs, OrleansHostWrapper.cs, IGreetingGrain.cs und GreetingGrain.cs den Schnittstellen bzw. Grain-Projekten hinzugefügt. Hier ist der Code für diese Dateien:

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);
            }
        }
    }
}

Wir gehen hier nicht im Detail auf die Grain-Implementierung ein, da dies den Rahmen dieses Artikels sprengt. Lesen Sie dies in anderen Dokumente nach, die sich darauf beziehen. Diese Dateien sind im Wesentlichen eine minimale Orleans-Anwendung, und wir verwenden diese als Ausgangspunkt, um mit dem Rest dieses Artikels fortzufahren.

In diesem Artikel verwenden wir den OrleansAzureUtils-Mitgliedschaftsanbieter, aber Sie können jeden anderen verwenden, der bereits von Orleans unterstützt wird.

Die Dockerfile-Datei

Zum Erstellen Ihres Containers verwendet Docker Images. Weitere Informationen zum Erstellen Ihres eigenen Images finden Sie in der Docker-Dokumentation. In diesem Artikel verwenden wir offizielle Microsoft-Images. Basierend auf den Ziel- und Entwicklungsplattformen müssen Sie das geeignete Image auswählen. In diesem Artikel verwenden wir microsoft/dotnet:1.1.2-sdk, wobei es sich um ein Linux-basiertes Image handelt. Sie können z. B. microsoft/dotnet:1.1.2-sdk-nanoserver für Windows verwenden. Wählen Sie eins aus, das Ihren Anforderungen entspricht.

Hinweis für Windows-Benutzer: Wie bereits erwähnt, verwenden wir in diesem Artikel .NET Core und Orleans Technical Preview 2.0, um dies plattformübergreifend zu gestalten. Wenn Sie Docker unter Windows mit der vollständig veröffentlichten Orleans-Version 1.4 und höher verwenden möchten, müssen Sie die Images verwenden, die auf Windows Server Core basieren, da NanoServer- und Linux-basierte Images nur .NET Core unterstützen.

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

Diese Dockerfile lädt im Wesentlichen den VSdbg-Debugger herunter, installiert ihn und startet einen leeren Container, der für immer aktiv gehalten wird, sodass wir ihn beim Debuggen nicht jedes Mal zerstören/aufbauen müssen.

Für die Produktion ist das Image nun kleiner, da es nur die .NET Core Runtime und nicht das ganze SDK enthält, und die Dockerfile ist etwas einfacher:

Docker-Datei:

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

Die „docker-compose“-Datei

Die docker-compose.yml-Datei definiert (innerhalb eines Projekts) im Wesentlichen eine Gruppe von Diensten und deren Abhängigkeiten auf Dienstebene. Jeder Dienst enthält eine oder mehrere Instanzen eines bestimmten Containers, der auf den Images basiert, die Sie in Ihrer Dockerfile ausgewählt haben. Weitere ausführliche Informationen zur docker-compose-Datei finden Sie in der Dokumentation zu „docker-compose“.

Für eine Orleans-Bereitstellung besteht ein häufiger Anwendungsfall darin, eine docker-compose.yml-Datei zu verwenden, die zwei Dienste enthält. Einen für das Orleans-Silo und die andere für den Orleans-Client. Der Client hätte eine Abhängigkeit vom Silo, was bedeutet, dass er erst gestartet wird, nachdem der Silodienst aktiviert ist. Ein weiterer Fall ist das Hinzufügen eines Speicher-/Datenbankdiensts/-containers, z. B. SQL Server, der zuerst vor dem Client und dem Silo gestartet werden sollte, sodass beide Dienste eine Abhängigkeit davon haben sollten.

Hinweis

Bevor Sie weiterlesen, beachten Sie, dass in docker-compose-Dateien Einzügeeine Bedeutung haben. Achten Sie also darauf, wenn Sie Probleme haben.

Im Folgenden erfahren Sie, wie wir unsere Dienste für diesen Artikel beschreiben:

docker-compose.override.yml (Debuggen):

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

version: '3.1'

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

In der Produktion ordnen wir das lokale Verzeichnis nicht zu, und wir haben auch keine build:-Aktion. Der Grund hierfür ist, dass die Images in der Produktion erstellt und in Ihre eigene Docker-Registrierung gepusht werden sollten.

Zusammenfügen der Bestandteile

Jetzt verfügen wir über alle beweglichen Teile, die zum Ausführen Ihrer Orleans-Anwendung erforderlich sind. Wir werden sie zusammenfügen, damit wir unsere Orleans-Lösung in Docker ausführen können (endlich!).

Wichtig

Die folgenden Befehle sollten aus dem Projektmappenverzeichnis ausgeführt werden.

Stellen wir zunächst sicher, dass alle NuGet-Pakete aus unserer Lösung wiederhergestellt werden. Dies ist nur einmalig erforderlich. Sie müssen dies nur dann erneut tun, wenn Sie eine Paketabhängigkeit von Ihrem Projekt ändern.

dotnet restore

Erstellen wir nun unsere Lösung wie gewohnt mithilfe der dotnet CLI und veröffentlichen sie in einem Ausgabeverzeichnis:

dotnet publish -o ./bin/PublishOutput

Tipp

Wir verwenden hier publish (veröffentlichen) anstelle von „build“ (erstellen), um Probleme mit unseren dynamisch geladenen Assemblys in Orleans zu vermeiden. Wir sind noch auf der Suche nach einer besseren Lösung dafür.

Nachdem die Anwendung erstellt und veröffentlicht wurde, müssen Sie Ihre Dockerfile-Images erstellen. Dieser Schritt muss nur einmal pro Projekt ausgeführt werden und sollte nur dann erneut ausgeführt werden, wenn Sie die Dockerfile oder „docker-compose“ ändern, oder wenn Sie aus irgendeinem Grund Ihre lokale Imageregistrierung bereinigt haben.

docker-compose build

Alle in sowohl Dockerfile als auch docker-compose.yml verwendeten Images werden aus der Registrierung gepullt und auf Ihrem Entwicklungscomputer zwischengespeichert. Ihre Images sind erstellt, und Sie sind jetzt zur Ausführung bereit.

Ok, führen wir sie aus!

# 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
#

Wenn Sie nun eine docker-compose ps ausführen, werden zwei Container für das orleansdocker-Projekt ausgeführt:

# 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

Hinweis

Wenn Sie Windows verwenden und Ihr Container ein Windows-Image als Basis verwendet, wird in der Spalte Befehl der PowerShell-Befehl angezeigt, der einem tail auf *NIX-Systemen entspräche, sodass der Container auf die gleiche Weise erhalten bleibt.

Nachdem Sie Ihre Container jetzt eingerichtet haben, müssen Sie sie nicht jedes Mal beenden, wenn Sie Ihre Orleans-Anwendung starten möchten. Sie müssen lediglich Ihre IDE integrieren, um die Anwendung innerhalb des Containers zu debuggen, der zuvor in Ihrer docker-compose.yml-Datei zugeordnet wurde.

Skalierung

Sobald Ihr Compose-Projekt ausgeführt wird, können Sie Ihre Anwendung ganz einfach mit dem docker-compose scale-Befehl hoch- oder herunterskalieren:

# 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

Nach einigen Sekunden sehen Sie, dass die Dienste auf die spezifische Anzahl von Instanzen skaliert werden, die Sie angefordert haben.

# 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

Wichtig

In der Spalte Command in diesen Beispielen wird der tail-Befehl nur angezeigt, weil wir den Debuggercontainer verwenden. Wenn wir in der Produktion wären, würde zum Beispiel dotnet OrleansSilo.dll angezeigt.

Docker Swarm

Der Docker-Clusteringstapel heißt Swarm. Weitere Informationen finden Sie unter Docker Swarm.

Um diesen Artikel in einem Swarm-Cluster auszuführen, müssen Sie keinen zusätzlichen Aufwand betreiben. Wenn Sie docker-compose up -d auf einem Swarm-Knoten ausführen, werden Container basierend auf den konfigurierten Regeln geplant. Gleiches gilt für andere Swarm-basierte Dienste wie Docker Datacenter, Azure ACS (im Swarm-Modus) und AWS ECS-Containerdienst. Sie müssen lediglich Ihren Swarm-Cluster bereitstellen, bevor Sie Ihre dockerisierteOrleans-Anwendung bereitstellen.

Hinweis

Wenn Sie eine Docker-Engine im Swarm-Modus verwenden, die bereits Unterstützung für stack, deploy und compose v3 aufweist, wäre docker stack deploy -c docker-compose.yml <name> ein besserer Ansatz zum Bereitstellen Ihrer Lösung. Denken Sie lediglich daran, dass die compose-Datei der v3 Ihre Docker-Engine unterstützen muss, und dass die meisten gehosteten Dienste wie Azure und AWS immer noch v2 und ältere Engines verwenden.

Google Kubernetes (K8s)

Wenn Sie Kubernetes zum Hosten von Orleans verwenden möchten, steht unter OrleansContrib\Orleans.Clustering.Kubernetes ein von der Community verwalteter Clusteringanbieter zur Verfügung. Dort finden Sie außerdem Dokumentation und Beispiele zum nahtlosen Hosten von Orleans in Kubernetes unter Verwendung des Anbieters.

Debuggen von Orleans innerhalb von Containern

Da Sie nun wissen, wie Sie Orleans in einem Container von Grund auf neu ausführen, wäre es gut, eins der wichtigsten Prinzipien in Docker zu nutzen. Container sind unveränderlich. Außerdem sollten sie (fast) das gleiche Image, die gleichen Abhängigkeiten und die gleiche Runtime in der Entwicklung wie in der Produktion aufweisen. Auf diese Weise wird sichergestellt, dass sich die gute alte Aussage Auf meinem Computer funktioniert es! nie wieder bewahrheitet. Um dies zu ermöglichen, benötigen Sie eine Möglichkeit, innerhalb des Containers zu entwickeln, wozu gehört, dass Ihrer Anwendung innerhalb des Containers ein Debugger angefügt ist.

Es gibt mehrere Möglichkeiten, dies mithilfe mehrerer Tools zu erreichen. Als ich diesen Artikel geschrieben habe, bin ich nach Auswertung mehrerer Methoden am Ende bei einer gelandet, die einfacher aussieht und in der Anwendung unauffälliger ist.

Wie bereits in diesem Artikel erwähnt, verwenden wir VSCode zum Entwickeln des Beispiels. Hier erfahren Sie nun also, wie Sie den Debugger innerhalb des Containers an Ihre Orleans-Anwendung anfügen.

Ändern Sie zunächst zwei Dateien in Ihrem .vscode-Verzeichnis in Ihrer Projektmappe:

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

Diese Datei teilt VSCode im Wesentlichen mit, dass bei jedem Erstellen des Projekts der Befehl publish wie zuvor manuell ausgeführt wird.

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

Jetzt können Sie einfach die Lösung aus VSCode erstellen (die veröffentlicht wird) und sowohl den Silo als auch den Client starten. Es wird ein docker exec-Befehl an die/den ausgeführte/n docker-compose-Dienstinstanz/-Container gesendet, um den Debugger für die Anwendung zu starten – und das ist alles. Sie haben den Debugger an den Container angefügt und verwenden ihn so, als ob es sich um eine lokal ausgeführte Orleans-Anwendung handelte. Der Unterschied besteht jetzt darin, dass er sich innerhalb des Containers befindet. Sobald Sie fertig sind, können Sie den Container einfach in Ihrer Registrierung veröffentlichen und ihn auf Ihre Docker-Hosts in der Produktion pullen.