Packen von ASP.NET Core-Anwendungen mit Docker

ASP.NET Core-Anwendungen sind wirklich plattformübergreifend und können unter Nixen und entsprechend in Docker ausgeführt werden. Mal sehen, wie sie gepackt werden können, um unter Linux bereitgestellt und in Verbindung mit Nginx verwendet zu werden. Details unter dem Schnitt!



Hinweis: Wir setzen die Reihe der Veröffentlichungen von Vollversionen von Artikeln aus dem Hacker-Magazin fort. Rechtschreibung und Zeichensetzung des Autors gespeichert.


Über Docker


Fast jeder hat von Microservice-Architektur gehört. Das Konzept, die Anwendung in Teile zu zerlegen, bedeutet nicht, dass sie neu ist. Aber das Neue ist das gut vergessene und recycelte Alte.


Wenn Sie versuchen, in wenigen Worten über Architektur zu sprechen, ist die Webanwendung in separate einheitliche Teile unterteilt - Dienste. Dienste interagieren nicht direkt miteinander und haben keine gemeinsamen Datenbanken. Dies geschieht, um jeden Dienst ohne Konsequenzen für andere ändern zu können. Dienstleistungen werden in Containern verpackt. Unter den Containern regiert Docker den Ball.


Um zu beschreiben, was Docker sehr oft vereinfacht wird, verwenden Sie den Begriff "virtuelle Maschine". Es gibt definitiv eine Ähnlichkeit, aber es ist falsch, dies zu sagen. Der einfachste Weg, diesen Unterschied zu verstehen, besteht darin, die folgenden Bilder aus der offiziellen Docker-Dokumentation zu betrachten:




Container verwenden den Kern des aktuellen Betriebssystems und teilen ihn unter sich auf. Während virtuelle Maschinen, die Hypervisor verwenden, Hardwareressourcen verwenden.
Docker Image ist ein schreibgeschütztes Objekt, in dem im Wesentlichen eine Vorlage zum Erstellen eines Containers gespeichert ist. Ein Container ist eine Umgebung, in der Code ausgeführt wird. Bilder werden in Repositories gespeichert. Im offiziellen Docker Hub- Repository können Sie beispielsweise nur ein Bild privat speichern. Es ist jedoch kostenlos. Selbst dafür müssen Sie sich bei ihnen bedanken.


INFO


Docker ist nicht der einzige Vertreter der Containerisierung. Darüber hinaus gibt es weitere Technologien. Zum Beispiel:


rkt (ausgesprochen 'Rakete') von CoreOS


LXD (ausgesprochen 'lexdi') von Ubuntu


Windows-Container - Sie werden es von niemandem erraten.


Nachdem wir uns mit der Theorie vertraut gemacht haben, gehen wir zur Praxis über.


Es macht keinen Sinn, die Docker-Installation zu zerlegen, da sie auf vielen Betriebssystemen installiert werden kann. Ich werde nur darauf hinweisen, dass Sie es für Ihre Plattform aus dem Docker Store herunterladen können . Wenn Sie Docker unter Windows installieren, muss die Virtualisierung im BIOS und im Betriebssystem aktiviert sein. Informationen zum Aktivieren in 10-ke finden Sie im folgenden Artikel: Installieren von Hyper-V unter Windows 10


Erstellen eines Docker-fähigen Projekts


Docker ist natürlich ein Linux-Produkt, aber bei Bedarf können Sie es bei der Entwicklung für Mac oder Windows verwenden. Wenn Sie ein Projekt in Visual Studio erstellen, um Docker-Unterstützung hinzuzufügen, aktivieren Sie einfach das Kontrollkästchen Docker-Unterstützung aktivieren.


Docker-Unterstützung kann zu einem vorhandenen Projekt hinzugefügt werden. Es wird dem Projekt auf die gleiche Weise hinzugefügt, wie verschiedene neue Komponenten hinzugefügt werden. Kontextmenü Hinzufügen - Docker-Unterstützung.


Wenn Docker auf Ihrem Computer installiert ist und ausgeführt wird, wird die Konsole automatisch geöffnet und der Befehl ausgeführt


docker pull microsoft/aspnetcore:2.0 

Dadurch wird der Vorgang zum Herunterladen des Bildes gestartet. Dieses Bild ist eigentlich ein Leerzeichen, auf dessen Grundlage Ihr Bild erstellt wird. ASP.NET Core 2.1 verwendet ein anderes Bild - Microsoft / Dotnet: SDK


Die folgenden Dateien werden automatisch im Verzeichnis mit der für Sie geeigneten Lösung erstellt:
.dockerignore (ohne Dateien und Verzeichnisse aus dem Docker-Image), docker-compose.yml (mit dieser Datei können Sie die Ausführung mehrerer Dienste konfigurieren), docker-compose.override.yml (Zusatzkonfiguration docker-compose), docker-compose.dcproj ( Projektdatei für Visual Studio).


Eine Dockerfile-Datei wird im Projektverzeichnis erstellt. Mit Hilfe dieser Datei erstellen wir unser Bild. Standardmäßig (falls das Projekt DockerServiceDemo heißt) sieht es möglicherweise folgendermaßen aus:


 FROM microsoft/aspnetcore:2.0 AS base WORKDIR /app EXPOSE 80 FROM microsoft/aspnetcore-build:2.0 AS build WORKDIR /src COPY DockerServiceDemo/DockerServiceDemo.csproj DockerServiceDemo/ RUN dotnet restore DockerServiceDemo/DockerServiceDemo.csproj COPY . . WORKDIR /src/DockerServiceDemo RUN dotnet build DockerServiceDemo.csproj -c Release -o /app FROM build AS publish RUN dotnet publish DockerServiceDemo.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "DockerServiceDemo.dll"] 

Bei der Erstkonfiguration für .NET Core 2.0 können Sie das Image nicht sofort mit dem Docker-Build-Befehl erstellen. Es ist so konfiguriert, dass die Docker-Compose-Datei aus einem Verzeichnis auf einer Ebene höher gestartet wird. Damit der Bau erfolgreich verläuft, kann das Dockerfile in ein ähnliches Aussehen gebracht werden:


 FROM microsoft/aspnetcore:2.0 AS base WORKDIR /app EXPOSE 80 FROM microsoft/aspnetcore-build:2.0 AS build WORKDIR /src COPY DockerServiceDemo.csproj DockerServiceDemo.csproj RUN dotnet restore DockerServiceDemo.csproj COPY . . WORKDIR /src RUN dotnet build DockerServiceDemo.csproj -c Release -o /app FROM build AS publish RUN dotnet publish DockerServiceDemo.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "DockerServiceDemo.dll"] 

Ich habe lediglich das zusätzliche DockerServiceDemo-Verzeichnis entfernt.


Wenn Sie Visual Studio Code verwenden, müssen Sie die Dateien manuell generieren. Obwohl VS Code über Zusatzfunktionen in Form einer Docker- Erweiterung verfügt, werde ich einen Link zum Handbuch zur Arbeit mit dem Docker von VS Code - Arbeiten mit Docker hinzufügen. Ja, der Artikel ist in Englisch, aber es ist mit Bildern


Docker mit drei Akkorden


Für die tägliche Arbeit mit dem Docker reichen nur wenige Befehle aus, um sich zu erinnern.


Das wichtigste Team ist natürlich der Aufbau eines Images. Dazu müssen Sie bash / CMD / PowerShell verwenden, um in das Verzeichnis zu wechseln, in dem sich die Docker-Datei befindet, und den folgenden Befehl ausführen:


 docker build -t your_image_name . 

Hier wird nach der Option -t der Name Ihres Bildes festgelegt. Achtung - am Ende des Befehls ein Leerzeichen nach dem Leerzeichen. Dieser Punkt bedeutet, dass das aktuelle Verzeichnis verwendet wird. Ein Bild kann mit einem Tag (Nummer oder Name) versehen werden. Fügen Sie dazu nach dem Namen einen Doppelpunkt ein und geben Sie ein Tag an. Wenn das Tag nicht angegeben ist, wird es standardmäßig mit dem neuesten Namen festgelegt. Um ein Bild an das Repository zu senden, muss der Bildname den Namen des Repositorys enthalten. Ungefähr so:


 docker build -t docker_account_name/image_name:your_tag . 

Hier ist Ihr_Docker_Kontoname der Name Ihres Docker-Hub-Kontos.


Wenn Sie das Image nur mit einem lokalen Namen erstellt haben, der das Repository nicht enthält, können Sie das Image nach der Erstellung mit dem folgenden Befehl mit einem anderen Namen markieren:


 docker tag image_name docker_account_name/image_name:your_tag 

Um Änderungen an den Hub zu senden, müssen Sie jetzt den folgenden Befehl ausführen:


 docker push docker_account_name/image_name:your_tag 

Zuvor müssen Sie sich bei Ihrem Docker-Konto anmelden. Unter Windows erfolgt dies über die Benutzeroberfläche der Anwendung, unter * nix jedoch über den folgenden Befehl:


 docker login 

In der Tat sind drei Teams nicht genug. Sie müssen auch in der Lage sein, den Betrieb des Containers zu überprüfen. Der Befehl, mit dem Sie den Container starten können, sieht folgendermaßen aus:


 docker run -it -p 5000:80 image_name 

Die Option -it erstellt ein Pseudo-TTY und Ihr Container reagiert auf Anforderungen. Nach dem Ausführen des Befehls ist der Dienst unter http: // localhost: 5000 / verfügbar.


-p 5000: 80 ordnet Port 5000 des Containers Port 80 des Hosts zu.


Darüber hinaus gibt es solche Befehle:


 docker ps –a 

Zeigen Sie eine Liste der Container. Da der Schalter -a hinzugefügt wurde, werden alle Container angezeigt, nicht nur die aktuell ausgeführten.


 docker rm container_name 

Dieser Befehl entfernt den Container mit dem Namen container_name. rm - kurz zum entfernen


 docker logs container_name 

Containerprotokolle anzeigen


 docker rmi image_name 

Löscht ein Bild mit dem Namen image_name


Starten eines Containers über einen Reverse-Proxy-Server


Tatsache ist, dass .NET Core-Anwendungen selbst ihren Kestrel-Webserver verwenden. Dieser Server wird für die Produktion nicht empfohlen. Warum? Es gibt mehrere Erklärungen.
Wenn mehrere Anwendungen IP und Port gemeinsam nutzen, kann Kestrel den Datenverkehr nicht verteilen. Darüber hinaus bietet der Reverse-Proxy-Server eine zusätzliche Sicherheitsebene, vereinfacht den Lastausgleich und die SSL-Einstellungen und lässt sich besser in die vorhandene Infrastruktur integrieren. Für die meisten Entwickler ist zusätzliche Sicherheit der wichtigste Grund für die Notwendigkeit von Reverse-Proxys.


Stellen Sie zunächst die ursprüngliche Dockerfile-Konfiguration wieder her. Danach werden wir uns mit der Datei docker-compose.yml befassen und versuchen, unseren Dienst alleine auszuführen. Das yml-Dateiformat wird als "yaml" gelesen und ist eine Abkürzung für "Noch eine andere Markup-Sprache" oder "YAML ist keine Markup-Sprache". Entweder eine andere Auszeichnungssprache oder überhaupt keine Auszeichnungssprache. Irgendwie ist nicht alles sicher.


Meine Standard-Docker-Compose-Datei sieht folgendermaßen aus:


 version: '3.4' services: dockerservicedemo: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile 

Die Datei docker-compose.override.yml fügt der Konfiguration mehrere Einstellungen hinzu:
Version: '3.4'


 services: dockerservicedemo: environment: - ASPNETCORE_ENVIRONMENT=Development ports: - "80" 

Wir können die erstellte Lösung mit Docker-Compose-Build erstellen. Durch Aufrufen des Docker-Compose-Up-Befehls starten wir unseren Container. Alles arbeitet? Fahren Sie dann mit dem nächsten Schritt fort. Erstellen Sie die Datei nginx.info. Die Konfiguration ist ungefähr wie folgt:


 worker_processes 4; events { worker_connections 1024; } http { sendfile on; upstream app_servers { server dockerservicedemo:80; } server { listen 80; location / { proxy_pass http://app_servers; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } } 

Hier geben wir an, dass nginx Port 80 abhört (listen 80;). Die empfangenen Anforderungen werden an den 80. Port des Hosts im Dockerservicedemo-Container umgeleitet. Außerdem teilen wir nginx mit, welche Header weitergegeben werden sollen.


Wir können http in nginx verwenden und über https auf die Website zugreifen. Wenn eine https-Anforderung über einen http-Proxy gesendet wird, werden viele Informationen von https nicht an http übergeben. Bei Verwendung eines Proxys geht außerdem die externe IP-Adresse verloren. Damit diese Informationen in den Headern übertragen werden können, müssen Sie den Code unseres ASP.NET-Projekts ändern und den folgenden Code am Anfang der Configure-Methode der Datei Startup.cs hinzufügen:


  app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); 

Die meisten Proxyserver verwenden die Header X-Forwarded-For und X-Forwarded-Proto. Diese Header werden jetzt in der Nginx-Konfiguration angegeben.


Fügen Sie nun das Nginx-Image und die Datei nginx.conf in die Doker-Compose-Konfiguration ein. Vorsicht in YAML-Räumen ist wichtig:


 version: '3.4' services: dockerservicedemo: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile ports: - 5000:80 proxy: image: nginx:latest volumes: - ./DockerServiceDemo/nginx.conf:/etc/nginx/nginx.conf ports: - 80:80 

Hier fügen wir unserer Konfiguration Proxys als Nginx-Image hinzu. Wir fügen diesem Bild eine externe Einstellungsdatei bei. Wir mounten es mithilfe eines Mechanismus namens Volume in das Container-Dateisystem. Wenn Sie am Ende hinzufügen: ro, wird das Objekt schreibgeschützt gemountet.


Der Proxy überwacht den externen 80. Port des Computers, auf dem der Container ausgeführt wird, und sendet eine Anforderung an den internen 80. Port des Containers.


Durch Ausführen des Befehls doker-compose up füllen wir das Nginx-Image aus dem Repository und starten unseren Container zusammen mit dem Proxy-Container. Jetzt unter http: // localhost: 80 / wird es über nginx zugänglich sein. Auf dem 5000. Port „dreht“ sich die Anwendung auch unter Kestrel.


Wir können überprüfen, ob die Anforderung an die Webanwendung über den Reverse-Proxy erfolgt. Öffnen Sie die Entwicklertools im Chrome-Browser und wechseln Sie zur Registerkarte Netzwerk. Klicken Sie hier auf localhost und wählen Sie die Registerkarte Header.



Wir starten den Container über Proxys und HTTPS


ASP.NET Core 2.1 brachte Verbesserungen in der HTTPS-Unterstützung mit sich.
Angenommen, mit der folgenden Middleware können Sie von einer ungesicherten Verbindung zu einer sicheren umleiten:


 app.UseHttpsRedirection(); 

Mit dem nächsten können Sie das HTTP Strict Transport Security Protocol (HSTS) verwenden.


 app.UseHsts(); 

HSTS ist eine Funktion aus dem HTTP / 2-Protokoll, dessen Spezifikation 2015 veröffentlicht wurde. Diese Funktionalität wird von modernen Browsern unterstützt und informiert, dass die Website nur https verwendet. Somit besteht ein Schutz gegen einen Downgrade-Angriff, bei dem der Angreifer die Situation ausnutzen kann, indem er den Übergang zu einem unsicheren http-Protokoll verwendet. Führen Sie beispielsweise ein Downgrade von TLS durch oder ersetzen Sie sogar ein Zertifikat.


In der Regel wird diese Art von Angriff in Verbindung mit Man-in-the-Middle-Angriffen verwendet. Sie sollten wissen und sich daran erinnern, dass HSTS Sie nicht vor einer Situation bewahrt, in der ein Benutzer die Site mithilfe des http-Protokolls besucht und dann zu https umleitet. Es gibt die sogenannte Chrome-Preload-Liste , die Links zu Websites enthält, die https unterstützen. Andere Browser (Firefox, Opera, Safari, Edge) unterstützen ebenfalls Listen von https-Sites, die basierend auf der Chrome-Liste erstellt wurden. Alle diese Listen sind jedoch weit von allen Websites entfernt.


Wenn Sie zum ersten Mal eine Core-Anwendung unter Windows ausführen, erhalten Sie eine Meldung, dass ein Entwicklerzertifikat erstellt und installiert wurde. Wenn Sie auf die Schaltfläche klicken und das Zertifikat installieren, wird es als vertrauenswürdig eingestuft. Über die Befehlszeile unter macOS können Sie dem Zertifikat mit dem folgenden Befehl Vertrauen hinzufügen:
dotnet dev-certs https –trust


Wenn das Dienstprogramm dev-certs nicht installiert ist, können Sie es mit dem folgenden Befehl installieren:


 dotnet tool install --global dotnet-dev-certs 

Wie Sie ein vertrauenswürdiges Zertifikat unter Linux hinzufügen, hängt von der Verteilung ab.
Zu Testzwecken verwenden wir das Entwicklerzertifikat. Aktionen mit einem von CA signierten Zertifikat sind ähnlich. Auf Wunsch können Sie kostenlose LetsEncrypt- Zertifikate verwenden


Mit dem Befehl können Sie das Entwicklerzertifikat in eine Datei exportieren


 dotnet dev-certs https -ep ___.pfx 

Die Datei muss unter Windows in das Verzeichnis% APPDATA% / ASP.NET / Https / oder unter macOS / Linux nach /root/.aspnet/https/ kopiert werden.


Erstellen Sie Benutzergeheimnisse mit den folgenden Inhalten, damit der Container den Pfad zum Zertifikat und sein Kennwort abruft:


 { "Kestrel":{ "Certificates":{ "Default":{ "Path": "/root/.aspnet/https/__.pfx", "Password": "___" } } } } 

Diese Datei speichert unverschlüsselte Daten und wird daher nur während der Entwicklung verwendet. Eine Datei wird in Visual Studio erstellt, indem Sie das Kontextmenü auf dem Projektsymbol aufrufen oder das Dienstprogramm für Benutzergeheimnisse unter Linux verwenden.


Unter Windows wird die Datei im Verzeichnis% APPDATA% \ Microsoft \ UserSecrets \ <Benutzer-Sekret_ID> \ Secrets.json gespeichert, und unter MacOS und Linux wird sie in ~ / .microsoft / usersecrets / <Benutzer_Sekret_ID> /Sekret.json gespeichert


Um die Einstellungen für die Produktion zu speichern, verwenden einige Linux-Distributionen möglicherweise systemd. Die Einstellungen werden unter dem Attribut Service gespeichert. Zum Beispiel so:


 [Service] Environment="Kestrel _ Certificates _ Default _Path=/root/.aspnet/https/__.pfx" Environment="Kestrel _ Certificates _ Default _Password=___" 

Als nächstes werde ich sofort die Arbeitsversion der Docker-Konfiguration für den Proxy und den Container über https geben und analysieren.


Docker-Compose-Datei:


 version: '3.4' services: dockerservicedemo21: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile  override: version: '3.4' services: dockerservicedemo: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=https://+:44392;http://+:80 - ASPNETCORE_HTTPS_PORT=44392 ports: - "59404:80" - "44392:44392" volumes: - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro proxy: image: nginx:latest volumes: - ./DockerServiceDemo/nginx.conf:/etc/nginx/nginx.conf - ./DockerServiceDemo/cert.crt:/etc/nginx/cert.crt - ./DockerServiceDemo/cert.rsa:/etc/nginx/cert.rsa ports: - "5001:44392" 

Jetzt werde ich unverständliche Momente beschreiben. Mit ASPNETCORE_URLS können wir im Anwendungscode nicht mit app.UseUrl den Port angeben, den die Anwendung überwacht.


ASPNETCORE_HTTPS_PORT führt eine Umleitung durch, ähnlich wie der folgende Code:
services.AddHttpsRedirection (options => options.HttpsPort = 44392)


Das heißt, der Datenverkehr von http-Anforderungen wird an einen bestimmten Port von https-Anforderungen umgeleitet.
Bei Verwendung von Ports wird angezeigt, dass die Anforderung vom externen 59404. Port an den 80. Container und vom 44392. externen Port an den 44392. umgeleitet wird. Da wir einen Reverse-Proxy-Server konfiguriert haben, können wir theoretisch Ports mit diesen Weiterleitungen entfernen.
Mithilfe von Volumes werden ein Verzeichnis mit einem pfx-Zertifikat und eine UserSecrets-Anwendung mit einem Kennwort und einem Link zum Zertifikat bereitgestellt.


Der Proxy-Abschnitt gibt an, dass Anforderungen vom 5001. externen Port an den 44392. Nginx-Port umgeleitet werden. Darüber hinaus werden eine Nginx-Konfigurationsdatei sowie ein Zertifikat und ein Zertifikatschlüssel bereitgestellt.


Damit sie ein einzelnes pfx-Zertifikat (das wir bereits haben) zum Erstellen von CRT- und RSA-Dateien erstellen können, können Sie OpenSSL verwenden. Zuerst müssen Sie das Zertifikat extrahieren:


 openssl pkcs12 -in ./_.pfx -clcerts -nokeys -out domain.crt 

Und dann der private Schlüssel:


 openssl pkcs12 -in ./_.pfx -nocerts -nodes -out domain.rsa 

Die Nginx-Konfiguration lautet wie folgt:


 worker_processes 4; events { worker_connections 1024; } http { sendfile on; upstream app_servers { server dockerservicedemo:44392; } server { listen 44392 ssl; ssl_certificate /etc/nginx/cert.crt; ssl_certificate_key /etc/nginx/cert.rsa; location / { proxy_pass https://app_servers; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } } 

Der Proxyserver überwacht Port 44392. Dieser Port empfängt Anforderungen vom 5001. Host-Port. Als Nächstes leitet der Proxy Anforderungen an den 44392. Port des Dockerdemoservice-Containers weiter.


Wenn Sie diese Beispiele verstanden haben, erhalten Sie einen guten Hintergrund für die Arbeit mit Docker, Microservices und Nginx.


Wir erinnern Sie daran, dass dies die Vollversion eines Artikels aus dem Hacker-Magazin ist . Sein Autor ist Alexey Sommer .

Source: https://habr.com/ru/post/de435914/


All Articles